„Mediastreamer2 VoIP“ variklio tyrinėjimas. 5 dalis

Straipsnio medžiaga paimta iš mano zen kanalas.

Tonų detektorius

Paskutiniame straipsnis Sukūrėme signalo lygio matuoklį. Šioje pamokoje sužinosime, kaip aptikti tono signalą.

„Mediastreamer2 VoIP“ variklio tyrinėjimas. 5 dalis

Seniau, kai ne kiekviena šeima turėjo televizorių, o pusė jų perjungdavo kanalus replėmis, užsienio techninės spaudos apžvalgose pasirodydavo intriguojančių naujienų, kad vienas televizorių gamintojas savo įrenginius aprūpino belaidžiu pulteliu. Iš detalių buvo žinoma, kad nuotolinio valdymo pultas veikė be baterijų dėka neįprasto požiūrio – pultelis buvo mechaninis ir buvo muzikos instrumento – metalofono ir revolverio – hibridas. Revolverio būgne buvo metaliniai skirtingo ilgio cilindrai, o kai į vieną iš jų atsitrenkė šaudymo kaištis, cilindras pradėjo skambėti savo dažniu. Tikriausiai ultragarsu. Televizoriuje esanti elektronika išgirdo šį signalą ir, nustačiusi jo dažnį, atliko atitinkamą veiksmą – perjungė kanalą, pakeitė garsumą, išjungė televizorių.

Šiandien mes bandysime rekonstruoti šią komandų perdavimo sistemą, naudodami žinias apie žiniasklaidos srautą.

Norėdami imituoti nuotolinio valdymo pultą, naudosime savo tonų generatoriaus pavyzdžio tekstą. Prie jo pridėsime generatoriaus dažnio valdymą iš klavišų paspaudimų ir imtuvą su dekoderiu, kuris išves gautas komandas į konsolę. Po pakeitimo generatorius turėtų pagaminti 6 dažnių tonus, su kuriais užkoduosime komandas didinti/sumažinti garsumą, keisti kanalą, įjungti/išjungti televizorių. Norėdami sukonfigūruoti detektorių, naudojama ši struktū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;

Detektorius gali turėti 10 šių struktūrų, todėl vieną detektorių galima sukonfigūruoti aptikti dešimt dviejų tonų signalų. Bet mes naudosime tik šešis vieno tono signalus. Norint perkelti nustatymus į detektorių, naudojamas MS_TONE_DETECTOR_ADD_SCAN metodas.

Kad detektorius mums praneštų, kad į jo įvestį atkeliavo signalas su norimais dažnio komponentais, turime suteikti jam atgalinio skambučio funkciją, kurią jis tokiu atveju paleis. Tai atliekama naudojant funkciją ms_filter_set_notify_callback(). Kaip argumentus jis gauna rodyklę į filtrą, rodyklę į atgalinio skambinimo funkciją ir rodyklę į duomenis, kuriuos norėtume perduoti atgalinio skambinimo funkcijai (vartotojo duomenys).

Kai detektorius suaktyvinamas, atgalinio skambučio funkcija gaus vartotojo duomenis, žymeklį į detektoriaus filtrą, įvykio identifikatorių ir įvykį apibūdinančią struktūrą:


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

Signalo apdorojimo blokinė schema parodyta pavadinimo paveikslėlyje.

Na, o dabar pats programos kodas su komentarais.

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

Sukompiliuojame ir paleidžiame programą. Jei viskas veikia tinkamai, po paleidimo turėtume gauti kažką panašaus į šią programos elgseną:

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

Paspauskite bet kokius klavišus nuo „1“ iki „6“, patvirtindami klavišu „Enter“, turėtumėte gauti kažką panašaus į šį sąrašą:


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

Matome, kad komandų tonai sėkmingai išsiųsti ir detektorius juos aptinka.

Kitame straipsnyje mes pereisime prie garso signalo perdavimo Ethernet tinklu naudojant RTP protokolą ir nedelsdami pritaikysime jį savo nuotolinio valdymo pulte.

Šaltinis: www.habr.com

Добавить комментарий