查看: 265|回复: 4

Teensy 实现听音辨数

[复制链接]

该用户从未签到

发表于 2019-6-26 20:58 | 显示全部楼层 |阅读模式
有一千个读者便有一千个哈姆雷特。又好比鲁迅先生评价《红楼梦》 “经学家看见《易》,道学家看见淫,才子看见缠绵,革命家看见排满,流言家看见宫闱秘事。” 再比如经常被人批判的《古惑仔》系列电影,觉得它教坏小朋友,我觉得并不是。在我看起来应该属于青春励志题材。展现了以陈浩南为首的香港下层青年人不甘平庸,通过打拼努力向上的故事。当然,还可以说他是批判现实主义题材的作品,也包含了做人的道理。比如,影片中靓坤转眼间就被几个小时前羞辱的小警察射杀。
image001.jpg
再比如3Q大战之时,记者采访了红衣教主,没成想有牛人直接从采访的视频声音中分析出来了他的电话【参考1】。谁能想到流氓软件之父竟然栽倒这样的事情中。
这次我们使用 Teensy 和数字麦克风来实现这个功能。
先介绍一下原理。电话实现拨号有两种方式:脉冲拨号和音频拨号。
脉冲拨号是一种时域处理方法,它用脉冲的个数来表示号码数字。脉冲拨号方式对脉冲的宽度、大小、间距、形状都有着严格的要求,如果由于线路的干扰或其他原因而使得这些参数发生了变化,则可能引起号码接收的错误。另一方面,由于每个脉冲都占有一定的时间(一般每个脉冲占用的时间为100ms),而使得这种拨号方式比较慢。当拨号时,用户通常会听到一串拨号音,老式的转盘电话就使用脉冲拨号。比如,拨号“0”时,电路“断”、“续”10次,代表数字“0”。可以看到,如果号码较长拨号耗时也会很长。因此,这种拨号方式逐渐为音频拨号所取代。
我们常用的音频拨号是双音多频 DTMFDual Tone Multi Frequency),双音多频,由高频群和低频群组成,低频群包含3个频率,高频群包含4个频率。一个高频信号和一个低频信号叠加组成一个组合信号,代表一个数字。
  
  
1209
1336
1447
697
1
2
3
770
4
5
6
852
7
8
9
941
*
0
#


比如,用频率为770Hz 的正弦波加到1366Hz 的正弦波合成一个声音表示数字“5”
这个合成过程用Matlab 模拟如下:
首先是 770Hz 的正弦波:
image003.jpg
这是 1366Hz 的正弦波
image004.png
二者相加的结果是
image006.png
对我们来说,目标是将上面这个合成后的结果分解得到具体是哪两个信号合成的。自然而然想到使用傅立叶变换来处理:
image008.png
放大,可以看到是 770 1366 合成的。
image010.png
这次选择 Teensy+SPH0645LM4H实现声音的采集和分析,最终结果显示在一个 1602LCD上。
接线方式:
Teensy 3.2    SPH0645LM4H
       GND             SEL
       D23             LRCL
        D13             DOUT
        D9              BCLK
        GND           GND
        3.3V            3V
Teensy 3.2    LCD1602
         GND           GND
         Vin              VCC      
         Pin19          SCL
         Pin18          SDA

代码上分析获得频率并没有通过傅立叶变换,而是直接使用 Teensy 的音频库中的AudioAnalyzeToneDetect函数。这个函数使用的是Goertzel算法,该算法的主要思想是检查音频数据中是否包含某一个给定的频率【参考2】。因此,对于我们来说就是不断测试当前的信号中是否有6971477这些频率,如果存在的话就转化为对应的数字。
完整代码如下:

kittenblock中小学创客名师推荐的图形化编程软件

// 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("%   ");

}


工作的照片:
image013.jpg

工作的视频:
很明显,你的按键信息包含在了发出的声音中,这是一个安全隐患。现实生活中,自动柜员机(ATM)也有一个键盘。仔细观察能发现每个按键会发出相同的声音。但是在十几年前,那个键盘的数字对应着的是不同的声音………..
参考:
1. https://www.cnblogs.com/emouse/archive/2012/09/01/2666308.html?utm_source=debugrun&utm_medium=referral转:技术宅逆天了!如何从按键音中听出周鸿祎的手机号码


打赏作者鼓励一下!
  • TA的每日心情
    奋斗
    2019-4-21 16:28
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2019-6-27 15:13 | 显示全部楼层
    十几二十年前盗打长途电话就是这么干了,有是锁 0 键.有点整个键盘都用箱子锁起来只留个话筒在外面.

    该用户从未签到

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

    该用户从未签到

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

    该用户从未签到

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

    是的,关于 USB Host 的有很多篇。
    打赏作者鼓励一下!
    您需要登录后才可以回帖 登录 | 立即注册  

    本版积分规则

    Copyright   ©2015-2016  Arduino中文社区  Powered by©Discuz!   ( 蜀ICP备14017632号-3 )
    快速回复 返回顶部 返回列表