我正在構建一個相當簡單的Android應用程序(sdk revision 14:ICS),它允許用戶一次選擇兩個音頻剪輯(全部爲RIFF/WAV格式,很少-endian,簽名的PCM-16位編碼)並以各種方式組合它們以創建新的聲音。我使用該組合中的最基本的方法是如下:使用Android的AudioTrack合併聲音樣本字節會產生噪音
....
hMain.setBigData(hMain.getAudioTransmutation().getBigData()); //set the shared bigData
// to the bigData in AudioTransmutation object
hMain.getAudioProc().playWavFromByteArray(hMain.getBigData(), 22050 + (22050*
(freqSeekSB.getProgress()/100)), 1024); //a SeekBar allows the user to adjust the freq
//ranging from 22050 hz to 44100 hz
....
public void playWavFromByteArray(byte[] audio,int sampleRate, int bufferSize){
int minBufferSize = AudioTrack.getMinBufferSize(sampleRate,
AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);
AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,
minBufferSize, AudioTrack.MODE_STREAM);
int i = 0;
at.play();
at.write(audio, 0, audio.length);
at.stop();
at.release();
for(i=0;i<audio.length;i++){
Log.d("me","the byte value at audio index " + i + " is " + audio[i]);
}
}
的組合和重放的結果:
//...sound samples are read in to memory as raw byte arrays elsewhere
//...offset is currently set to 45 so as to skip the 44 byte header of basic
//RIFF/WAV files
...
//Actual combination method
public byte[] makeChimeraAll(int offset){
for(int i=offset;i<bigData.length;i++){
if(i < littleData.length){
bigData[i] = (byte) (bigData[i] + littleData[i]);
}
else{
//leave bigData alone
}
}
return bigData;
}
返回的字節陣列可接着經由AudioTrack類正是如此被播放使用上面的代碼接近我想要的(這兩個樣本在所產生的混合聲音中仍然是可辨別的),但也存在很多裂縫,爆裂聲和其他噪聲。
所以,三個問題:首先,我正確使用AudioTrack?其次,AudioTrack配置中的排序在哪裏?這些聲音本身播放得很好,聽起來幾乎就像我所期望的那樣,因此RIFF/WAV格式的小端特性似乎在某處傳達,但我不確定它在哪裏。最後,對於有符號的16位PCM編碼,我應該看到什麼字節值範圍?我期望在上面的Log.d(...)調用中看到logcat中-32768到32767之間的值,但是結果往往在-100到100的範圍內(除了一些異常值外)。也許,組合字節值超出16位範圍可能會造成噪聲?
感謝, CCJ
UPDATE:主要得益於比約內羅氏公司和威廉Coderer!我現在讀音頻數據爲short []結構,DataInputStream的字節序使用來自William的EndianInputStream(http://stackoverflow.com/questions/8028094/java-datainputstream-replacement-for-endianness)和組合方式已更改爲:
//Audio Chimera methods!
public short[] makeChimeraAll(int offset){
//bigData and littleData are each short arrays, populated elsewhere
int intBucket = 0;
for(int i=offset;i<bigData.length;i++){
if(i < littleData.length){
intBucket = bigData[i] + littleData[i];
if(intBucket > SIGNED_SHORT_MAX){
intBucket = SIGNED_SHORT_MAX;
}
else if (intBucket < SIGNED_SHORT_MIN){
intBucket = SIGNED_SHORT_MIN;
}
bigData[i] = (short) intBucket;
}
else{
//leave bigData alone
}
}
return bigData;
}
混合音頻輸出質量與這些改進是真棒!
哎呦......感謝您指出需要使用短數組而不是字節數組;它實際上是16位的PCM,所以我不知道爲什麼我認爲逐字節的存儲和處理工作。也許是因爲我無法真正找到脈衝編碼調製在內部工作的良好解釋......您是否有任何關於瞭解數字音頻編碼/處理的低級細節的建議參考? – CCJ
另外值得注意的是,由於我的RIFF/WAV音頻文件是使用little-endian字節排序進行編碼的,因此我需要使用DataInputStream的修改版本來正確讀取短值(股票java版本假設爲大端)。幸運的是,我發現了一個很好的實現必要的按位操作來完成此任務在這裏:http://stackoverflow.com/questions/8028094/java-datainputstream-replacement-for-endianness – CCJ
是的,你需要處理字節序,作爲好。你可能會考慮一個類似於RandomAccessFIle的包裝器。對於參考資料,你可以從這裏開始:http://blog.bjornroche.com/2011/11/slides-from-fundamentals-of-audio.html還有一本書叫數字音頻與Java它現在已經過時,並有一些不準確的地方,但它有工作代碼,這是你不會在很多地方找到的東西。在我的第一個鏈接更多的參考。 –