# Android 拨号音DTMF 编码解码理论和实战

## f(n)=2*cos(w*Ts)*f(n-1) - f(n-2)

float a1=2*cos(w*Ts);
float a2=-1;

a1=2*cos(2*pi*440/8000)=1.8817615，

short y[3] = {0, 0x15AE,0}; // y(n), y(n-1), y(n-2)
short a1=0x786F;
short a2=0xC000;
short singen(){
y[0]=( (long)a1*(long)y[1]+(long)a2*(long)y[2] )>>14;
y[2]=y[1];   ////  递归推导公式，f(n)
y[1]=y[0];
return y[0];

## 二、 对应Android代码分解

文件： ./media/libaudioclient/ToneGenerator.cpp

const ToneGenerator::ToneDescriptor ToneGenerator::sToneDescriptors[] = {
{ .segments = {

{ .duration = ToneGenerator::TONEGEN_INF,

.waveFreq =  { 1336,     941,    0 },      0,   0},
{ .duration = 0 ,

.waveFreq = { 0 }, 0, 0}},   /////暂停时间，间隔
.repeatCnt = ToneGenerator::TONEGEN_INF,
.repeatSegment = 0 },     // TONE_DTMF_0

........................  //  看到这个就明白了

### /////  准备好13个键的 Tone，f1， f2数据， 0 ~9 * #

while (mpToneDesc->segments[segmentIdx].duration) {    ////  13 个 Segment
// Get total number of sine waves: needed to adapt sine wave gain.
unsigned int lNumWaves = numWaves(segmentIdx);
unsigned int freqIdx = 0;
unsigned int frequency = mpToneDesc->segments[segmentIdx].waveFreq[freqIdx];
while (frequency) {
// Instantiate a wave generator if  ot already done for this frequency
if (mWaveGens.indexOfKey(frequency) == NAME_NOT_FOUND) {
ToneGenerator::WaveGenerator *lpWaveGen =
new ToneGenerator::WaveGenerator(mSamplingRate,
frequency,
TONEGEN_GAIN/lNumWaves);

//// 将频率和对象存入向量中，以供后续使用
}

frequency = mpNewToneDesc->segments[segmentIdx].waveFreq[++freqIdx];
}
segmentIdx++;

### void ToneGenerator::audioCallback(int event, void* user, void *info) {

////// 由于tone不是声音文件，需要按规则生成

unsigned int lFreqIdx = 0;
unsigned short lFrequency = lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[lFreqIdx];

while (lFrequency != 0) {
WaveGenerator *lpWaveGen = lpToneGen->mWaveGens.valueFor(lFrequency);
lpWaveGen->getSamples(lpOut, lGenSmp, lWaveCmd);

/// 请注意：lpOut，用的巧，高低频率都在这里加上了
lFrequency = lpToneDesc->segments[lpToneGen->mCurSegment].waveFreq[++lFreqIdx];
}

### ToneGenerator::WaveGenerator::WaveGenerator(uint32_t samplingRate,         unsigned short frequency, float volume) {

F_div_Fs = frequency / (double)samplingRate;
d0 = - (float)GEN_AMP * sin(2 * M_PI * F_div_Fs);
mS2_0 = (short)d0;
mAmplitude_Q15 = (short)(32767. * 32767. * volume / GEN_AMP); // 这个不太理解

d0 = 32768.0 * cos(2 * M_PI * F_div_Fs);  // Q15*2*cos()

### void ToneGenerator::WaveGenerator::getSamples(short *outBuffer,

// loop generation
while (count) {
count--;
Sample = ((lA1  *  lS1) >> S_Q14) -  lS2;
// shift delay
lS2 = lS1;
lS1 = Sample;
Sample = (lAmplitude * Sample) >> S_Q15;
*(outBuffer++) += (short)Sample;  // put result in buffer，使用加号很有意义
}

### 三、Dtmf音的识别，反解码算法

从某些连续段落的波形资料中找到是某两个已定义的高频与低频的组合, 即可对应出 tone digit.

def goertzel_mag(numSamples, target_freq, sample_rate, data):
'''
int k, i
float floatnumSamples
float omega, sine, cosine, coeff, q0, q1, r2, magnitude, real, imag
'''
#floatnumSamples = (float)numSamples

scalingFactor = numSamples / 2.0
k = (int) (0.5 + ((numSamples * target_freq)/sample_rate))
omega = (2.0 * math.pi * k)/numSamples
sine = math.sin(omega)
cosine = math.cos(omega)
coeff = 2.0 * cosine
q0 = 0
q1 = 0
q2 = 0

for i in range(0, (int)(numSamples)):
#print("Hello")
q0 = (coeff * q1) - q2 + data[i]
q2 = q1
q1 = q0

#real = (q1 - (q2 * cosine)) / scalingFactor
#imag = (q2 * sine) / scalingFactor

#magnitude = math.sqrt((real * real) + (imag * imag))
magnitude = (q2*q2) + (q1*q1) - (coeff * q1 * q2)
return magnitude

max_mag_hi_freq = 0
maxvalue_mag_hi_freq = 0
for freq in hi_freqs:
mag = goertzel_mag(chunk_sample_count, freq, sample_rate, chunk_data)
#if c == 277:
#    print("(%d)CHUNK %d magnitude with higher frequency (%d) = %f" % \
#          (max_mag_hi_freq, c, freq, mag))
if(mag > maxvalue_mag_hi_freq):
maxvalue_mag_hi_freq = mag
max_mag_hi_freq = freq

max_mag_lo_freq = 0
maxvalue_mag_lo_freq = 0
for freq in lo_freqs:
mag = goertzel_mag(chunk_sample_count, freq, sample_rate, chunk_data)
#print("CHUNK %d magnitude with lower frequency (%d) = %f" % \
#      (c, freq, mag))
if(mag > maxvalue_mag_lo_freq):
maxvalue_mag_lo_freq = mag
max_mag_lo_freq = freq

参考原文：https://blog.csdn.net/hankhanti/article/details/49902441

1. Px和Py是否足够强大？我们可以设定一个门限，如果么Px和Py这两个任何一个低于这个门限，那么N个采样被评估为没有识别出DTMF符号。参考资料[2]中建议这个门限值为4*105。但是如果采样值的取值范围是-32768到32767的话，实际上计算出来的P值会非常大，这个门限设为4*109都可以。
2. Px和Py的差别是否太大？正常的DTMF信号，这两个能量应该接近，那么如果差别较大，我们视为无效。参考资料[2]中建议的方法为：如果Py < Px * 0.398，那么认为无效。如果Px < Py * 0.158也认为无效。但是实际上，我们将0.158改为0.5，识别效果更佳。
3. 其它频率的能量P有没有很多接近Px和Py的？参考资料[2]中建议的方法为：首先取近Px和Py中较大的那个，设为Pm，如果其他频率的P值有2个以上达到了Pm的15.8%，那么认为是噪音导致，视为无效。

解码算法：
/* Twist check
* CEPT => twist < 6dB
* AT&T => forward twist < 4dB and reverse twist < 8dB
*  -ndB < 10 log10( v1 / v2 ), where v1 < v2
*  -4dB < 10 log10( v1 / v2 )
*  -0.4  < log10( v1 / v2 )
*  0.398 < v1 / v2
*  0.398 * v2 < v1
*/
        if ( r[col] > r[row] )
{
/* Normal twist */
max_index = col;
if ( r[row] < (r[col] * 0.398) )    /* twist > 4dB, error */
see_digit = FALSE;
}
else /* if ( r[row] > r[col] ) */
{
/* Reverse twist */
max_index = row;
if ( r[col] < (r[row] * 0.158) )    /* twist > 8db, error */
see_digit = FALSE;
}

12-25 5788

#### 基于DTMF音调的数据通信（一）——android平台

©️2020 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

1.余额是钱包充值的虚拟货币，按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载，可以购买VIP、C币套餐、付费专栏及课程。