2014-11-08 33 views
1

我正在嘗試使用AudioContext對象播放原始PCM16緩衝區。聲音的確在播放,但是以非常飽和的方式播放。使用JavaScript播放PCM16時的飽和聲音

該問題可能來自將無符號16位整數轉換爲[-1.0; 1.0]漂浮,但我沒有看到我在做什麼的問題。

我在下面做了一個最小的html示例以方便複製。該output.raw文件可以從MP3文件用下面的命令來獲得:

MP3FILE=myfile.mp3 
ffmpeg -i $MP3FILE -f s16le -ac 1 -acodec pcm_s16le output.raw 

我使用Linux鉻Version 38.0.2125.111 (290379) (64-bit)

感謝您的幫助!

編輯:直接加載PCM32(s32le)樣品表現出同樣的問題。

<html> 
 

 
<head> 
 
</head> 
 

 
<body> 
 
<script> 
 
var oReq = new XMLHttpRequest(); 
 
oReq.open("GET", "output.raw", true); 
 
oReq.responseType = "arraybuffer"; 
 
oReq.onload = function (oEvent) { 
 
    var context = new window.AudioContext(); 
 

 
    var pcm16Buffer = new Uint16Array(oReq.response); 
 
    var frameCount = pcm16Buffer.length; 
 
    var channels = 1; 
 
    var buffer = context.createBuffer(channels, frameCount, context.sampleRate); 
 

 
    for (var channel = 0; channel < channels; ++channel) { 
 
    var channelBuffer = buffer.getChannelData(channel); 
 
    for (var i = 0; i < frameCount; ++i) { 
 
     channelBuffer[i] = pcm16Buffer[i] * 2/65535 - 1; 
 
    } 
 
    } 
 

 
    var source = context.createBufferSource(); 
 
    source.buffer = buffer; 
 
    source.connect(context.destination); 
 
    source.start(); 
 
}; 
 

 
function run() { 
 
    oReq.send(); 
 
} 
 
</script> 
 
<a href="javascript:run()">run</a> 
 
</body> 
 

 
</html>

答: 以上的for循環轉換PCM16到PCM32是不正確,因爲它不正確地轉換二進制補碼。

回答

0

你有ffmpeg的輸出簽署了16位的數據,但你通過​​類型數組,這是16位無符號閱讀它的JavaScript。這不會給你的文件中的-32k到+ 32k值移到0到64k;值存儲在two's complement中,因此負值將顯示爲較大的正值。主要是您需要做的是將pcm16Buffer更改爲Int16Array

你轉換成浮點數就可以是一個簡單的

channelBuffer[i] = pcm16Buffer[i]/32768; 
+0

哦,你是對的,這樣一個愚蠢的錯誤。非常感謝你! – 2014-11-08 19:15:02

+0

如果一個值是正的,則在32767上除,否則爲32768,即。 '... = pcm16Buffer [i] <0? pcm16Buffer [i]/32768:pcm16Buffer [i]/32767;' – K3N 2015-05-14 13:27:32

0

Web Audio API只能渲染PCM緩衝區,所以無論音頻來自何處,進程必須最終提供該格式。首先,使用Audacity等其他工具驗證您的輸入文件是否正常播放。下面給出一個輸入WAV文件,從中提取原始PCM緩衝區。

<html> 

<head> 
</head> 

<body> 
    <script> 

     var audio_context, gain_node, source_node; 
     var chosen_audio_file_url = "output_2.wav"; 

     // log if an error occurs 
     function on_error(e) { 

      console.log("ERROR - " + e); 
     } 

     try { 

      window.AudioContext = window.AudioContext || 
      window.webkitAudioContext || 
      window.mozAudioContext || 
      window.oAudioContext || 
      window.msAudioContext; 

      audio_context = new AudioContext(); 
      console.log("cool audio context established"); 

     } catch (e) { 

      alert("Web Audio API is not supported by this browser"); 
     } 


     gain_node = audio_context.createGain(); // Declare gain node 
     gain_node.connect(audio_context.destination); // Connect gain node to speakers 

     source_node = audio_context.createBufferSource(); 
     source_node.connect(gain_node); 

     var request = new XMLHttpRequest(); 
     request.open('GET', chosen_audio_file_url, true); 
     request.responseType = 'arraybuffer'; 

     // When loaded decode the data 
     request.onload = function() { 

      // decode the data 
      audio_context.decodeAudioData(request.response, function(buffer) { 

       console.log(chosen_audio_file_url, ' ... buffer.length ', buffer.length); 

       source_node.buffer = buffer; 
       source_node.start(0); 

       // --- 

      }, on_error); 
     } 

     function run() { 
      request.send(); 
     } 

    </script> 

    <a href="javascript:run()">run</a> 
</body> 

</html> 

讓我們知道,如果你需要更多的東西

+0

謝謝您的回答。我的文件完全正確,因爲我可以使用「ffmpeg -y -f s16le -ar 44.1k -ac 1 -i output.raw file.wav」返回到WAV。 您的代碼示例確實有效,但不幸的是我無法使用'decodeAudioData()'。你有沒有嘗試過直接鍛造緩衝液的方式? 感謝您的幫助。 – 2014-11-08 19:06:42

0

你試圖轉換從簽名到簽名,但你的代碼沒有做任何事情可能會改變的跡象。首先,通過減去32767,將無符號16位(0-65536)轉換爲帶符號(-32767至32767)。然後除以32767以縮放至-1.0至1.0。另外,確保你的部門是一個浮點部門。

channelBuffer[i] = (pcm16Buffer[i]-32767)/32767.0; 
+0

我認爲你的等式只是我的另一個寫法。 – 2014-11-08 19:05:12

+0

我不知道java腳本的細節,但它確實看起來像你的整數算術。 – jaket 2014-11-09 02:18:28