Teensy 实现听音辨数-Arduino中文社区 - Powered by Discuz! Archiver

Zoologist 发表于 2019-6-26 20:58

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

lbq691477940 发表于 2019-6-27 15:13

十几二十年前盗打长途电话就是这么干了,有是锁 0 键.有点整个键盘都用箱子锁起来只留个话筒在外面.

impking 发表于 2019-7-11 12:21

老大好,又来膜拜了。之前按照网上的帖子,实验过一个“简易频率计”,可以分辨出声音的基频,当时为什么会关注呢,是因为在b站看到了一个软件“imitone”,可以人声转midi,不知道内部原理和你这个项目是不是一样,但你这个牛啊,可以开发成硬件版的imitone。

impking 发表于 2019-7-11 12:26

BTW,哈哈,我知道你叫什么名字了,前段时间翻了16年的无线电,上面有几篇喜欢的文章,一看就知道是你的了。

Zoologist 发表于 2019-7-11 13:07

impking 发表于 2019-7-11 12:26
BTW,哈哈,我知道你叫什么名字了,前段时间翻了16年的无线电,上面有几篇喜欢的文章,一看就知道是你的了 ...

是的,关于 USB Host 的有很多篇。
页: [1]
查看完整版本: Teensy 实现听音辨数