Вивчаємо VoIP-движок Mediastreamer2. Частина 5

Матеріал статті взято з мого дзен-каналу.

Виявник тонального сигналу

У минулій статті ми створили вимірювач рівня сигналу. У цьому ми навчимося виявляти тональний сигнал.

Вивчаємо VoIP-движок Mediastreamer2. Частина 5

У старі часи, коли не в кожній родині був телевізор, і у половини з них канали перемикалися за допомогою пасатижів, в оглядах іноземної технічної преси з'явилася новина, що один і виробників телевізорів забезпечив свої апарати пультом дистанційного бездротового управління. З подробиць було відомо, що пульт працює без батарейок завдяки використанню незвичайного підходу — пульт був механічний і являв собою гібрид музичного інструменту — металофону та револьвера. У барабані револьвера були металеві циліндри, різні по довжині, і коли бійок ударяв по одному з них, циліндр починав дзвеніти на власній частоті. Імовірно на ультразвуку. Електроніка в телевізорі чула цей сигнал і, визначивши його частоту, виконувала відповідну дію — переключити канал, змінити гучність, вимкнути телевізор.

Сьогодні ми спробуємо зробити реконструкцію цієї системи передачі команд, скориставшись нашими знаннями медіастрімера.

Для імітації пульта скористаємося текстом нашого прикладу тонального генератора. Ми додамо до нього керування частотою генератора від натискань з клавіатури та приймач з декодером, який виводитиме в консоль прийняті команди. Після зміни, генератор повинен видавати тональні сигнали 6 частот, якими ми кодуватимемо команди збільшення/зменшення гучності, зміни каналу, включення/вимкнення телевізора. Для налаштування детектора використовується структура:

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;

Детектор можна передати 10 таких структур, тим самим один детектор можна налаштувати на виявлення десяти двотональних сигналів. Але ми з вами будемо використовувати лише шість однотональних сигналів. Для передачі настройок детектору використовується метод MS_TONE_DETECTOR_ADD_SCAN.

Щоб детектор міг повідомити нас про те, що на його вхід надійшов сигнал з частотними складовими, ми повинні йому надати функцію зворотного виклику, яку він запустить з такого випадку. Це робиться за допомогою функції ms_filter_set_notify_callback(). В якості аргументів вона отримує покажчик на фільтр, покажчик на функцію зворотного виклику, покажчик на дані, які ми хотіли б передати функції зворотного виклику (дані користувача).

При спрацюванні детектора функція зворотного виклику отримає дані користувача, покажчик на фільтр детектора, ідентифікатор події та структуру описує подію:


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

Структурна схема обробки сигналу показана в великій картинці.

Ну а тепер сам код програми із коментарями.

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

Компілюємо та запускаємо програму. Якщо все працює правильно, то після запуску ми маємо отримати приблизно таку поведінку програми:

$ ./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

Натискаємо будь-які клавіші від "1" до "6", підтверджуючи клавішею "Enter", повинен виходити приблизно такий лістинг:


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

Ми бачимо, що тони команд успішно вирушають і детектор їх виявляє.

У наступній статті ми звернемося до передачі звукового сигналу через Ethernet за допомогою RTP-протоколу і тут же застосуємо його в нашому дистанційному пульті.

Джерело: habr.com

Додати коментар або відгук