javascript 如何使用 HTML5 音频 API 播放从 XMLHTTPRequest 返回的音频

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30330856/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 12:03:27  来源:igfitidea点击:

How do I play audio returned from an XMLHTTPRequest using the HTML5 Audio API

javascriptnode.jshtml5-audiotext-to-speechibm-watson

提问by exiquio

I'm failing to be able to play audio when making an "AJAX" request to my server side api.

向我的服务器端 api 发出“AJAX”请求时,我无法播放音频。

I have backend Node.js code that's using IBM's Watson Text-to-Speech service to serve audio from text:

我有后端 Node.js 代码,它使用 IBM 的 Watson Text-to-Speech 服务来提供来自文本的音频:

var render = function(request, response) {
    var options = {
        text: request.params.text,
        voice: 'VoiceEnUsMichael',
        accept: 'audio/ogg; codecs=opus'
    };

    synthesizeAndRender(options, request, response);
};

var synthesizeAndRender = function(options, request, response) {
    var synthesizedSpeech = textToSpeech.synthesize(options);

    synthesizedSpeech.on('response', function(eventResponse) {
        if(request.params.text.download) {
            var contentDisposition = 'attachment; filename=transcript.ogg';

            eventResponse.headers['content-disposition'] = contentDisposition;
        }
    });

    synthesizedSpeech.pipe(response);
};

I have client side code to handle that:

我有客户端代码来处理:

var xhr = new XMLHttpRequest(),
    audioContext = new AudioContext(),
    source = audioContext.createBufferSource();

module.controllers.TextToSpeechController = {
    fetch: function() {
        xhr.onload = function() {
            var playAudio = function(buffer) {
                source.buffer = buffer;
                source.connect(audioContext.destination);

                source.start(0);
            };

            // TODO: Handle properly (exiquio)
            // NOTE: error is being received
            var handleError = function(error) {
                console.log('An audio decoding error occurred');
            }

            audioContext
                .decodeAudioData(xhr.response, playAudio, handleError);
        };
        xhr.onerror = function() { console.log('An error occurred'); };

        var urlBase = 'http://localhost:3001/api/v1/text_to_speech/';
        var url = [
            urlBase,
            'test',
        ].join('');

        xhr.open('GET', encodeURI(url), true);
        xhr.setRequestHeader('x-access-token', Application.token);
        xhr.responseType = 'arraybuffer';
        xhr.send();
    }
}

The backend returns the audio that I expect, but my success method, playAudio, is never called. Instead, handleError is always called and the error object is always null.

后端返回我期望的音频,但我的成功方法 playAudio 从未被调用。相反,handleError 总是被调用并且错误对象总是为空。

Could anyone explain what I'm doing wrong and how to correct this? It would be greatly appreciated.

谁能解释我做错了什么以及如何纠正这个问题?这将不胜感激。

Thanks.

谢谢。

NOTE: The string "test" in the URL becomes a text param on the backend and and ends up in the options variable in synthesizeAndRender.

注意:URL 中的字符串“test”成为后端的文本参数,并最终出现在 synthesizeAndRender 中的选项变量中。

回答by Eric S. Bullington

Unfortunately, unlike Chrome's HTML5 Audio implementation, Chrome's Web Audio doesn't support audio/ogg;codecs=opus, which is what your request uses here. You need to set the format to audio/wavfor this to work. To be sure it's passed through to the server request, I suggest putting it in the query string (accept=audio/wav, urlencoded).

不幸的是,与 Chrome 的 HTML5 音频实现不同,Chrome 的网络音频不支持 audio/ogg;codecs=opus,这是您的请求在这里使用的。您需要将格式设置audio/wav为使其工作。为确保它传递到服务器请求,我建议将它放在查询字符串 ( accept=audio/wav, urlencoded) 中。

Are you just looking to play the audio, or do you need access to the Web Audio API for audio transformation? If you just need to play the audio, I can show you how to easily play this with the HTML5 Audio API (not the Web Audio one). And with HTML5 Audio, you can stream it using the technique below, andyou can use the optimal audio/ogg;codecs=opusformat.

您只是想播放音频,还是需要访问 Web Audio API 进行音频转换?如果您只需要播放音频,我可以向您展示如何使用 HTML5 Audio API(而不是 Web Audio API)轻松播放。借助 HTML5 音频,您可以使用以下技术进行流式传输,并且可以使用最佳audio/ogg;codecs=opus格式。

It's as simple as dynamically setting the source of your audio element, queried from the DOM via something like this:

它就像动态设置音频元素的来源一样简单,通过以下方式从 DOM 中查询:

(in HTML)

(在 HTML 中)

<audio id="myAudioElement" />

(in your JS)

(在你的 JS 中)

var audio = document.getElementById('myAudioElement') || new Audio();
audio.src = yourUrl;

Your can also set the audio element's source via an XMLHttpRequest, but you won't get the streaming. But since you can use a POST method, you're not limited to the text length of a GET request (for this API, ~6KB). To set it in xhr, you create a data uri from a blob response:

您还可以通过 XMLHttpRequest 设置音频元素的源,但您不会获得流式传输。但由于您可以使用 POST 方法,因此您不受 GET 请求的文本长度的限制(对于此 API,约 6KB)。要在 xhr 中设置它,您需要从 blob 响应创建一个数据 uri:

    xhr.open('POST', encodeURI(url), true);
    xhr.setRequestHeader('Content-Type', 'application/json');
    xhr.responseType = 'blob';
    xhr.onload = function(evt) {
      var blob = new Blob([xhr.response], {type: 'audio/ogg'});
      var objectUrl = URL.createObjectURL(blob);
      audio.src = objectUrl;
      // Release resource when it's loaded
      audio.onload = function(evt) {
        URL.revokeObjectURL(objectUrl);
      };
      audio.play();
    };
    var data = JSON.stringify({text: yourTextToSynthesize});
    xhr.send(data);

As you can see, with XMLHttpRequest, you have to wait until the data are fully loaded to play. There maybe a way to stream from XMLHttpRequest using the very new Media Source Extensions API, which is currently available only in Chrome and IE (no Firefox or Safari). This is an approach I'm currently experimenting with. I'll update here if I'm successful.

如您所见,使用 XMLHttpRequest,您必须等到数据完全加载才能播放。有可能是一种方式,从XMLHttpRequest来流使用非常新媒体来源扩展API,这是目前仅适用于Chrome和IE(无火狐或Safari)可用。这是我目前正在试验的一种方法。如果我成功了,我会在这里更新。