2014-11-08 4 views
1

Я пытаюсь воспроизвести необработанные буферы PCM16 с объектом AudioContext. Звук действительно играет, но очень насыщенно.Насыщенный звук при воспроизведении PCM16 с JavaScript

Проблема может возникнуть в результате преобразования беззнаковых 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 Chromium 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>

ОТВЕТ: выше для цикла преобразования PCM16 в PCM32 некорректен, так как это не правильно преобразовать двоичное дополнение.

ответ

0

Вы имеете выход FFmpeg подписали 16 бит данных, но тогда вы читаете это в JavaScript через Uint16Array типизированного массива, который без знака 16 бит. Это не даст вам значений -32k до + 32k в файле, смещенном на 0 до 64k; значения хранятся в two's complement, поэтому отрицательные значения будут отображаться как большие положительные значения. Прежде всего, вам нужно изменить pcm16Buffer на номер Int16Array.

Ваше преобразование с плавающей точкой может затем быть простой

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

О, ты прав, такая глупая ошибка. Большое спасибо! –

+0

Если значение положительное деление на 32767, отрицательное 32768, т.е. '... = pcm16Buffer [i] <0? pcm16Buffer [i]/32768: pcm16Buffer [i]/32767; ' – K3N

0

API 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

Спасибо за ваш ответ. Мои файлы совершенно правильные, так как я могу вернуться в WAV, используя «ffmpeg -y -f s16le -ar 44.1k -ac 1 -i output.raw file.wav». Ваш образец кода работает, но, к сожалению, я не могу использовать 'decodeAudioData()'. Вы когда-нибудь пробовали напрямую подделывать буферы так, как я? Спасибо за помощь. –

0

Вы пытаетесь конвертировать из неподписанных в подписанных, но ваш код все, что может изменить знак не делать. Во-первых, сдвиньте свой беззнаковый 16-битный (0-65536) в подписанный (-32767 по 32767) путем вычитания 32767. Затем разделите на 32767 на шкалу от -1,0 до 1,0. Кроме того, убедитесь, что ваше разделение является подразделением с плавающей запятой.

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

Я думаю, что ваше уравнение - это еще одно мое письмо. –

+0

Я не знаю деталей java-скрипта, но он уверен, что ваш делает целочисленную арифметику. – jaket

Смежные вопросы