我在Stackoverflow发表了相同的提问->点击跳转到StackOverFlow
java
最近在研究Java和Android声音信号生成的问题。在网上搜索了很长时间关于正弦波信号编码方式,基本上都是经过正弦函数android
y=sin(x)
来生成符合采样率的波形信号。代码大同小异,在Java上经过SourceDataLine来播放,在Android经过AudioTrack来播放。函数
Java:测试
this.aFormat = new AudioFormat( this.sample_Rate, //SAMPLE_RATE this.sample_Size_in_Bits, //SAMPLE SIZE IN BITS this.channels, //CHANNELS this.signed, //SIGNED this.bigEndian); //bigEndian SourceDataLine sourceDataLine = AudioSystem.getSourceDataLine(aFormat); sourceDataLine.open(aFormat); sourceDataLine.start(); byte[] buf = new byte[1]; for(int i=0;i<msecs*8;i++){ double angle = (2.0*Math.PI*i)/(sample_Rate/frequency); buf[0] = (byte)(Math.sin(angle)*127); sourceDataLine.write(buf, 0, 1); }
msecs是播放秒数,frequency是信号频率。this
经过SourceDataLine是能够每一个byte即时写入的。不用生成完整的buffer再一次写入sourceDataLine中。编码
Android:code
bufferSize = AudioTrack.getMinBufferSize( sampleRate, channels, encodingSize); audioTrack = new AudioTrack( managerType, sampleRate, channels, encodingSize, bufferSize, trackMode); audioTrack.setStereoVolume(AudioTrack.getMaxVolume(), AudioTrack.getMaxVolume()); audioTrack.play(); double phase = 0.0; int amp = 10000; short[] buffer = new short[bufferSize]; double phaseIncrement = (2 * Math.PI * frequency) / sampleRate; for (int i = 0; i < bufferSize; i++) { buffer[i] = (short) (amp * Math.sin(phase)); phase += phaseIncrement; } while(flag) { audioTrack.write(buffer, 0, buffer.length); }
flag是播放进行/中止的标记。orm
通过测试发现。audioTrack没办法像sourceDataLine那样一次写入一个byte的数据(我也不知道为啥,就是播不出来。求大神给解答一下)。因此我选择了一次生成1秒的数据(就是采样率数量的样本)而后屡次写入audioTrack。至于为啥生成一秒……由于生成多了慢,用户等待时间就长,生成少了不到一个采样周期,音频不完整。rem
可是问题来了,经过这些代码生成的正弦波信号是有问题的。get
不稳定。设定相同的频率,好比14 kHz,播放的频率是不稳定的,听起来就是有时候声调高有时候声调低。这个问题在Java和Android代码中都有出现过。并且有时候在播放开始后,听到的声音明显带有颤动。须要关闭程序再从新开始几回才能有一个稳定的播放结果。
噪音,噪音问题很是严重。这样播放出来的正弦波信号掺杂着不一样频率的噪声。有时甚至调到22 kHz(理论上人耳不可听到),还能听到很大的嗡嗡声。在示波器上显示的FFT图像,也能够明显的看到有很大的噪音干扰。不知道是Java的问题仍是代码的问题,个人师兄用Matlab编写的代码用相同的公式在同型号电脑(MBA)上彻底没有噪声。示波器显示的图像也明显更干净一些。
有没有哪位大神能帮我解释一下这个问题,或者帮我改改代码。谢谢你们。