O conteúdo deste artigo foi retirado do meu .
Detector de sinal tonal
Na parte anterior Criamos um medidor de nível de sinal. Neste curso, aprenderemos a detectar sinais tonais.
![]()
Nos velhos tempos, quando nem toda família tinha uma televisão e metade delas mudava os canais com um alicate, surgiu uma notícia intrigante na cobertura da imprensa técnica estrangeira, informando que um dos fabricantes de televisores equipou seus dispositivos com um controle remoto sem fio. Sabia-se que o controle funcionava sem baterias, graças a um conceito incomum — o controle era mecânico e representava um híbrido de um instrumento musical — um metalofone e um revólver. No tambor do revólver haviam cilindros metálicos, diferentes em comprimento, e quando o percussor atingia um deles, o cilindro começava a vibrar em sua própria frequência. Presumivelmente, em ultrassom. A eletrônica na televisão ouvia esse sinal e, ao identificar sua frequência, realizava a ação correspondente — trocar de canal, alterar o volume, desligar a televisão.
Hoje tentaremos fazer uma reconstrução desse sistema de transmissão de comandos, usando nosso conhecimento sobre streaming de mídia.
Para simular o controle remoto, usaremos o texto do nosso exemplo de gerador tonal. Vamos adicionar ao código o controle da frequência do gerador com pressionamentos de teclas e um receptor com decodificador, que imprimirá no console os comandos recebidos. Após as alterações, o gerador deve emitir sinais tonais com 6 frequências que codificaremos em comandos de aumentar/reduzir volume, trocar de canal, ligar/desligar a televisão. Para configurar o detector, usamos a estrutura:
struct _MSToneDetectorDef{
char tone_name[8];
int frequency; /**< Frequência esperada do tom */
int min_duration; /**< Duração mínima do tom em milissegundos */
float min_amplitude; /**< Amplitude mínima do tom, 1.0 correspondente ao nível normalizado de 0dbm */
};
typedef struct _MSToneDetectorDef MSToneDetectorDef;É possível passar 10 dessas estruturas para o detector, assim um único detector pode ser ajustado para detectar dez sinais tonais duplos. Mas nós usaremos apenas seis sinais tonais simples. Para passar as configurações ao detector, usa-se o método MS_TONE_DETECTOR_ADD_SCAN.
Para que o detector possa nos avisar quando um sinal com as frequências desejadas chega à sua entrada, precisamos fornecer uma função de retorno de chamada que ele executará nessa situação. Isso é feito através da função ms_filter_set_notify_callback(). Ela recebe como argumentos um ponteiro para o filtro, um ponteiro para a função de retorno de chamada, e um ponteiro para os dados que gostaríamos de passar para a função de retorno de chamada (dados do usuário).
Quando o detector é acionado, a função de retorno de chamada receberá os dados do usuário, um ponteiro para o filtro do detector, um identificador de evento e uma estrutura que descreve o 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;
O esquema estrutural do processamento do sinal está mostrado na imagem principal.
Agora, vamos ao código do programa com comentários.
/* Файл 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 e executamos o programa. Se tudo funcionar corretamente, ao iniciar, devemos obter um comportamento semelhante ao seguinte:
$ ./mstest4
ALSA lib conf.c:4738:(snd_config_expand) Parâmetros desconhecidos 0
ALSA lib control.c:954:(snd_ctl_open_noupdate) CTL inválido:0
ortp-warning-Não foi possível conectar mixer ao cartão: Argumento inválido
ALSA lib conf.c:4738:(snd_config_expand) Parâmetros desconhecidos 0
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) PCM desconhecido:0
ALSA lib conf.c:4738:(snd_config_expand) Parâmetros desconhecidos 0
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) PCM desconhecido:0
ortp-warning-Estranho, o cartão de som Intel 82801AA-ICH não parece ser capaz de nada, tentando com plughw...
Pressione a tecla de comando e, em seguida, Enter.
Para encerrar o programa, digite 0.
ortp-warning-alsa_set_params: periodsize:256 Usando 256
ortp-warning-alsa_set_params: period:8 Usando 8
Pressione qualquer tecla de "1" a "6", confirmando com a tecla "Enter", deve resultar em uma listagem semelhante a esta:
2
Comando enviado: 2
Comando recebido: V-
1
Comando enviado: 1
Comando recebido: V+
3
Comando enviado: 3
Comando recebido: C+
4
Comando enviado: 4
Comando recebido: C-
0
$Podemos ver que os tons dos comandos estão sendo enviados com sucesso e o detector os reconhece.
No próximo artigo, abordaremos a transmissão de sinais de áudio pela rede Ethernet usando o protocolo RTP e aplicaremos isso em nosso controle remoto.
Fonte: habr.com
