如何通过 javascript/html5 播放 wav 音频字节数组?

IT技术 javascript html gwt wav audiocontext
2021-03-03 09:04:09

我正在使用以下方法播放包含 wav 数据的字节数组。正在从 GWT 项目调用该函数。

此功能播放声音,但听起来像是某种地狱怪物。采样率绝对正确(声音是由 Neospeech 生成的),我已经尝试了 numberOfSamples 的各种值,这似乎只是代表音频数据的长度。

numberOfSamples 的值大于 30000 将播放音频文件的全长,但它是乱码和可怕的。

那么,我做错了什么?

function playByteArray(byteArray, numberOfSamples) {
    sampleRate = 8000;

    if (!window.AudioContext) {
        if (!window.webkitAudioContext) {
            alert("Your browser does not support any AudioContext and cannot play back this audio.");
            return;
        }
        window.AudioContext = window.webkitAudioContext;
    }

    var audioContext = new AudioContext();

    var buffer = audioContext.createBuffer(1, numberOfSamples, sampleRate);
    var buf = buffer.getChannelData(0);
    for (i = 0; i < byteArray.length; ++i) {
        buf[i] = byteArray[i];
    }

    var source = audioContext.createBufferSource();
    source.buffer = buffer;
    source.connect(audioContext.destination);
    source.start(0);
}
3个回答

我想出了如何做我在问题中描述的事情,并认为我应该发布它以造福他人。代码如下。我调用 playByteArray 并将包含 pcm wav 数据的字节数组传递给它。

window.onload = init;
var context;    // Audio context
var buf;        // Audio buffer

function init() {
if (!window.AudioContext) {
    if (!window.webkitAudioContext) {
        alert("Your browser does not support any AudioContext and cannot play back this audio.");
        return;
    }
        window.AudioContext = window.webkitAudioContext;
    }

    context = new AudioContext();
}

function playByteArray(byteArray) {

    var arrayBuffer = new ArrayBuffer(byteArray.length);
    var bufferView = new Uint8Array(arrayBuffer);
    for (i = 0; i < byteArray.length; i++) {
      bufferView[i] = byteArray[i];
    }

    context.decodeAudioData(arrayBuffer, function(buffer) {
        buf = buffer;
        play();
    });
}

// Play the loaded file
function play() {
    // Create a source node from the buffer
    var source = context.createBufferSource();
    source.buffer = buf;
    // Connect to the final output node (the speakers)
    source.connect(context.destination);
    // Play immediately
    source.start(0);
}
我是否遗漏了什么,或者for循环中将每个单独的字节分配给函数bufferView内部是否没有意义playByteArray
2021-04-27 09:04:09
@user7290573 是的,我也有同样的问题,不确定作者介意解释吗?
2021-04-27 09:04:09
很棒的东西,我一直在寻找像这样一个简洁明了的例子
2021-05-09 09:04:09

清理建议:

function playByteArray( bytes ) {
    var buffer = new Uint8Array( bytes.length );
    buffer.set( new Uint8Array(bytes), 0 );

    context.decodeAudioData(buffer.buffer, play);
}

function play( audioBuffer ) {
    var source = context.createBufferSource();
    source.buffer = audioBuffer;
    source.connect( context.destination );
    source.start(0);
}

与 AudioContext 相比,这种方法适用于最新的 iOS Safari。音频对象应该在点击/点击(用户交互)事件上创建,所以不要在请求回调中这样做。

const audio = new Audio()    
fetch(url, options) // set content header to array buffer
      .then((response) => {
        var blob = new Blob([response.value], { type: 'audio/mp3' })
        var url = window.URL.createObjectURL(blob)        
        audio.src = url
        audio.play()
      })

来自这里的片段