android的tone音正弦波快速产生算法源代码DTMF双音频和如何快速计算正弦波

假如我们需要产生取样频率为8KHz的440Hz的正弦波,那么a1=2*cos(2*pi*440/8000)=1.8817615,而y[1]=sin(2*pi*440/8000)=0.33873792。

来拆开解读一下:[luther.gliethttp-20110419]
{{{
1. 其中2*pi为一个正弦,2*pi*440表示1秒内会有440个正弦,
2. 2*pi*440/8000那就表示1秒内要作8000次采样,结果为每次采样的步进弧度值
}}}
也可以这样理解:
{{{
1. 2*pi为一个正弦
2. 440/8000为有效数据占用百分比
3. 2*pi*440/8000就表示一个正弦每个有效数据之间的弧度间隔值
4. 如果按照2*pi*440/8000为有效数据的步进大小,因为1秒会做8000次步进,
所以在1秒整时我们会刚刚好完成440个2*pi正弦数据输出.
}}}
// 下面是android的tone正弦波快速产生算法的源代码[luther.gliethttp]
////////////////////////////////////////////////////////////////////////////////
// WaveGenerator::WaveGenerator class Implementation
////////////////////////////////////////////////////////////////////////////////

//---------------------------------- public methods ----------------------------

////////////////////////////////////////////////////////////////////////////////
//
// Method: WaveGenerator::WaveGenerator()
//
// Description: Constructor.
//
// Input:
// samplingRate: Output sampling rate in Hz
// frequency: Frequency of the sine wave to generate in Hz
// volume: volume (0.0 to 1.0)
//
// Output:
// none
//
////////////////////////////////////////////////////////////////////////////////
ToneGenerator::WaveGenerator::WaveGenerator(unsigned short samplingRate,
unsigned short frequency, float volume) {
double d0;
double F_div_Fs; // frequency / samplingRate

F_div_Fs = frequency / (double)samplingRate;
d0 = - (float)GEN_AMP * sin(2 * M_PI * F_div_Fs);
mS2_0 = (short)d0;
mS1 = 0;
mS2 = mS2_0;

mAmplitude_Q15 = (short)(32767. * 32767. * volume / GEN_AMP);
// take some margin for amplitude fluctuation
if (mAmplitude_Q15 > 32500)
mAmplitude_Q15 = 32500;

d0 = 32768.0 * cos(2 * M_PI * F_div_Fs); // Q14*2*cos()
if (d0 > 32767)
d0 = 32767;
mA1_Q14 = (short) d0;

LOGV("WaveGenerator init, mA1_Q14: %d, mS2_0: %d, mAmplitude_Q15: %d\n",
mA1_Q14, mS2_0, mAmplitude_Q15);
}
////////////////////////////////////////////////////////////////////////////////
//
// Method: WaveGenerator::getSamples()
//
// Description: Generates count samples of a sine wave and accumulates
// result in outBuffer.
//
// Input:
// outBuffer: Output buffer where to accumulate samples.
// count: number of samples to produce.
// command: special action requested (see enum gen_command).
//
// Output:
// none
//
////////////////////////////////////////////////////////////////////////////////
void ToneGenerator::WaveGenerator::getSamples(short *outBuffer,
unsigned int count, unsigned int command) {
long lS1, lS2;
long lA1, lAmplitude;
long Sample; // current sample

// init local
if (command == WAVEGEN_START) {
lS1 = (long)0;
lS2 = (long)mS2_0;
} else {
lS1 = (long)mS1;
lS2 = (long)mS2;
}
lA1 = (long)mA1_Q14;
lAmplitude = (long)mAmplitude_Q15;

if (command == WAVEGEN_STOP) {
lAmplitude <<= 16;
if (count == 0) {
return;
}
long dec = lAmplitude/count;
// loop generation
while (count--) {
Sample = ((lA1 * lS1) >> S_Q14) - lS2;
// shift delay
lS2 = lS1;
lS1 = Sample;
Sample = ((lAmplitude>>16) * Sample) >> S_Q15;
*(outBuffer++) += (short)Sample; // put result in buffer
lAmplitude -= dec;
}
} else {
// loop generation
while (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
}
}

// save status
mS1 = (short)lS1;
mS2 = (short)lS2;
}


==========================================================================

转:http://hi.baidu.com/cui1206/blog/item/e3d1cd18d0fc11b54bedbc35.html

