Studujeme VoIP engine Mediastreamer2. Část 5

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

Detektor tonálního signálu

V minulosti článku Vytvořili jsme měřič úrovně signálu. V tomto textu se naučíme detekovat tonální signál.

Studujeme VoIP engine Mediastreamer2. Část 5

V dobách, kdy neměl každý dům televizi a polovina z nich přepínala kanály pomocí kleští, se v zahraniční technické tiskové zprávě objevila zajímavá novinka, že jeden z výrobců televizorů vybavil své přístroje bezdrátovým dálkovým ovládáním. Z podrobností bylo známo, že ovladač funguje bez baterií díky neobvyklému přístupu - ovladač byl mechanický a představoval hybrid hudebního nástroje - metalofonu a revolveru. V bubnu revolveru byly kovové válce, různé délky, a když úderník udeřil na jeden z nich, válec začal zvonit na své vlastní frekvenci. Předpokládalo se, že na ultrazvuku. Elektronika v televizoru tento signál zachytila a po určení jeho frekvence provedla odpovídající akci - přepnula kanál, změnila hlasitost, vypnula televizor.

Dnes se pokusíme znovu vytvořit tento systém přenosu příkazů za využití našich znalostí o mediálních streamech.

Pro simulaci ovladače využijeme text našeho příkladu tonálního generátoru. Přidáme do něj řízení frekvence generátoru od stisků klávesnice a přijímač s dekodérem, který bude vypisovat přijaté příkazy do konzole. Po změně by generátor měl vydávat tonální signály šesti frekvencí, kterými budeme kódovat příkazy pro zvýšení/snížení hlasitosti, změnu kanálu, zapnutí/vypnutí televizoru. Pro nastavení detektoru se používá struktura:

struct _MSToneDetectorDef{  
     char tone_name[8];     
     int frequency; 
     /**< Očekávaná frekvence tónu */ 
     int min_duration; 
     /**< Minimální doba tónu v milisekundách */ 
     float min_amplitude; 
     /**< Minimální amplituda tónu, 1.0 odpovídá normalizované úrovni 0dbm */
};

typedef struct _MSToneDetectorDef MSToneDetectorDef;

Detektoru lze předat 10 takových struktur, čímž lze jeden detektor nastavit na detekci deseti dvoutonálních signálů. Ale my použijeme pouze šest monotónních signálů. Pro předání nastavení detektoru se používá metoda MS_TONE_DETECTOR_ADD_SCAN.

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

Když detektor spustí akci, zpětná funkce obdrží uživatelská data, ukazatel na detekční filtr, 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;

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

A teď kód programu 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);
    }
}

Kompilujeme a spouštíme program. Pokud vše funguje správně, po spuštění bychom měli vidět přibližně takovéto chování programu:

$ ./mstest4
ALSA lib conf.c:4738:(snd_config_expand) Neznámé parametry 0
ALSA lib control.c:954:(snd_ctl_open_noupdate) Neplatný CTL default:0
ortp-varování-Nepodařilo se připojit mixer k kartě: Neplatný argument
ALSA lib conf.c:4738:(snd_config_expand) Neznámé parametry 0
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) Neznámé PCM default:0
ALSA lib conf.c:4738:(snd_config_expand) Neznámé parametry 0
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) Neznámé PCM default:0
ortp-varování-Zvláštní, zvuková karta Intel 82801AA-ICH se zdá být schopná ničeho, zkouším to znovu s plughw...
Stiskněte klávesu příkazu, poté enter.
Pro ukončení programu zadejte 0.
ortp-varování-alsa_set_params: periodsize:256 Používá se 256
ortp-varování-alsa_set_params: period:8 Používá se 8

Stiskněte libovolné klávesy od "1" do "6", potvrďte klávesou "Enter", měl by se objevit přibližně takovýto výpis:


2
Odeslána příkaz: 2
                      Přijata příkaz: V-
1
Odeslána příkaz: 1
                      Přijata příkaz: V+
3
Odeslána příkaz: 3
                      Přijata příkaz: C+
4
Odeslána příkaz: 4
                      Přijata příkaz: C-
0
$

Vidíme, že tóny příkazů byly úspěšně odeslány a detektor je zaznamenal.

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

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster