Skúmanie enginu Mediastreamer2 VoIP. Časť 5

Materiál článku je prevzatý z môjho zenový kanál.

Tónový detektor

V poslednom článok Vytvorili sme merač úrovne signálu. V tomto sa naučíme, ako rozpoznať tónový signál.

Skúmanie enginu Mediastreamer2 VoIP. Časť 5

V dávnych dobách, keď nie každá rodina mala televízor a polovica z nich prepínala kanály pomocou klieští, sa v recenziách zahraničnej technickej tlače objavili zaujímavé správy, že jeden výrobca televízorov vybavil svoje zariadenia bezdrôtovým diaľkovým ovládaním. Z detailov bolo poznať, že diaľkové ovládanie fungovalo bez batérií vďaka použitiu neobvyklého prístupu - diaľkový ovládač bol mechanický a bol hybridom hudobného nástroja - metalofónu a revolvera. Revolverový bubon obsahoval kovové valce rôznych dĺžok a keď úderník zasiahol jeden z nich, valec začal zvoniť vlastnou frekvenciou. Vraj na ultrazvuku. Elektronika v televízore počula tento signál a po určení jeho frekvencie vykonala príslušnú akciu - prepnite kanál, zmeňte hlasitosť, vypnite televízor.

Dnes sa pokúsime zrekonštruovať tento systém prenosu príkazov s využitím našich poznatkov o streamovaní médií.

Na simuláciu diaľkového ovládača použijeme text nášho príkladu tónového generátora. Pridáme k nemu ovládanie frekvencie generátora stláčaním kláves a prijímač s dekodérom, ktorý bude vydávať prijaté príkazy do konzoly. Po zmene by mal generátor produkovať tóny 6 frekvencií, pomocou ktorých budeme kódovať príkazy na zvýšenie/zníženie hlasitosti, zmenu kanálu, zapnutie/vypnutie TV. Na konfiguráciu detektora sa používa nasledujúca štruktúra:

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 mať 10 týchto štruktúr, takže jeden detektor môže byť nakonfigurovaný na detekciu desiatich dvojtónových signálov. Použijeme ale iba šesť jednotónových signálov. Na prenos nastavení do detektora sa používa metóda 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 zložkami, musíme mu poskytnúť funkciu spätného volania, ktorú v tomto prípade spustí. To sa vykonáva pomocou funkcie ms_filter_set_notify_callback(). Ako argumenty dostane ukazovateľ na filter, ukazovateľ na funkciu spätného volania a ukazovateľ na údaje, ktoré by sme chceli odovzdať funkcii spätného volania (údaje používateľa).

Keď je detektor spustený, funkcia spätného volania dostane užívateľské dáta, ukazovateľ na filter detektora, identifikátor udalosti a štruktúru popisujúcu udalosť:


/** * 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 spracovania signálu je znázornená na titulnom obrázku.

No a teraz samotný kód programu s komentármi.

/* Файл 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 skompilujeme a spustíme. Ak všetko funguje správne, po spustení by sme mali dostať niečo ako toto správanie 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

Stlačte ľubovoľné klávesy od „1“ do „6“ a potvrďte tlačidlom „Enter“, mali by ste dostať niečo ako tento zoznam:


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

Vidíme, že príkazové tóny sú úspešne odoslané a detektor ich deteguje.

V ďalšom článku sa budeme venovať prenosu zvukového signálu cez ethernetovú sieť pomocou protokolu RTP a ihneď ho aplikujeme v našom diaľkovom ovládači.

Zdroj: hab.com

Pridať komentár