2009-04-11 193 views
5

當我運行以下代碼時,背景中出現輕微的失真(聽起來像是嗡嗡聲)。由於其微妙的性質,它相信在字節轉換中會出現某種別名。在Java中生成正弦波時的背景噪音

的AudioFormat = PCM_SIGNED 44100.0赫茲,16位,立體聲,4個字節/幀,大端

:代碼假定(現在),該數據是在大端。

public static void playFreq(AudioFormat audioFormat, double frequency, SourceDataLine sourceDataLine) 
{ 
    System.out.println(audioFormat); 
    double sampleRate = audioFormat.getSampleRate(); 
    int sampleSizeInBytes = audioFormat.getSampleSizeInBits()/8; 
    int channels = audioFormat.getChannels(); 

    byte audioBuffer[] = new byte[(int)Math.pow(2.0, 19.0) * channels * sampleSizeInBytes]; 

    for (int i = 0; i < audioBuffer.length; i+=sampleSizeInBytes*channels) 
    { 
     int wave = (int) (127.0 * Math.sin(2.0 * Math.PI * frequency * i/(sampleRate * sampleSizeInBytes * channels)) ); 

     //wave = (wave > 0 ? 127 : -127); 

     if (channels == 1) 
     { 
      if (sampleSizeInBytes == 1) 
      { 
       audioBuffer[i] = (byte) (wave); 
      } 

      else if (sampleSizeInBytes == 2) 
      { 
       audioBuffer[i] = (byte) (wave); 
       audioBuffer[i+1] = (byte)(wave >>> 8); 
      } 
     } 

     else if (channels == 2) 
     { 
      if (sampleSizeInBytes == 1) 
      { 
       audioBuffer[i] = (byte) (wave); 
       audioBuffer[i+1] = (byte) (wave); 
      } 

      else if (sampleSizeInBytes == 2) 
      { 
       audioBuffer[i] = (byte) (wave); 
       audioBuffer[i+1] = (byte)(wave >>> 8); 

       audioBuffer[i+2] = (byte) (wave); 
       audioBuffer[i+3] = (byte)(wave >>> 8); 
      } 
     } 
    } 

    sourceDataLine.write(audioBuffer, 0, audioBuffer.length); 
} 

回答

7

你的評論說,該代碼假定爲big-endian。

技術上你實際上在小端輸出,但它似乎並沒有通過一個幸運的怪癖,因爲不管你最顯著字節始終爲0

編輯:於進一步解釋 - 當你的值最大值爲127,你應該寫(0x00,0x7f),但是你的代碼的實際輸出是(0x7f,0x00),它是32512.這恰好是16位的最大值的32767,但底部的8位全部爲零。最好始終使用32767作爲最大值,如果需要,則丟棄最低8位。

這意味着即使您要輸出16位數據,有效分辨率也只有8位。這似乎是由於缺乏音質。

我已經創建了一個只將原始數據轉儲到文件的代碼版本,並且無法看到任何錯誤的位移自身。沒有意外的符號或丟失位的變化,但與8位採樣質量一致。

此外,對於什麼是值得你的數學會比較容易,如果你基於樣本數計算的波動方程,然後再擔心字節偏移分別:

int samples = 2 << 19; 
byte audioBuffer[] = new byte[samples * channels * sampleSizeInBytes]; 

for (int i = 0, j = 0; i < samples; ++i) 
{ 
    int wave = (int)(32767.0 * Math.sin(2.0 * Math.PI * frequency * i/sampleRate)); 
    byte msb = (byte)(wave >>> 8); 
    byte lsb = (byte) wave; 

    for (int c = 0; c < channels; ++c) { 
     audioBuffer[j++] = msb; 
     if (sampleSizeInBytes > 1) { 
      audioBuffer[j++] = lsb; 
     } 
    } 
} 
2

我假設您反覆調用此代碼播放長音。

您正在生成的wave是否有可能在寫入之前完成一整段時間?

如果wave在完成整個週期之前得到「截止」,然後將下一個波寫入輸出,您肯定會聽到一些奇怪的東西,我認爲這可能是導致嗡嗡聲的原因。

例如:

 /-------\    /-------\    /-------\ 
    -----/   \  -----/   \  -----/   \ 
        \      \      \ 
        \-----     \-----     \----- 

通知這一波的部分之間的斷開。這可能會導致嗡嗡聲。

+0

是的,這不是問題,因爲緩衝區足以讓聲音通過很多循環。它確實說明了咔嗒聲,但不是持續的嗡嗡聲。 – yxk 2009-04-11 19:55:15

+0

這不是正確的答案 - 代碼清楚地生成大量樣本,並且在每個循環結束時都沒有截斷 – Alnitak 2009-04-11 20:16:13