Εξερευνώντας τη μηχανή 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 και θα το εφαρμόσουμε αμέσως στο τηλεχειριστήριό μας.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο