我在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)上徹底沒有噪聲。示波器顯示的圖像也明顯更乾淨一些。
有沒有哪位大神能幫我解釋一下這個問題,或者幫我改改代碼。謝謝你們。