Materiał artykułu pochodzi z mojego .

Tworzymy generator tonalny
W poprzednim Zainstalowaliśmy bibliotekę do strumieniowania mediów, narzędzia deweloperskie i sprawdziliśmy ich działanie, tworząc aplikację próbkę.
Dziś stworzymy aplikację, która będzie mogła generować tonalny sygnał na karcie dźwiękowej. Aby rozwiązać to zadanie, musimy połączyć filtry w pokazanej poniżej schemacie generatora dźwięku:
![]()
Czytamy schemat z lewej do prawej, w tym kierunku płynie nasz strumień danych. Strzałki również to sugerują. Prostokąty oznaczają filtry, które przetwarzają bloki danych i wystawiają wynik na wyjście. Wewnątrz prostokąta podana jest jego rola, a poniżej wielkimi literami typ filtru. Strzałki łączące prostokąty to kolejki danych, po których bloki danych są dostarczane z filtra do filtra. W ogólnym przypadku filtr może mieć wiele wejść i wyjść.
Wszystko zaczyna się od źródła taktów, które ustala tempo, w jakim przetwarzane są dane w filtrach. W rytmie jego taktów każdy filtr przetwarza wszystkie bloki danych, które znajdują się na jego wejściu, i wystawia bloki z wynikiem na wyjście — do kolejki. Najpierw obliczenia wykonuje filtr najbliższy źródłu taktów, następnie filtry podłączone do jego wyjść (może ich być wiele) i tak dalej. Kiedy ostatni filtr w łańcuchu zakończy obliczenia, wykonanie zatrzymuje się do momentu, gdy nadejdzie nowy takt. Takty, domyślnie, następują w odstępach 10 milisekund.
Wróćmy do naszego schematu. Takty trafiają na wejście źródła ciszy, to jest filtr, który generuje na swoim wyjściu blok danych zawierający zera na każdy takt. Jeśli spojrzeć na ten blok jako na blok pomiarów dźwięku, to nie jest nic innego jak cisza. Na pierwszy rzut oka wydaje się to dziwne, generować bloki danych z ciszy — przecież nie można jej usłyszeć, ale te bloki są niezbędne do działania generatora sygnału dźwiękowego. Generator wykorzystuje te bloki jak czystą kartkę papieru, zapisując na nich pomiary dźwiękowe. W swoim normalnym stanie generator jest wyłączony i po prostu przesyła bloki wejściowe na wyjście. W ten sposób bloki ciszy przechodzą bez zmian przez cały schemat z lewej do prawej, trafiając do karty dźwiękowej. Karta dźwiękowa cicho odbiera bloki z kolejki podłączonej do jej wejścia.
Jednak wszystko zmienia się, gdy generator otrzymuje polecenie odtwarzania dźwięku, zaczyna generować pomiary dźwiękowe i zastępuje nimi pomiary w blokach wejściowych, wystawiając zmodyfikowane bloki na wyjście. Karta dźwiękowa zaczyna odtwarzać dźwięk. Poniżej znajduje się program, który realizuje powyżej opisaną pracę:
/* Файл mstest2.c */
#include <mediastreamer2/msfilter.h>
#include <mediastreamer2/msticker.h>
#include <mediastreamer2/dtmfgen.h>
#include <mediastreamer2/mssndcard.h>
int main()
{
ms_init();
/* Создаем экземпляры фильтров. */
MSFilter *voidsource = ms_filter_new(MS_VOID_SOURCE_ID);
MSFilter *dtmfgen = ms_filter_new(MS_DTMF_GEN_ID);
MSSndCard *card_playback = ms_snd_card_manager_get_default_card(ms_snd_card_manager_get());
MSFilter *snd_card_write = ms_snd_card_create_writer(card_playback);
/* Создаем тикер. */
MSTicker *ticker = ms_ticker_new();
/* Соединяем фильтры в цепочку. */
ms_filter_link(voidsource, 0, dtmfgen, 0);
ms_filter_link(dtmfgen, 0, snd_card_write, 0);
/* Подключаем источник тактов. */
ms_ticker_attach(ticker, voidsource);
/* Включаем звуковой генератор. */
char key='1';
ms_filter_call_method(dtmfgen, MS_DTMF_GEN_PLAY, (void*)&key);
/* Даем, время, чтобы все блоки данных были получены звуковой картой.*/
ms_sleep(2);
}Po zainicjowaniu mediastreamera następuje utworzenie trzech filtrów: voidsource, dtmfgen, snd_card_write. Tworzony jest źródło sygnału taktowego.
Następnie należy połączyć filtry zgodnie z naszym schematem, przy czym źródło taktu powinno być podłączone jako ostatnie, ponieważ od tego momentu schemat zacznie działać. Jeśli podłączysz źródło taktu do nieukończonego schematu, może zdarzyć się, że mediastreamer zakończy pracę awaryjnie, jeśli wykryje w łańcuchu chociażby jeden filtr, którego wszystkie wejścia lub wszystkie wyjścia "wiszą w powietrzu" (nie są podłączone).
Połączenie filtrów wykonuje się za pomocą funkcji
ms_filter_link(src, src_out, dst, dst_in), gdzie pierwszy argument to wskaźnik na filtr źródłowy, drugi argument to numer wyjścia źródła (zwróć uwagę, że wejścia i wyjścia są numerowane od zera). Trzeci argument to wskaźnik na filtr odbierający, czwarty — numer wejścia odbiornika.
Wszystkie filtry są połączone, a ostatnim podłączanym źródłem jest generator taktów (dalej będziemy go nazywać tikkerem). Po tym nasza schemat dźwiękowa zaczyna działać, ale w głośnikach komputera na razie nic nie słychać — generator dźwięku jest wyłączony i po prostu przepuszcza przez siebie dane wejściowe z ciszą. Aby uruchomić generację sygnału tonalnego, konieczne jest wykonanie metody filtra generatora.
Będziemy generować sygnał dwutonowy (DTMF) odpowiadający naciśnięciu przycisku "1" na telefonie. W tym celu przy pomocy funkcji ms_filter_call_method() wywołujemy metodę MS_DTMF_GEN_PLAY, przekazując jej jako argument wskaźnik na kod, któremu powinien odpowiadać odtwarzany sygnał.
Pozostaje skompilować program:
$ gcc mstest2.c -o mstest2 `pkg-config mediastreamer --libs --cflags`I uruchomić:
$ ./mstest2Po uruchomieniu programu usłyszysz w głośnikach komputera krótki sygnał dźwiękowy składający się z dwóch tonów.
Zbudowaliśmy i uruchomiliśmy naszą pierwszą schemat dźwiękową. Zobaczyliśmy, jak tworzyć instancje filtrów, jak je łączyć i jak wywoływać ich metody. Ciesząc się z pierwszego sukcesu, musimy jednak zwrócić uwagę na to, że nasz program nie zwalnia przydzielonej pamięci przed zakończeniem. W następnym nauczymy się sprzątać po sobie.
Źródło: habr.com
