Poznawanie silnika Mediastreamer2 VoIP. Część 2

Materiał artykułu pochodzi z mojego kanał zen.

Poznawanie silnika Mediastreamer2 VoIP. Część 2

Tworzenie generatora tonów

W poprzednim Artykuł Zainstalowaliśmy bibliotekę streamerów multimediów, narzędzia programistyczne i przetestowaliśmy ich funkcjonalność, budując aplikację próbną.

Dzisiaj stworzymy aplikację, która może wygenerować sygnał tonowy na karcie dźwiękowej. Aby rozwiązać ten problem, musimy podłączyć filtry do pokazanego poniżej obwodu generatora dźwięku:

Poznawanie silnika Mediastreamer2 VoIP. Część 2

Diagram czytamy od lewej do prawej, to jest kierunek, w którym porusza się nasz przepływ danych. Strzałki również na to wskazują. Prostokąty wskazują filtry przetwarzające bloki danych i generujące wynik. Wewnątrz prostokąta wskazana jest jego rola, a typ filtra jest oznaczony wielkimi literami tuż poniżej. Strzałki łączące prostokąty to kolejki danych, przez które bloki danych dostarczane są z filtra do filtra. Ogólnie rzecz biorąc, filtr może mieć wiele wejść i wyjść.

Wszystko zaczyna się od źródła zegara, które ustala tempo, w jakim dane są obliczane w filtrach. Zgodnie ze swoim cyklem zegara każdy filtr przetwarza wszystkie bloki danych znajdujące się na jego wejściu. I umieszcza bloki z wynikiem w kolejce. Najpierw obliczenia wykonuje filtr najbliższy źródła zegara, następnie filtry podłączone do jego wyjść (wyjść może być wiele) i tak dalej. Po zakończeniu przetwarzania ostatniego filtru w łańcuchu wykonywanie zostaje zatrzymane do czasu nadejścia nowego zegara. Uderzenia domyślnie następują w odstępie 10 milisekund.

Wróćmy do naszego diagramu. Cykle zegara docierają na wejście źródła ciszy; jest to filtr, który jest zajęty generowaniem bloku danych zawierających zera na wyjściu dla każdego cyklu zegara. Jeśli potraktujemy ten blok jako blok próbek dźwiękowych, to jest to nic innego jak cisza. Na pierwszy rzut oka dziwne wydaje się generowanie bloków danych z ciszą – w końcu nie słychać, ale bloki te są niezbędne do działania generatora sygnału dźwiękowego. Generator wykorzystuje te bloki jak czystą kartkę papieru, zapisując na nich próbki dźwiękowe. W normalnym stanie generator jest wyłączony i po prostu przekazuje bloki wejściowe na wyjście. W ten sposób bloki ciszy przechodzą w niezmienionej postaci przez cały obwód od lewej do prawej, kończąc na karcie dźwiękowej. Który po cichu pobiera bloki z kolejki podłączonej do jego wejścia.

Ale wszystko się zmienia, jeśli generator otrzyma polecenie odtwarzania dźwięku, zaczyna generować próbki dźwięku i zastępuje je próbkami w blokach wejściowych, a zmienione bloki umieszcza na wyjściu. Karta dźwiękowa rozpocznie odtwarzanie dźwięku. Poniżej znajduje się program realizujący opisany powyżej schemat pracy:

/* Файл 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 streamera multimediów tworzone są trzy filtry: voidsource, dtmfgen, snd_card_write. Utworzono źródło zegara.

Następnie należy podłączyć filtry zgodnie z naszym obwodem, a źródło zegara należy podłączyć jako ostatnie, ponieważ po tym obwód natychmiast zacznie działać. Jeśli podłączysz źródło zegara do niedokończonego obwodu, może się zdarzyć, że streamer multimediów ulegnie awarii, jeśli wykryje co najmniej jeden filtr w łańcuchu, w którym wszystkie wejścia lub wszystkie wyjścia „wiszą w powietrzu” (nie są podłączone).

Podłączenie filtrów odbywa się za pomocą funkcji

ms_filter_link(src, src_out, dst, dst_in)

gdzie pierwszy argument jest wskaźnikiem do filtru źródłowego, drugim argumentem jest numer wyjścia źródłowego (należy pamiętać, że numeracja wejść i wyjść rozpoczyna się od zera). Trzeci argument to wskaźnik do filtru odbiornika, czwarty to numer wejścia odbiornika.

Wszystkie filtry są podłączone, a źródło zegara jest podłączane jako ostatnie (w dalszej części będziemy nazywać to po prostu tickerem). Po czym nasz obwód dźwiękowy zaczyna działać, ale w głośnikach komputera jeszcze nic nie słychać - generator dźwięku jest wyłączony i po prostu przechodzi przez wejściowe bloki danych z ciszą. Aby rozpocząć generowanie dźwięku, należy uruchomić metodę filtra generatora.

Wygenerujemy sygnał dwutonowy (DTMF) odpowiadający naciśnięciu przycisku „1” w telefonie. W tym celu korzystamy z funkcji ms_filter_call_method() Wywołujemy metodę MS_DTMF_GEN_PLAY przekazując jej jako argument wskaźnik do kodu, któremu powinien odpowiadać sygnał odtwarzania.

Pozostaje tylko skompilować program:

$ gcc mstest2.c -o mstest2 `pkg-config mediastreamer --libs --cflags`

I biegnij:

$ ./mstest2

Po uruchomieniu programu w głośniku komputera usłyszysz krótki sygnał dźwiękowy składający się z dwóch tonów.

Zbudowaliśmy i uruchomiliśmy nasz pierwszy obwód dźwiękowy. Zobaczyliśmy, jak tworzyć instancje filtrów, jak je łączyć i jak wywoływać ich metody. Chociaż jesteśmy zadowoleni z naszego początkowego sukcesu, nadal musimy zwracać uwagę na fakt, że nasz program nie zwalnia przydzielonej pamięci przed wyjściem. W następnym Artykuł nauczymy się sprzątać po sobie.

Źródło: www.habr.com

Dodaj komentarz