Explorarea motorului VoIP Mediastreamer2. Partea 5

Materialul articolului este preluat de pe mine canal zen.

Detector de ton

În trecut articol Am creat un contor de nivel de semnal. În aceasta vom învăța cum să detectăm un semnal de ton.

Explorarea motorului VoIP Mediastreamer2. Partea 5

Pe vremuri, când nu toate familiile aveau un televizor și jumătate dintre ei schimbau canalele folosind un clește, în recenziile presei tehnice străine apăreau știri interesante că un producător de televizoare și-a echipat dispozitivele cu o telecomandă fără fir. Din detalii se știa că telecomanda funcționa fără baterii datorită utilizării unei abordări neobișnuite - telecomanda era mecanică și era un hibrid al unui instrument muzical - un metalofon și un revolver. Tamburul revolverului conținea cilindri metalici de diferite lungimi, iar când percutorul a lovit unul dintre ei, cilindrul a început să sune cu propria frecvență. Probabil la ecografie. Electronica din televizor a auzit acest semnal și, după ce i-a determinat frecvența, a efectuat acțiunea corespunzătoare - comutați canalul, schimbați volumul, opriți televizorul.

Astăzi vom încerca să reconstruim acest sistem de transmisie a comenzilor, folosind cunoștințele noastre despre media streamer.

Pentru a simula o telecomandă, vom folosi textul exemplului nostru de generator de tonuri. Îi vom adăuga controlul frecvenței generatorului de la apăsarea tastelor și un receptor cu un decodor care va scoate comenzile primite către consolă. După schimbare, generatorul ar trebui să producă tonuri de 6 frecvențe, cu care vom codifica comenzi de creștere/scădere a volumului, schimbarea canalului, pornirea/oprirea televizorului. Pentru configurarea detectorului se folosește următoarea structură:

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;

Un detector poate primi 10 dintre aceste structuri, astfel încât un detector poate fi configurat să detecteze zece semnale cu două tonuri. Dar vom folosi doar șase semnale cu un singur ton. Pentru a transfera setările la detector, se utilizează metoda MS_TONE_DETECTOR_ADD_SCAN.

Pentru ca detectorul să ne anunțe că la intrare a ajuns un semnal cu componentele de frecvență dorite, trebuie să îi oferim o funcție de apel invers pe care o va lansa în acest caz. Acest lucru se face folosind funcția ms_filter_set_notify_callback(). Ca argumente, primește un pointer către filtru, un pointer către funcția de apel invers și un pointer către datele pe care am dori să le transmitem funcției de apel invers (date utilizator).

Când detectorul este declanșat, funcția de apel invers va primi date despre utilizator, un pointer către filtrul detectorului, un identificator de eveniment și o structură care descrie evenimentul:


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

Diagrama bloc a procesării semnalului este prezentată în imaginea de titlu.

Ei bine, acum codul programului în sine cu comentarii.

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

Compilăm și rulăm programul. Dacă totul funcționează corect, atunci după lansare ar trebui să obținem ceva de genul acestui comportament al programului:

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

Apăsați orice tastă de la „1” la „6”, confirmând cu tasta „Enter”, ar trebui să obțineți ceva de genul acesta:


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

Vedem că tonurile de comandă sunt trimise cu succes și detectorul le detectează.

În articolul următor ne vom referi la transmiterea unui semnal audio printr-o rețea Ethernet folosind protocolul RTP și îl vom aplica imediat în telecomandă.

Sursa: www.habr.com

Adauga un comentariu