חקר מנוע ה-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(). כארגומנטים הוא מקבל מצביע למסנן, מצביע לפונקציית ה-callback ומצביע לנתונים שנרצה להעביר לפונקציית ה-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

הוספת תגובה