原生JavaScript中Web Audio API音频对象无法播放的问题排查
问题:Web Audio API加载音频点击无声音,直接用audio标签可正常播放
我正在学习Web Audio API,项目目录中有一个Recording.m4a文件,尝试将其加载到Audio对象中,通过按钮点击触发播放。JavaScript代码如下:
var ctx = new AudioContext(); let audioFileObj = new Audio("Recording.m4a"); let recordingFileStream = ctx.createMediaElementSource(audioFileObj); //configuring the output source of the audio stream recordingFileStream.connect(ctx.destination); let button = document.querySelector(".playbutton"); button.addEventListener("click", function(){ if (ctx.state === 'suspended') { ctx.resume().then(() => { audioFileObj.play(); console.log("if triggered"); }); } else { audioFileObj.play(); console.log("else triggered"); } });
控制台日志显示逻辑正常执行,首次点击触发if分支,后续点击触发else分支,且无报错,但点击按钮没有声音。而直接在HTML中嵌入audio元素并使用控件却能正常播放该录音,HTML代码如下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title></title> <meta name="description" content=""> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="styles.css"> </head> <body> <button class="playbutton">PRESS</button> <audio controls> <source src="Recording.m4a" > </audio> <script src="script.js" async defer></script> </body> </html>
请问问题出在哪里?
解答
问题核心在于:你通过JS创建的Audio对象是未挂载到DOM的游离元素,浏览器的资源加载策略会限制这类元素的媒体资源加载,导致音频并未真正完成加载,所以调用play()也没有声音输出。而页面中直接写的<audio>标签是关联DOM的,浏览器会正常加载其资源,因此能正常播放。
给你两种可行的解决方法:
方法一:将JS创建的Audio对象挂载到DOM(可隐藏)
把创建的Audio元素添加到页面DOM中,哪怕设置为隐藏,浏览器就会正常加载它的音频资源:
var ctx = new AudioContext(); let audioFileObj = new Audio("Recording.m4a"); // 挂载到DOM,可隐藏 document.body.appendChild(audioFileObj); audioFileObj.style.display = 'none'; let recordingFileStream = ctx.createMediaElementSource(audioFileObj); recordingFileStream.connect(ctx.destination); let button = document.querySelector(".playbutton"); button.addEventListener("click", function(){ if (ctx.state === 'suspended') { ctx.resume().then(() => { audioFileObj.play(); }); } else { audioFileObj.play(); } });
方法二:改用AudioBufferSourceNode加载音频
直接通过Web Audio API的AudioBufferSourceNode加载并解码音频文件,这种方式不需要依赖DOM元素,更符合Web Audio API的原生使用场景:
var ctx = new AudioContext(); let button = document.querySelector(".playbutton"); let audioBuffer = null; // 提前加载并解码音频数据 fetch('Recording.m4a') .then(res => res.arrayBuffer()) .then(buf => ctx.decodeAudioData(buf)) .then(decodedData => { audioBuffer = decodedData; }); button.addEventListener("click", function(){ if (ctx.state === 'suspended') { ctx.resume().then(playAudio); } else { playAudio(); } }); function playAudio() { if (!audioBuffer) return; // 创建Buffer源节点 const source = ctx.createBufferSource(); source.buffer = audioBuffer; source.connect(ctx.destination); source.start(); }
内容的提问来源于stack exchange,提问作者Abb
相关产品推荐
相关产品推荐

