Teensy 实现听音辨数
有一千个读者便有一千个哈姆雷特。又好比鲁迅先生评价《红楼梦》 “经学家看见《易》,道学家看见淫,才子看见缠绵,革命家看见排满,流言家看见宫闱秘事。” 再比如经常被人批判的《古惑仔》系列电影,觉得它教坏小朋友,我觉得并不是。在我看起来应该属于青春励志题材。展现了“以陈浩南为首的香港下层青年人不甘平庸,通过打拼努力向上”的故事。当然,还可以说他是批判现实主义题材的作品,也包含了做人的道理。比如,影片中靓坤转眼间就被几个小时前羞辱的小警察射杀。再比如3Q大战之时,记者采访了红衣教主,没成想有牛人直接从采访的视频声音中分析出来了他的电话【参考1】。谁能想到流氓软件之父竟然栽倒这样的事情中。这次我们使用 Teensy 和数字麦克风来实现这个功能。先介绍一下原理。电话实现拨号有两种方式:脉冲拨号和音频拨号。脉冲拨号是一种时域处理方法,它用脉冲的个数来表示号码数字。脉冲拨号方式对脉冲的宽度、大小、间距、形状都有着严格的要求,如果由于线路的干扰或其他原因而使得这些参数发生了变化,则可能引起号码接收的错误。另一方面,由于每个脉冲都占有一定的时间(一般每个脉冲占用的时间为100ms),而使得这种拨号方式比较慢。当拨号时,用户通常会听到一串拨号音,老式的转盘电话就使用脉冲拨号。比如,拨号“0”时,电路“断”、“续”10次,代表数字“0”。可以看到,如果号码较长拨号耗时也会很长。因此,这种拨号方式逐渐为音频拨号所取代。我们常用的音频拨号是双音多频 DTMF(Dual Tone Multi Frequency),双音多频,由高频群和低频群组成,低频群包含3个频率,高频群包含4个频率。一个高频信号和一个低频信号叠加组成一个组合信号,代表一个数字。120913361447
697123
770456
852789
941*0#
比如,用频率为770Hz 的正弦波加到1366Hz 的正弦波合成一个声音表示数字“5”这个合成过程用Matlab 模拟如下:首先是 770Hz 的正弦波:
这是 1366Hz 的正弦波二者相加的结果是对我们来说,目标是将上面这个合成后的结果分解得到具体是哪两个信号合成的。自然而然想到使用傅立叶变换来处理:放大,可以看到是 770 和 1366 合成的。这次选择 Teensy+SPH0645LM4H实现声音的采集和分析,最终结果显示在一个 1602LCD上。接线方式: Teensy 3.2 SPH0645LM4H GND SEL D23 LRCL D13 DOUT D9 BCLK GND GND 3.3V 3VTeensy 3.2 LCD1602 GND GND Vin VCC Pin19 SCL Pin18 SDA
代码上分析获得频率并没有通过傅立叶变换,而是直接使用 Teensy 的音频库中的AudioAnalyzeToneDetect函数。这个函数使用的是Goertzel算法,该算法的主要思想是检查音频数据中是否包含某一个给定的频率【参考2】。因此,对于我们来说就是不断测试当前的信号中是否有697到1477这些频率,如果存在的话就转化为对应的数字。完整代码如下:// Dial Tone (DTMF) decoding
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3f,20,4);
// Create the Audio components.These should be created in the
// order data flows, inputs/sources -> processing -> outputs
//
AudioInputI2S audioIn;
AudioAnalyzeToneDetect row1; // 7 tone detectors are needed
AudioAnalyzeToneDetect row2; // to receive DTMF dial tones
AudioAnalyzeToneDetect row3;
AudioAnalyzeToneDetect row4;
AudioAnalyzeToneDetect column1;
AudioAnalyzeToneDetect column2;
AudioAnalyzeToneDetect column3;
// Create Audio connections between the components
//
AudioConnection patchCord01(audioIn, 0, row1, 0);
AudioConnection patchCord02(audioIn, 0, row2, 0);
AudioConnection patchCord03(audioIn, 0, row3, 0);
AudioConnection patchCord04(audioIn, 0, row4, 0);
AudioConnection patchCord05(audioIn, 0, column1, 0);
AudioConnection patchCord06(audioIn, 0, column2, 0);
AudioConnection patchCord07(audioIn, 0, column3, 0);
int charpos=0;
void setup() {
// Audio connections require memory to work.For more
// detailed information, see the MemoryAndCpuUsage example
AudioMemory(12);
lcd.init();
lcd.backlight();
while (!Serial);
delay(100);
Serial.println("Start decoding");
// Configure the tone detectors with the frequency and number
// of cycles to match.These numbers were picked for match
// times of approx 30 ms.Longer times are more precise.
row1.frequency(697, 21);
row2.frequency(770, 23);
row3.frequency(852, 25);
row4.frequency(941, 28);
column1.frequency(1209, 36);
column2.frequency(1336, 40);
column3.frequency(1477, 44);
}
const float row_threshold = 0.009;
const float column_threshold = 0.009;
char lastdigit=0;
void loop() {
float r1, r2, r3, r4, c1, c2, c3;
char digit=0;
// read all seven tone detectors
r1 = row1.read();
r2 = row2.read();
r3 = row3.read();
r4 = row4.read();
c1 = column1.read();
c2 = column2.read();
c3 = column3.read();
/*
// print the raw data, for troubleshooting
Serial.print("tones: ");
Serial.print(r1);
Serial.print(", ");
Serial.print(r2);
Serial.print(", ");
Serial.print(r3);
Serial.print(", ");
Serial.print(r4);
Serial.print(", ");
Serial.print(c1);
Serial.print(", ");
Serial.print(c2);
Serial.print(", ");
Serial.println(c3);
*/
// check all 12 combinations for key press
if (r1 >= row_threshold) {
if (c1 > column_threshold) {
digit = '1';
} else if (c2 > column_threshold) {
digit = '2';
} else if (c3 > column_threshold) {
digit = '3';
}
} else if (r2 >= row_threshold) {
if (c1 > column_threshold) {
digit = '4';
} else if (c2 > column_threshold) {
digit = '5';
} else if (c3 > column_threshold) {
digit = '6';
}
} else if (r3 >= row_threshold) {
if (c1 > column_threshold) {
digit = '7';
} else if (c2 > column_threshold) {
digit = '8';
} else if (c3 > column_threshold) {
digit = '9';
}
} else if (r4 >= row_threshold) {
if (c1 > column_threshold) {
digit = '*';
} else if (c2 > column_threshold) {
digit = '0';
} else if (c3 > column_threshold) {
digit = '#';
}
}
// print the key, if any found
if ((digit > 0)&&(lastdigit!=digit)) {
//if (digit > 0) {
Serial.print("--> Key: ");
Serial.println(digit);
lcd.setCursor(charpos % 16, (charpos / 16)%2);
lcd.print(digit);
charpos++;
}
lastdigit=digit;
// uncomment these lines to see how much CPU time
// the tone detectors and audio library are using
//Serial.print("CPU=");
//Serial.print(AudioProcessorUsage());
//Serial.print("%, max=");
//Serial.print(AudioProcessorUsageMax());
//Serial.print("% ");
}
工作的照片:
工作的视频:https://zhuanlan.zhihu.com/p/70966370很明显,你的按键信息包含在了发出的声音中,这是一个安全隐患。现实生活中,自动柜员机(ATM)也有一个键盘。仔细观察能发现每个按键会发出相同的声音。但是在十几年前,那个键盘的数字对应着的是不同的声音………..参考:1. https://www.cnblogs.com/emouse/archive/2012/09/01/2666308.html?utm_source=debugrun&utm_medium=referral转:技术宅逆天了!如何从按键音中听出周鸿祎的手机号码2. https://blog.csdn.net/silent123go/article/details/54022037
十几二十年前盗打长途电话就是这么干了,有是锁 0 键.有点整个键盘都用箱子锁起来只留个话筒在外面. 老大好,又来膜拜了。之前按照网上的帖子,实验过一个“简易频率计”,可以分辨出声音的基频,当时为什么会关注呢,是因为在b站看到了一个软件“imitone”,可以人声转midi,不知道内部原理和你这个项目是不是一样,但你这个牛啊,可以开发成硬件版的imitone。 BTW,哈哈,我知道你叫什么名字了,前段时间翻了16年的无线电,上面有几篇喜欢的文章,一看就知道是你的了。 impking 发表于 2019-7-11 12:26
BTW,哈哈,我知道你叫什么名字了,前段时间翻了16年的无线电,上面有几篇喜欢的文章,一看就知道是你的了 ...
是的,关于 USB Host 的有很多篇。
页:
[1]