电话拨号时的“嘟嘟”声由两组单频信号叠加而成。
| 按键 | 1209Hz | 1336Hz | 1447Hz | 1663Hz |
|---|---|---|---|---|
| 697Hz | 1 | 2 | 3 | A |
| 770Hz | 4 | 5 | 6 | B |
| 852Hz | 7 | 8 | 9 | C |
| 941Hz | * | 0 | # | D |
[waveData,Fs] = audioread(".m4a")
% waveData 是读取到的音频数据 常常是双通道的
% Fs 是该音频文件的采样速率
waveDataHalf = waveData(:,1) %此次只使用 waveData 的一个通道的数据
save -ascii waveDataHalf
% 保存数据
用 Python 处理数据
刚插入耳机时会有一段强度很大的信号,所以需要截断时域信号的开始部分
线性搜索截断出每一个拨号声音的区间
独立的对每一个拨号音频做FFT即可获得单个拨号音频的频谱
对8个频率附近分别做理想带通滤波器
低频组的4个和高频组的4个频率各选出一个强度最高的频率组合出键值
import numpy as np
import matplotlib.pyplot as plt
from scipy.fft import fft
freqLow1 = 697 #Hz
freqLow2 = 770
freqLow3 = 852
freqLow4 = 941
freqHigh1 = 1209
freqHigh2 = 1336
freqHigh3 = 1447
freqHigh4 = 1633
threshold = 25 #Hz 使用了8个理想带通滤波器,这个是带通滤波器的门限
sampleFreq = 48000 #Hz 采样频率
searchThreshold = 0.03 #信号强度高于0.03,认为不是噪声
detectSpace = 4800
avgParm = 200
def get_key_val(data):
dataFFT = abs(fft(data))
sPointNum = len(data) #样点数目
k = np.array(range(1,sPointNum+1))
f = k*sampleFreq/sPointNum
window = np.zeros((len(data))) #设置理想带通滤波器
for i in range(0,len(f)):
if f[i] > freqLow1-threshold and f[i] < freqLow1+threshold:
window[i] = 1
if f[i] > freqLow2-threshold and f[i] < freqLow2+threshold:
window[i] = 1
if f[i] > freqLow3-threshold and f[i] < freqLow3+threshold:
window[i] = 1
if f[i] > freqLow4-threshold and f[i] < freqLow4+threshold:
window[i] = 1
if f[i] > freqHigh1-threshold and f[i] < freqHigh1+threshold:
window[i] = 1
if f[i] > freqHigh2-threshold and f[i] < freqHigh2+threshold:
window[i] = 1
if f[i] > freqHigh3-threshold and f[i] < freqHigh3+threshold:
window[i] = 1
if f[i] > freqHigh4-threshold and f[i] < freqHigh4+threshold:
window[i] = 1
dataFFT = dataFFT*window
max_vals = [0,0,0,0,0,0,0,0] #分别找8个频带内信号的最大值
max_vals[0] = max(dataFFT[int((freqLow1-threshold)*sPointNum/sampleFreq-1):int((freqLow1+threshold)*sPointNum/sampleFreq+1)]) #k=f*sampleFreq/sPointNum
max_vals[1] = max(dataFFT[int((freqLow2-threshold)*sPointNum/sampleFreq-1):int((freqLow2+threshold)*sPointNum/sampleFreq+1)])
max_vals[2] = max(dataFFT[int((freqLow3-threshold)*sPointNum/sampleFreq-1):int((freqLow3+threshold)*sPointNum/sampleFreq+1)])
max_vals[3] = max(dataFFT[int((freqLow4-threshold)*sPointNum/sampleFreq-1):int((freqLow4+threshold)*sPointNum/sampleFreq+1)])
max_vals[4] = max(dataFFT[int((freqHigh1-threshold)*sPointNum/sampleFreq-1):int((freqHigh1+threshold)*sPointNum/sampleFreq+1)])
max_vals[5] = max(dataFFT[int((freqHigh2-threshold)*sPointNum/sampleFreq-1):int((freqHigh2+threshold)*sPointNum/sampleFreq+1)])
max_vals[6] = max(dataFFT[int((freqHigh3-threshold)*sPointNum/sampleFreq-1):int((freqHigh3+threshold)*sPointNum/sampleFreq+1)])
max_vals[7] = max(dataFFT[int((freqHigh4-threshold)*sPointNum/sampleFreq-1):int((freqHigh4+threshold)*sPointNum/sampleFreq+1)])
max1 = max2 = max1Index = max2Index = 0 #低频组和高频组各找一个强度最大的信号所在的频率
for i in range(4):
if max_vals[i] > max1:
max1 = max_vals[i]
max1Index = i
for i in range(4,8):
if max_vals[i] > max2:
max2 = max_vals[i]
max2Index = i
keyVal = max1Index + 4*(max2Index-4) #换算出相应键值
keyName = "147*2580369#ABCD" #键值换算出按键
return keyName[keyVal] #返回键值
def find_boarder(pos, data): #在data[pos]附近寻找该拨号信号的左右边界,结果以数组返回
L = R = pos
while(max(data[L-avgParm:L]) > searchThreshold):
L -= avgParm
while (max(data[R:R+avgParm]) > searchThreshold):
R += avgParm
return [L,R]
# main
ans = []
F = open(r"")
data = F.read()
data = np.array(data.split())
data = np.delete(data,[range(0,20000)]) #截断前20000个采样点
data = [float(i) for i in data]
plt.plot(range(len(data)),data)
#plt.show() #读入音频信号显示
print("截断区间")
print("下限 上限 区间间隔")
i = 0
while i < len(data):
if max(data[i:i+avgParm]) > searchThreshold:
tmp = find_boarder(i,data)
print(tmp[0], tmp[1], tmp[1]-tmp[0])
plt.plot(range(tmp[1] - tmp[0]), data[tmp[0]:tmp[1]])
#plt.show() #手机号截断效果显示
i = tmp[1]
if tmp[1]-tmp[0] > 0:
ans.append(get_key_val(data[tmp[0]:tmp[1]]))
i += detectSpace
F.close()
print(ans) #输出手机号
print(len(ans)) #输出结果长度



