Poznawanie silnika Mediastreamer2 VoIP. Część 5

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

Detektor tonu

W przeszłości Artykuł Stworzyliśmy miernik poziomu sygnału. W tym artykule dowiemy się, jak wykryć sygnał tonowy.

Poznawanie silnika Mediastreamer2 VoIP. Część 5

W dawnych czasach, kiedy nie każda rodzina posiadała telewizor, a połowa z nich przełączała kanały za pomocą szczypiec, w recenzjach zagranicznej prasy technicznej pojawiała się intrygująca wiadomość, że jeden z producentów telewizorów wyposażał swoje urządzenia w bezprzewodowego pilota. Ze szczegółów wiadomo było, że pilot działał bez baterii dzięki zastosowaniu nietypowego podejścia – pilot był mechaniczny i stanowił hybrydę instrumentu muzycznego – metalofonu i rewolweru. Bęben rewolwerowy zawierał metalowe cylindry o różnej długości, a gdy iglica uderzyła w jeden z nich, cylinder zaczął dzwonić z własną częstotliwością. Pewnie na USG. Elektronika w telewizorze usłyszała ten sygnał i po ustaleniu jego częstotliwości wykonała odpowiednią akcję - przełącz kanał, zmień głośność, wyłącz telewizor.

Dzisiaj spróbujemy zrekonstruować ten system przekazywania poleceń, wykorzystując naszą wiedzę na temat streamera multimedialnego.

Aby zasymulować pilota, użyjemy tekstu naszego przykładowego generatora tonów. Dodamy do tego sterowanie częstotliwością generatora za pomocą naciśnięć klawiszy oraz odbiornik z dekoderem, który będzie wysyłał odebrane polecenia do konsoli. Po zmianie generator powinien generować tony o 6 częstotliwościach, za pomocą których zakodujemy komendy zwiększające/zmniejszające głośność, zmieniające kanał, włączające/wyłączające telewizor. Do skonfigurowania czujki wykorzystywana jest następująca struktura:

struct _MSToneDetectorDef{  
     char tone_name[8];     
     int frequency; /**<Expected frequency of the tone*/ 
     int min_duration; /**<Min duration of the tone in milliseconds */ 
     float min_amplitude; /**<Minimum amplitude of the tone, 1.0 corresponding to the normalized 0dbm level */
};

typedef struct _MSToneDetectorDef MSToneDetectorDef;

Do detektora można przypisać 10 takich struktur, zatem jeden detektor można skonfigurować do wykrywania dziesięciu sygnałów dwutonowych. Ale użyjemy tylko sześciu sygnałów jednotonowych. Do przesłania ustawień do czujki wykorzystywana jest metoda MS_TONE_DETECTOR_ADD_SCAN.

Aby detektor powiadomił nas, że na jego wejście dotarł sygnał o pożądanych składowych częstotliwości, musimy zapewnić mu funkcję wywołania zwrotnego, którą w tym przypadku uruchomi. Odbywa się to za pomocą funkcji ms_filter_set_notify_callback(). Jako argumenty otrzymuje wskaźnik do filtra, wskaźnik do funkcji wywołania zwrotnego oraz wskaźnik do danych, które chcielibyśmy przekazać do funkcji wywołania zwrotnego (dane użytkownika).

Po uruchomieniu detektora funkcja wywołania zwrotnego otrzyma dane użytkownika, wskaźnik do filtra detektora, identyfikator zdarzenia oraz strukturę opisującą zdarzenie:


/** * Structure carried as argument of the MS_TONE_DETECTOR_EVENT**/
struct _MSToneDetectorEvent{ 
      char tone_name[8];       /* Имя тона которое мы ему назначили при настройке детектора. */
      uint64_t tone_start_time;   /* Время в миллисекундах, когда тон был обнаружен. */
};

typedef struct _MSToneDetectorEvent MSToneDetectorEvent;

Schemat blokowy przetwarzania sygnału pokazano na obrazku tytułowym.

Cóż, teraz sam kod programu z komentarzami.

/* Файл mstest4.c Имитатор пульта управления и приемника. */
#include <mediastreamer2/msfilter.h>
#include <mediastreamer2/msticker.h>
#include <mediastreamer2/dtmfgen.h>
#include <mediastreamer2/mssndcard.h>
#include <mediastreamer2/msvolume.h>
#include <mediastreamer2/mstonedetector.h>

/* Подключаем заголовочный файл с функциями управления событиями
 * медиастримера. */
#include <mediastreamer2/mseventqueue.h>

/* Функция обратного вызова, она будет вызвана фильтром, как только он
 * обнаружит совпадение характеристик входного сигнала с заданными. */
static void tone_detected_cb(void *data, MSFilter *f, unsigned int event_id,
        MSToneDetectorEvent *ev)
{
    printf("                      Принята команда: %sn", ev->tone_name);
}

int main()
{
    ms_init();

    /* Создаем экземпляры фильтров. */
    MSFilter  *voidsource = ms_filter_new(MS_VOID_SOURCE_ID);
    MSFilter  *dtmfgen = ms_filter_new(MS_DTMF_GEN_ID);
    MSFilter  *volume = ms_filter_new(MS_VOLUME_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);
    MSFilter  *detector = ms_filter_new(MS_TONE_DETECTOR_ID);

    /* Очищаем массив находящийся внутри детектора тонов, он описывает
     * особые приметы разыскиваемых сигналов.*/
    ms_filter_call_method(detector, MS_TONE_DETECTOR_CLEAR_SCANS, 0);

    /* Создаем источник тактов - тикер. */
    MSTicker *ticker=ms_ticker_new();

    /* Соединяем фильтры в цепочку. */
    ms_filter_link(voidsource, 0, dtmfgen, 0);
    ms_filter_link(dtmfgen, 0, volume, 0);
    ms_filter_link(volume, 0, detector, 0);
    ms_filter_link(detector, 0, snd_card_write, 0);

    /* Подключаем к фильтру функцию обратного вызова. */
    ms_filter_set_notify_callback(detector,
            (MSFilterNotifyFunc)tone_detected_cb, NULL);

    /* Подключаем источник тактов. */
    ms_ticker_attach(ticker,voidsource);

    /* Создаем массив, каждый элемент которого описывает характеристику
     * одного из тонов, который требуется обнаруживать: Текстовое имя
     * данного элемента, частота в герцах, длительность в миллисекундах,
     * минимальный уровень относительно 0,775В. */  
    MSToneDetectorDef  scan[6]=
    {
        {"V+",  440, 100, 0.1}, /* Команда "Увеличить громкость". */
        {"V-",  540, 100, 0.1}, /* Команда "Уменьшить громкость". */
        {"C+",  640, 100, 0.1}, /* Команда "Увеличить номер канала". */
        {"C-",  740, 100, 0.1}, /* Команда "Уменьшить номер канала". */
        {"ON",  840, 100, 0.1}, /* Команда "Включить телевизор". */
        {"OFF", 940, 100, 0.1}  /* Команда "Выключить телевизор". */
    };

    /* Передаем в детектор тонов приметы сигналов. */
    int i;
    for (i = 0; i < 6; i++)
    {
        ms_filter_call_method(detector, MS_TONE_DETECTOR_ADD_SCAN,
                &scan[i]);
    }

    /* Настраиваем структуру, управляющую выходным сигналом генератора.*/
    MSDtmfGenCustomTone dtmf_cfg;
    dtmf_cfg.tone_name[0] = 0;
    dtmf_cfg.duration = 1000;
    dtmf_cfg.frequencies[0] = 440;
    /* Будем генерировать один тон, частоту второго тона установим в 0.*/
    dtmf_cfg.frequencies[1] = 0;
    dtmf_cfg.amplitude = 1.0;
    dtmf_cfg.interval = 0.;
    dtmf_cfg.repeat_count = 0.;

    /* Организуем цикл сканирования нажатых клавиш. Ввод нуля завершает
     * цикл и работу программы. */
    char key='9';
    printf("Нажмите клавишу команды, затем ввод.n"
        "Для завершения программы введите 0.n");
    while(key != '0')
    {
        key = getchar();
        if ((key >= 49) && (key <= 54))
        {
                printf("Отправлена команда: %cn", key);
            /* Устанавливаем частоту генератора в соответствии с
             * кодом нажатой клавиши.*/
            dtmf_cfg.frequencies[0] = 440 + 100*(key-49);

            /* Включаем звуковой генератор c обновленной частотой. */
            ms_filter_call_method(dtmfgen, MS_DTMF_GEN_PLAY_CUSTOM,
                    (void*)&dtmf_cfg);
        }
        ms_usleep(20000);
    }
}

Kompilujemy i uruchamiamy program. Jeśli wszystko działa poprawnie, to po uruchomieniu powinniśmy uzyskać mniej więcej takie zachowanie programu:

$ ./mstest4
ALSA lib conf.c:4738:(snd_config_expand) Unknown parameters 0
ALSA lib control.c:954:(snd_ctl_open_noupdate) Invalid CTL default:0
ortp-warning-Could not attach mixer to card: Invalid argument
ALSA lib conf.c:4738:(snd_config_expand) Unknown parameters 0
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) Unknown PCM default:0
ALSA lib conf.c:4738:(snd_config_expand) Unknown parameters 0
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) Unknown PCM default:0
ortp-warning-Strange, sound card Intel 82801AA-ICH does not seems to be capable of anything, retrying with plughw...
Нажмите клавишу команды, затем ввод.
Для завершения программы введите 0.
ortp-warning-alsa_set_params: periodsize:256 Using 256
ortp-warning-alsa_set_params: period:8 Using 8

Naciśnij dowolny klawisz od „1” do „6”, potwierdzając klawiszem „Enter”, powinieneś otrzymać coś takiego:


2
Отправлена команда: 2
                      Принята команда: V-
1
Отправлена команда: 1
                      Принята команда: V+
3
Отправлена команда: 3
                      Принята команда: C+
4
Отправлена команда: 4
                      Принята команда: C-
0
$

Widzimy, że sygnały poleceń zostały pomyślnie wysłane i detektor je wykrywa.

W kolejnym artykule zajmiemy się przesyłaniem sygnału audio przez sieć Ethernet za pomocą protokołu RTP i od razu zastosujemy go w naszym pilocie.

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

Dodaj komentarz