快速计算正弦波
在DSP运用中,经常需要产生正弦波。如果直接用c的数学函数sin,当然可以产生正弦波,但是由于sin函数本身的效率很低,产生正弦波所需要的 MIPS就会占去DSP处理能力的相当大的一部分。本章介用递推数列算正弦波的方法,先介绍原理,推导出递推公式,然后用浮点小数实现计算,再用定点小数进一步优化算法,最后进行误差分析,并提出更精确的定点小数算法。
先来看看如何推导出递推数列的公式。
我们所要产生的正弦波,其实是一系列的整数,把这些整数按照一定的取样频率发送给数模转换器,就可以变成真正的正弦波了。假设取样周期是Ts,产生的正弦波的圆频率为w,那么我们需要产生的数列就是:
sin(0), sin(w*Ts), sin(2*w*Ts), ... sin(n*w*Ts)
假设f(n)= sin(n*w*Ts),则问题就变成,从f(n-1), f(n-2), f(n-3),..., 如何计算f(n)了。解决了这个问题,也就找到了递推公式。
下面是这个递推公式的求解过程,假设x=w*Ts:
公式:sin( a + b) = sin(a)*cos(b) + cos(a)*sin(b)
sin( x + (n-1)x ) = sin(x)*cos( (n-1)x ) + cos(x)*sin( (n-1)x )
公式:Sin(a)*cos(b) = 1/2 * [ sin( a+b ) + sin( a-b ) ]
sin(x)*cos( (n-1)x ) = 1/2 * [ sin(nx) - sin( (n-2)x ) ]
sin(nx) = 1/2 * [ sin(nx) - sin( (n-2)x ) ] + cos(x)*sin( (n-1)x )
sin(nx) = 2*cos(x)*sin( (n-1)x ) - sin( (n-2)x )

我们看到这个递推公式是:
f(n)=2*cos(w*Ts)*f(n-1) - f(n-2)
也就是说只要知道最初始的两项f(0)和f(1),就可以计算出整个正弦波了。

根据上面的递推公式,很容易写出下面的正弦波计算程序。只要事先计算一次sin(w*Ts)和cos(w*Ts),以后的值就可以通过递推公式得到,所以计算一个值所需要的工作就是一次乘法,一次加法,两次变量复制而已了。
以下内容为程序代码:



float y[3] = {0, sin(w*Ts),0}; // y(n), y(n-1), y(n-2)
float a1=2*cos(w*Ts);
float a2=-1;
float singen(){
y[0]=a1*y[1]+a2*y[2];
y[2]=y[1];
y[1]=y[0];
return y[0];
}



假如我们需要产生取样频率为8KHz的440Hz的正弦波,那么a1=2*cos(2*pi*440/8000)=1.8817615,而y[1]=sin(2*pi*440/8000)=0.33873792。

现在看如何用定点小数来更快的计算正弦波。我们使用16bit也就是short型的整数来表示定点小数。首先需要决定的是小数的Q值,虽然我们最后计算的正弦波的值都是小于1的,但是在计算过程中需要用2*cos(w*Ts),而这个值最大为2,所以我们选择的Q值必须至少最大能表示2。这里我们选择 Q14,Q14的定点小数能表示-2到2的取值范围,对于本例的正弦波计算正好合适。1.8817615的Q14值是1.8817615*2^14= 5550=0x786F,同样0.33873792的Q14值为0x15AE。

下面就是完整的计算8KHz取样频率的400Hz的定点小数的正弦波的程序。
以下内容为程序代码:



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];
y[1]=y[0];
return y[0];
}


使用定点小数计算不但速度比浮点更快,而且计算得出来的值是整数,这个数值可以直接传递给DAC(数模转换器)转换为模拟的声音信号,如果使用浮点小数计算的话,还必须把浮点数转换为整数才能传递给DAC。

使用定点小数计算必须仔细分析误差,下面来看看我们产生的正弦波的误差是多少。定点小数计算中的误差就是由定点小数表达精度决定的。在上面的例子中我们用 0x786F表示1.8817615,这存在一定的误差,把Q14的0x786F再转换为浮点数就是0x786F/2^14=1.8817749,我们可以看到相对误差非常小,也就是说最终得到的正弦波在频率上的误差也是非常小的。

但是,定点小数并不是什么时候都这么精确。例如如果用CD 音质的取样频率44100Hz来产生100Hz的正弦波,那么a1=2*cos(2*pi*440/44100)= 1.9960713,这个数转换为16比特的Q14的值是0x7fc0。我们可以看到这时定点小数已经十分接近0x7fff了,最终产生的正弦波的频率也会有很大的误差。为了能够精确地计算这样的正弦波,必须使用32bit的Q30定点小数。关于32bit定点小数的计算方法将在别的章节介绍。

另外上面的singen函数每调用一次只产生一个值,如果要产生实时的正弦波的话,函数的调用频率和取样频率相同,DSP的负担相对比较大。一般DSP计算都采取块计算方式,一次计算n个(例如64)个取样值,这样不但减少了函数的调用负担,也可以减少中间的内存移动的次数(y[2]=y[1]; y[1]= y[0];)。

更多相关文章

  1. EditText保留两位小数并输入不超过最大值
  2. 基于Android的计算器(代码行数足够用来交大作业)
  3. Android培训班(29)
  4. android中editText保留小数点后两位
  5. Android使double保留两位小数的多方法 java保留两位小数
  6. 安卓简单计算器整理
  7. Android实现EditText输入金额
  8. android edittext 设置只允许输入整数,(设置输入类型)
  9. Android(安卓)播放指定频率正弦波声音

随机推荐

  1. Android图片剪裁的实现
  2. android 多行文本显示的textView
  3. 一个android 异步多线程类介绍
  4. Performance Tips for Android’s ListVi
  5. android 数组数据绑定到listview
  6. Android.mk添加第三方jar包
  7. Android(安卓)Saving Data
  8. Android(安卓)ExpandableListView分组效
  9. android 可以拖动的圆形 进度条
  10. Android(安卓)Auto-IP support