简体   繁体   English

用javascript播放PCM

[英]Play PCM with javascript

I got some problems playing PCM Audio on the browser. 我在浏览器上播放PCM Audio时遇到了一些问题。 The PCM audio comes from an android device with udp-protocol and is saved on the server as *.raw PCM音频来自具有udp协议的Android设备,并以* .raw保存在服务器上

I unsuccessfully trying to play this saved file with the help of webaudioapi. 我在webaudioapi的帮助下试图播放这个保存的文件失败了。 Using following code, plays me some creepy sound with white noise: 使用以下代码,播放一些带有白噪声的令人毛骨悚然的声音:

var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
audioCtx.sampleRate = 16000;


// Stereo
var channels = 1;
// Create an empty two second stereo buffer at the
// sample rate of the AudioContext
var frameCount = audioCtx.sampleRate * 10.0;

var myAudioBuffer = audioCtx.createBuffer(channels, frameCount, audioCtx.sampleRate);


var req = new XMLHttpRequest();
req.open('GET', "example.raw", false);
req.overrideMimeType('text\/plain; charset=x-user-defined');
req.send(null);

function play(){
    for (var channel = 0; channel < channels; channel++) {

        var nowBuffering = myAudioBuffer.getChannelData(channel,16,16000);
        for (var i = 0; i < frameCount; i++) {
            // audio needs to be in [-1.0; 1.0]
            // for this reason I also tried to divide it by 32767
            // as my pcm sample is in 16-Bit. It plays still the
            // same creepy sound less noisy.
            nowBuffering[i] = (req.responseText.charCodeAt(i) & 0xff;

        }
    }
    // Get an AudioBufferSourceNode.
    // This is the AudioNode to use when we want to play an AudioBuffer
    var source = audioCtx.createBufferSource();
    // set the buffer in the AudioBufferSourceNode
    source.buffer = myAudioBuffer;
    // connect the AudioBufferSourceNode to the
    // destination so we can hear the sound
    source.connect(audioCtx.destination);
    // start the source playing
    source.start();
}

It's playing such an unidentifiable sound that I'm not sure if it's playing the pcm file which I supposed it has to do. 它正在播放如此无法识别的声音,我不确定它是否正在播放我认为必须要播放的pcm文件。

I'm supposing it has to do something with the pcm file. 我想它必须对pcm文件做一些事情。 The PCM file has 16 kHz sample rate, 16 bits per sample and only one channel or rather mono-channel. PCM文件的采样率为16 kHz,每个采样16位,只有一个通道或者更确切地说是单通道。

Anybody with the same problem here or did anybody have suggestions to fix my problem? 在这里遇到同样问题的人还是有人提出修复问题的建议?

I am looking since some days for a solution and appreciate any help. 我正在寻找一些解决方案,并感谢任何帮助。

First of all: 首先:

audioCtx.sampleRate = 16000; doesn't work. 不起作用。 You can't modify audioCtx.sampleRate. 您无法修改audioCtx.sampleRate。 Instead, you needed to do the following: 相反,您需要执行以下操作:

var frameCount = req.responseText.length / 2;
var myAudioBuffer = audioCtx.createBuffer(channels, frameCount, 16000);

Because your file is 16-bit, its length in bytes is twice the number of frames you need. 因为您的文件是16位,所以它的长度(以字节为单位)是您需要的帧数的两倍。

(req.responseText.charCodeAt(i) & 0xff) will yield a value between 0 and 255, representing a single 8-bit byte. (req.responseText.charCodeAt(i) & 0xff)将产生0到255之间的值,表示单个8位字节。 You need 16 bits. 你需要16位。

You need to know the byte order of your sample, and process two bytes each time 您需要知道样本的字节顺序,并且每次处理两个字节

For little endian (LSB first): 对于小端(LSB优先):

var word = (req.responseText.charCodeAt(i * 2) & 0xff) + ((req.responseText.charCodeAt(i * 2 + 1) & 0xff) << 8);

For big endian (MSB first): 对于大端(MSB优先):

var unsignedWord = ((req.responseText.charCodeAt(i * 2) & 0xff) << 8) + (req.responseText.charCodeAt(i * 2 + 1) & 0xff);

That will yield a number between 0 and 65535, representing an unsigned 16-bit integer. 这将产生0到65535之间的数字,表示无符号的16位整数。 In order to convert to signed integer, you need to do the following (replace X with the above code) 要转换为有符号整数,您需要执行以下操作(用上面的代码替换X)

var signedWord = (unsignedWord + 32768) % 65536 - 32768;

This will yield a number between -32768 and 32767, which you can then divide by 32768.0 in order to obtain your desired result. 这将产生介于-32768和32767之间的数字,然后您可以除以32768.0以获得所需的结果。

nowBuffering[i] = signedWord / 32768.0;

Edit: Working example https://o.lgm.cl/example.html (16-bit LSB) 编辑:工作示例https://o.lgm.cl/example.html(16位LSB)

@Locolois @Locolois

I tried your suggestion/solution and get some clear sounds, which unfortunately still didn't sound like the original. 我尝试了你的建议/解决方案并获得了一些清晰的声音,遗憾的是它听起来仍然不像原版。 It has also white noise every second, which was less creepy than my solution :D but I still didn't hear my voice which I recorded. 它每秒都有白噪声,这比我的解决方案更不令人毛骨悚然:D但我仍然没有听到我录制的声音。 I'm not sure if the pcm, which android.audiorecord exports has big or little endian, so I tried both ways. 我不确定pcm,哪个android.audiorecord导出有大或小端,所以我尝试了两种方式。 But the sound which I heard by using the suggestion you made for big endian, sounded more correct for me than the little endian version. 但是,通过使用你为big endian提出的建议,我听到的声音听起来比小端版更合适。 The little endian version was also fully with white noise. 小端版本也充满了白噪声。

Is it the right implementation, of your explanation?: 这是你的解释的正确实现吗?:

for (var i = 0; i < frameCount; i+=2) {     
    var msbFirst = (req.responseText.charCodeAt(i) & 0xff) + (req.responseText.charCodeAt(i + 1) & 0xff) << 8;
    var msbSigned = (msbFirst + 32768) % 65536 - 32768;
    nowBuffering[i] =  msbSigned / 65536.0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM