Prozkoumání enginu Mediastreamer2 VoIP. Část 5

Materiál článku je převzat z mého zenový kanál.

Tónový detektor

V minulosti článek Vytvořili jsme měřič úrovně signálu. V tomto se naučíme, jak detekovat tónový signál.

Prozkoumání enginu Mediastreamer2 VoIP. Část 5

Za starých časů, kdy ne každá rodina měla televizi a polovina z nich přepínala kanály pomocí kleští, se v recenzích zahraničního odborného tisku objevily zajímavé zprávy, že jeden výrobce televizorů vybavil svá zařízení bezdrátovým dálkovým ovládáním. Z detailů bylo znát, že dálkové ovládání fungovalo bez baterií díky použití neobvyklého přístupu - dálkové ovládání bylo mechanické a bylo hybridem hudebního nástroje - metalofonu a revolveru. Revolverový buben obsahoval kovové válce různých délek, a když úderník zasáhl jeden z nich, válec začal zvonit vlastní frekvencí. Asi na ultrazvuku. Elektronika v televizoru tento signál slyšela a po určení jeho frekvence provedla příslušnou akci - přepněte kanál, změňte hlasitost, vypněte televizor.

Dnes se pokusíme rekonstruovat tento systém přenosu příkazů s využitím našich znalostí mediálního streameru.

Pro simulaci dálkového ovládání použijeme text našeho příkladu tónového generátoru. Přidáme k němu ovládání frekvence generátoru z stisku kláves a přijímač s dekodérem, který bude vydávat přijaté příkazy do konzole. Po změně by měl generátor produkovat tóny o 6 frekvencích, kterými budeme kódovat příkazy pro zvýšení/snížení hlasitosti, změnu kanálu, zapnutí/vypnutí TV. Pro konfiguraci detektoru se používá následující 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;

Detektor může mít 10 těchto struktur, takže jeden detektor může být nakonfigurován pro detekci deseti dvoutónových signálů. Použijeme ale pouze šest jednotónových signálů. Pro přenos nastavení do detektoru se používá metoda MS_TONE_DETECTOR_ADD_SCAN.

Aby nám detektor oznámil, že na jeho vstup dorazil signál s požadovanými frekvenčními složkami, musíme mu zajistit funkci zpětného volání, kterou v tomto případě spustí. To se provádí pomocí funkce ms_filter_set_notify_callback(). Jako argumenty obdrží ukazatel na filtr, ukazatel na funkci zpětného volání a ukazatel na data, která bychom chtěli předat funkci zpětného volání (uživatelská data).

Když je detektor spuštěn, funkce zpětného volání obdrží uživatelská data, ukazatel na filtr detektoru, identifikátor události a strukturu popisující událost:


/** * 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;

Blokové schéma zpracování signálu je na titulním obrázku.

No a nyní samotný programový kód s komentáři.

/* Файл 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);
    }
}

Program zkompilujeme a spustíme. Pokud vše funguje správně, měli bychom po spuštění získat něco jako toto chování 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

Stiskněte libovolnou klávesu od „1“ do „6“ a potvrďte klávesou „Enter“, měli byste získat něco jako tento výpis:


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

Vidíme, že povelové tóny jsou úspěšně odeslány a detektor je detekuje.

V příštím článku se zaměříme na přenos zvukového signálu po síti Ethernet pomocí protokolu RTP a ihned jej aplikujeme v našem dálkovém ovladači.

Zdroj: www.habr.com

Přidat komentář