El material del artículo fue tomado de mi .
Detección de señal tonal
En el anterior Hemos creado un medidor de nivel de señal. En este aprenderemos a detectar una señal tonal.
![]()
En tiempos pasados, cuando no todas las familias tenían un televisor, y muchas de ellas cambiaban de canal con pinzas, apareció en las reseñas de la prensa técnica extranjera una intrigante noticia: uno de los fabricantes de televisores equipó sus aparatos con un control remoto inalámbrico. Se conocían pocos detalles, pero se sabía que el control remoto funcionaba sin baterías gracias a un enfoque inusual: era mecánico y representaba un híbrido entre un instrumento musical — un metalófono — y un revólver. En el tambor del revólver había cilindros metálicos de diferentes longitudes, y cuando el martillo golpeaba uno de ellos, el cilindro comenzaba a vibrar a su propia frecuencia. Presumiblemente, en ultrasonido. La electrónica del televisor escuchaba esta señal y, al determinar su frecuencia, realizaba la acción correspondiente: cambiar de canal, ajustar el volumen, apagar el televisor.
Hoy intentaremos reconstruir este sistema de transmisión de comandos, aprovechando nuestro conocimiento sobre el reproductor multimedia.
Para simular el control remoto, utilizaremos el texto de nuestro generador tonal de ejemplo. Agregaremos control de frecuencia del generador mediante pulsaciones del teclado y un receptor con decodificador, que mostrará en la consola los comandos recibidos. Tras la modificación, el generador deberá emitir señales tonales de 6 frecuencias, con las que codificaremos los comandos de aumento/disminución de volumen, cambio de canal y encendido/apagado del televisor. Para configurar el detector, se utiliza la estructura:
struct _MSToneDetectorDef{
char tone_name[8];
int frequency;
int min_duration;
float min_amplitude;
};
typedef struct _MSToneDetectorDef MSToneDetectorDef;Se pueden pasar 10 tales estructuras al detector, es decir, un solo detector se puede configurar para detectar diez señales tono-doble. Pero nosotros usaremos solo seis señales tono-simple. Para transmitir la configuración al detector se utiliza el método MS_TONE_DETECTOR_ADD_SCAN.
Para que el detector pueda notificarnos que ha recibido una señal con las componentes de frecuencia deseadas, debemos proporcionarle una función de callback que se ejecutará en tal caso. Esto se realiza utilizando la función ms_filter_set_notify_callback(). Como argumentos, recibe un puntero al filtro, un puntero a la función de callback y un puntero a los datos que quisiéramos pasar a la función de callback (datos del usuario).
Cuando se activa el detector, la función de callback recibirá los datos del usuario, un puntero al filtro del detector, el identificador del evento y una estructura que describe el evento:
/** * 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;
El esquema estructural de procesamiento de señales se muestra en la imagen de encabezado.
Y ahora, el código del programa con comentarios.
/* Файл 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);
}
}Compilamos y ejecutamos el programa. Si todo funciona correctamente, al iniciar deberíamos obtener un comportamiento del programa aproximadamente así:
$ ./mstest4
ALSA lib conf.c:4738:(snd_config_expand) Parámteros desconocidos 0
ALSA lib control.c:954:(snd_ctl_open_noupdate) CTL inválido default:0
ortp-warning-No se pudo conectar el mezclador a la tarjeta: Argumento inválido
ALSA lib conf.c:4738:(snd_config_expand) Parámteros desconocidos 0
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) PCM desconocido default:0
ALSA lib conf.c:4738:(snd_config_expand) Parámteros desconocidos 0
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) PCM desconocido default:0
ortp-warning-Inusual, la tarjeta de sonido Intel 82801AA-ICH parece no ser capaz de nada, reintentando con plughw...
Presione la tecla de comando y luego enter.
Para salir del programa, ingrese 0.
ortp-warning-alsa_set_params: periodsize:256 Usando 256
ortp-warning-alsa_set_params: period:8 Usando 8
Presionamos cualquier tecla del "1" al "6", confirmando con la tecla "Enter", deberíamos obtener un listado aproximadamente así:
2
Comando enviado: 2
Comando recibido: V-
1
Comando enviado: 1
Comando recibido: V+
3
Comando enviado: 3
Comando recibido: C+
4
Comando enviado: 4
Comando recibido: C-
0
$Vemos que las señales de comando se envían con éxito y el detector las detecta.
En el siguiente artículo abordaremos la transmisión de señales de sonido a través de la red Ethernet utilizando el protocolo RTP y lo aplicaremos de inmediato en nuestro control remoto.
Fuente: habr.com
