niestandardowe-strumieniowanie

Niestandardowe strumieniowanie audio

Trochę teorii


Na początku musimy sobie odpowiedzieć na pytanie czym jest strumieniowanie audio. Strumieniowanie audio (audio stream) jest podobne do muzyki (klasa sf::Music), posiada podobne funkcje, które zachowują się w ten sam sposób. Podstawową różnicą jest to że nie odtwarzamy tutaj pliku audio, lecz odtwarzamy audio bezpośrednio ze źródła, które odtwarzamy. Inaczej rzecz ujmując, możesz wtedy odtworzyć np: audio otrzymane z sieci (komunikator głowowy w twojej grze), muzykę wygenerowaną przez twój program, czy też format audio, którego SFML jeszcze nie wspiera.

Klasa sf::Music jest wyspecjalizowana w strumieniowaniu audio poprzez pobieranie małych fragmentów (samples) z pliku.

Z racji że mówimy o strumieniowaniu audio będziemy mówili o audio, które nie może być załadowane w całości do pamięci, lecz będziemy je wczytywali fragmentami i dopiero odtwarzali. Jeżeli twoje dźwięki mogą być załadowane  w całości do pamięci to strumieniowanie audio nie pomoże ci, skorzystaj wtedy z sf::SoundBuffer oraz odtwórz swój dźwięk za pomocą sf::Sound.

sf::SoundStream


Aby utworzyć swoją klasę do strumieniowania audio musisz dziedziczyć po klasie abstrakcyjnej sf::SoundStream. Posiada ona 2 wirtualne klasy, które musisz napisać: onGetData oraz onSeek.

Funkcja onGetData jest wywoływana za każdym razem gdy zostanie odtworzony obecnie pobrany fragment muzyki i będzie potrzebny kolejny. Przez tą funkcję musisz dostarczyć kolejnego fragmentu używając argumentu data.

Musisz zwrócić true jeżeli wszystko udało się wczytać, w przeciwnym razie (lub gdy nie ma więcej danych do odtworzenia) zwróć false, wtedy odtwarzanie audio zostanie zatrzymane.

SFML tworzy kopie sample’i w momencie gdy zostanie zwrócona prawda w onGetData, więc nie musisz trzymać tych danych, możesz spokojnie je usunąć.

Metoda onSeek jest wywoływana w momencie wywołania funkcji setPlayingOffset. Służy ona do zmiany wskaźnika odtwarzanych danych na odpowiedni. Jako argument przyjmuje czas utworu, który ma być odtworzony (czas jest podawany licząc od początku utworu nie od obecnej pozycji). Ta funkcja czasami jest niemożliwa do zaimplementowania, wtedy możesz zostawić ją pustą i powiedzieć użytkownikowi, że nie da się przewinąć utworu.

Twoja klasa jest już prawie gotowa do pracy. Klasa SoundStream potrzebuje jeszcze wiedzieć o numerze kanału (channel number) oraz o częstotliwości próbkowania (sample rate). Aby przekazać te parametry musisz użyć funkcji protected initialize najczęściej zaraz po wczytaniu/zainicjalizowaniu strumieniowania.

 

Kwestia wątkowania (thread)


Strumieniowanie audio zawsze odbywa się w oddzielnym wątku, jednak powinieneś wiedzieć co się tam dzieje i gdzie.

onSeek jest wywoływane bezpośrednio z funkcji setPlayingOffset więc jest zawsze wykonywana w wątku z którego została wezwana. Jednakże onGetData jest wzywane dopóki jest odtwarzane audio w oddzielny wątku stworzonym przez SFML. Więc jeżeli twój strumień audio używa danych może zostać przydzielony jednocześnie do obu wątków: tego w którym został uruchomiony oraz w wątku odtwarzającym, który musisz chronić za pomocą np. mutex’ów tak aby uniknąć równoczesnego dostępu do tych samych danych. W przeciwnym razie będzie mogło dojść do nieprzewidywalnych zachowań (zniszczenie odtwarzanych danych, crash, itd).

Jeżeli nie czujesz się pewnie w kategorii wątków zapraszam do poradnika o wątkach.

 

Użycie własnego strumieniowania audio


Skoro już posiadamy zdefiniowaną klasę, przyjrzyjmy się jak należy ją poprawnie użyć. Jest to bardzo proste, wręcz podobne do tego co pokazywałem w poradniku o sf::Music. Możesz kontrolować audio za pomocą: play, pause, stop i setPlayeingOffset. Oczywiście możesz je także odtworzyć z takimi właściwościami jak np. głośność. Możesz się także odnieść do dokumentacji API lub do innych poradników o audio.

 

Przykład


Poniżej mamy prosty przykład strumieniowania audio, gdzie jest odtwarzanie audio z bufora audio. Całość w sama sobie jest totalnie bezużyteczna, ale chodzi tutaj o pokazanie w jaki sposób dane (data) są strumieniowanie przez klasę, nie ważne z jakiego źródła.

Oryginalny artykuł