PureToneGenerator

一连搞了几天的PureToneGenerator,先前也是一直以为这个东西很简单,不就是AudioTrack,利用write函数写个buffer就成了吗?其实也不是那么简单。

首先说一下利用AudioTrack来产生pure sin wave的流程:

(1)初始化一个AudioTrack的实例,利用代码去获得一个最小的buffer size,如下:

                int bufferSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_MONO,
                        AudioFormat.ENCODING_PCM_16BIT);
                audioTrack =
                        new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, AudioFormat.CHANNEL_OUT_MONO,
                                AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM);

上面的第一句话是获取最小buffer的函数,第一个sampleRate最高(当前2016年)为48000Hz,几年前还只有44100的,所以老一点的手机注意一下这个值。这个sampleRate的最大值也可以用下面这句话得到:

int rate = AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC);

第二个参数AudioFormat.CHANNEL_OUT_MONO是指输出通道为单声道,另外的可配置项为Stero表示双声道。第三个AudioFormat.ENCODING_PCM_16BIT是后面的要用到的write函数的PCM流的编码格式,这里还可以设置为short或者float 型,具体的更多的细节可以看google上的说明:点击打开链接


第二个函数是用来得到一个AudioTrack的实例,可以用audioTrack.getState()来得到初始化的状态,建议最好是做一下检查,不然出错了都不知道。

(2)接下来就可以play(),然后write()输出码流了。这两个的顺序在stream mode下,play在前,write在后,如果是staic mode,换个顺序要好点。


输出的是pure tone时,只有一个频率。利用下面的代码产生一个PCM raw码流:

mPCMData[i] = (short)(32768 * Math.sin(2.0*Math.PI * (i) * F0/rate));

这个F0表示要产生的频率,rate表示是采样频率。乘以32768是将信号放大,PCM码流的值是short型,32768是short型的最大值。


上面说的是基本步骤,刚开始我利用上面的步骤产生了8K以下的正弦波信号,用FFT去做频谱分析,主频率的峰值确实是最大的,但是问题是有harmonics,还有aliasing产生的harmonics。后来看了许多相关的资料:点击打开链接,以及在阅读论文中得到了一些建议,发现真有可能是由于没有对AudioTrack进行warm up 操作造成的。这个warm up 在android source code 网页里面是有说明的。然后看到了这个网页上(点击打开链接)的作者也说明了对amplitude进行缓慢拉升,可以除掉一些noise。后来就把ZenTone点击打开链接的代码下下来跑了一下。确实没什么谐波。

于是乎我就把作者产生PCM码流的代码改了一下,因为我要持续的获得PCM码流。下面的是作者原始的代码:

      for (i = 0; i < ramp; ++i) {  // Ramp amplitude up (to avoid clicks)
        // Ramp up to maximum
        final short val = (short) (sample[i] * 32767 * i / ramp);
        // in 16 bit wav PCM, first byte is the low order byte
        generatedSnd[idx++] = (byte) (val & 0x00ff);
        generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
      }

      for (i = ramp; i < numSamples - ramp;
          ++i) {                        // Max amplitude for most of the samples
        // scale to maximum amplitude
        final short val = (short) (sample[i] * 32767);
        // in 16 bit wav PCM, first byte is the low order byte
        generatedSnd[idx++] = (byte) (val & 0x00ff);
        generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
      }

      for (i = numSamples - ramp; i < numSamples; ++i) { // Ramp amplitude down
        // Ramp down to zero
        final short val = (short) (sample[i] * 32767 * (numSamples - i) / ramp);
        // in 16 bit wav PCM, first byte is the low order byte
        generatedSnd[idx++] = (byte) (val & 0x00ff);
        generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
      }

它的逻辑是开始和结尾的amplitude被缩小。开头的被保留,后面的缩小我就去掉了。
audioTrack.write(generatedSnd, 0, generatedSnd.length);    // Load the track
于是我就紧跟着上面的代码写了个

audioTrack.write(generatedSnd, ramp, generatedSnd.length);    // Load the track
然后就是第二个write进去的数据死活听不到。我又去反编译了二个程序,看了下别人的源码,源码连amplitude的渐变都没有,依然声音产生的perfect。没办法,去看看api还有哪些辅助函数吧。看api时发现,原来write函数是有返回值的,终于爽到了!!!

int	zero or the positive number of bytes that were written, or one of the following error codes. The number of bytes will be a multiple of the frame size in bytes not to exceed sizeInBytes.
ERROR_INVALID_OPERATION if the track isn't properly initialized
ERROR_BAD_VALUE if the parameters don't resolve to valid data and indexes
ERROR_DEAD_OBJECT if the AudioTrack is not valid anymore and needs to be recreated. The dead object error code is not returned if some data was successfully transferred. In this case, the error is returned at the next write()
ERROR in case of other error
This is equivalent to write(byte[], int, int, int) with writeMode set to WRITE_BLOCKING.
接着就发现了第二个write产生了ERROR_BAD_VALUE,当把第二个write函数中的offset置0时,终于听到声音了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值