Mediastreamer2 VoIP programmas izpēte. 5. daļa

Raksta materiāls ņemts no mana zen kanāls.

Toņu detektors

Pagātnē raksts Mēs esam izveidojuši signāla līmeņa mērītāju. Šajā mēs uzzināsim, kā noteikt toņa signālu.

Mediastreamer2 VoIP programmas izpēte. 5. daļa

Agrākos laikos, kad ne katrā ģimenē bija televizors un puse pārslēdza kanālus, izmantojot knaibles, ārzemju tehniskās preses apskatos parādījās intriģējoša ziņa, ka viens televizoru ražotājs savas ierīces aprīkojis ar bezvadu tālvadības pulti. No detaļām bija zināms, ka tālvadības pults darbojās bez baterijām, pateicoties neparastai pieejai - pults bija mehāniska un bija mūzikas instrumenta - metalofona un revolvera hibrīds. Revolvera trumulī atradās dažāda garuma metāla cilindri, un, šaušanas tapai trāpot vienam no tiem, cilindrs sāka zvanīt savā frekvencē. Iespējams, ultraskaņā. Televizora elektronika dzirdēja šo signālu un, noteikusi tā frekvenci, veica atbilstošu darbību - pārslēdza kanālu, mainīja skaļumu, izslēdza televizoru.

Šodien mēs mēģināsim rekonstruēt šo komandu pārraides sistēmu, izmantojot savas zināšanas par mediju straumētāju.

Lai simulētu tālvadības pulti, mēs izmantosim mūsu toņu ģeneratora piemēra tekstu. Mēs tam pievienosim ģeneratora frekvences kontroli no taustiņsitieniem un uztvērēju ar dekoderi, kas izvadīs saņemtās komandas uz konsoli. Pēc maiņas ģeneratoram jāražo 6 frekvenču toņi, ar kuriem iekodēsim komandas skaļuma palielināšanai/samazinašanai, kanāla maiņai, televizora ieslēgšanai/izslēgšanai. Lai konfigurētu detektoru, tiek izmantota šāda 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;

Detektoram var piešķirt 10 no šīm struktūrām, tāpēc vienu detektoru var konfigurēt, lai noteiktu desmit divu toņu signālus. Bet mēs izmantosim tikai sešus viena toņa signālus. Lai pārsūtītu iestatījumus uz detektoru, tiek izmantota metode MS_TONE_DETECTOR_ADD_SCAN.

Lai detektors mums paziņotu, ka tā ieejā ir nonācis signāls ar vēlamajiem frekvences komponentiem, mums tas ir jānodrošina ar atzvanīšanas funkciju, ko tas šajā gadījumā palaidīs. Tas tiek darīts, izmantojot funkciju ms_filter_set_notify_callback(). Kā argumentus tas saņem rādītāju uz filtru, rādītāju uz atzvanīšanas funkciju un rādītāju uz datiem, kurus mēs vēlētos nodot atzvanīšanas funkcijai (lietotāja dati).

Kad detektors tiek aktivizēts, atzvanīšanas funkcija saņems lietotāja datus, rādītāju uz detektora filtru, notikuma identifikatoru un struktūru, kas apraksta notikumu:


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

Signāla apstrādes blokshēma ir parādīta titulbildē.

Nu tagad pats programmas kods ar komentāriem.

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

Mēs apkopojam un palaižam programmu. Ja viss darbojas pareizi, pēc palaišanas mums vajadzētu iegūt kaut ko līdzīgu šādai programmas darbībai:

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

Nospiediet jebkuru taustiņu no “1” līdz “6”, apstiprinot ar taustiņu “Enter”, jums vajadzētu iegūt kaut ko līdzīgu šim sarakstam:


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

Mēs redzam, ka komandu signāli ir veiksmīgi nosūtīti un detektors tos nosaka.

Nākamajā rakstā mēs pievērsīsimies audio signāla pārraidīšanai Ethernet tīklā, izmantojot RTP protokolu, un nekavējoties to izmantosim mūsu tālvadības pultī.

Avots: www.habr.com

Pievieno komentāru