音频无声最常因文件未加载或解码失败:404需修正路径,MIME类型错误需服务器配置;编码不兼容(如Safari拒fltp)需用ffprobe验证;应双源fallback、起本地服务、绑定用户手势调play()、iOS需touchstart同步执行并设muted;还需检查音量、显示状态、CORS及AudioContext挂起。

audio.src路径404或格式不被支持
没声音最常是因为根本没加载到音频文件,或者浏览器加载了但解不了码。Network 面板里看请求状态:如果是 404,说明路径错了;如果返回了但 Content-Type 是 text/plain 或空着,服务器没配对 MIME 类型——Apache 要加 AddType audio/mpeg .mp3,Nginx 要在 types 块里补 audio/mpeg mp3。
别信文件后缀,用 ffprobe your-file.mp3 查真实编码:Safari 会拒掉 fltp(浮点)编码的 MP3,Firefox 对某些 AAC-LC 以外的 MP3 也静默失败。稳妥做法是用 <source src="a.mp3" type="audio/mpeg"> + <source src="a.ogg" type="audio/ogg"> 双源 fallback,顺序不能错。
本地双击打开 HTML(file:// 协议)时,Chrome 直接禁音频解码,必须起服务:python3 -m http.server 8000 再访问 http://localhost:8000。
play() 报 NotAllowedError 或静默失败
这是现代浏览器(Chrome、Edge、Safari、Firefox)的默认策略:没用户手势就调 play(),99% 失败。错误信息通常是 DOMException: play() failed because the user didn't interact with the document first,或者控制台没报错但 audio.paused 仍是 true,readyState 卡在 0(HAVE_NOTHING)。
立即学习“前端免费学习笔记(深入)”;
不能在 DOMContentLoaded、window.onload 或 setTimeout 里调 play()——这些都不算用户交互。
可靠写法是绑定一次点击或触摸:
document.body.addEventListener('click', () => {
audio.play().catch(e => console.warn('play failed:', e));
}, { once: true });
iOS Safari 更严格:必须在 touchstart 回调里同步调 play(),不能包在 Promise.then() 或异步逻辑里;微信 iOS 还得等 WeixinJSBridgeReady 事件。
muted 属性和父容器静音状态被忽略
muted 不是“没声音”的 bug,而是解锁自动播放的钥匙。加了 muted 的 <audio> 才可能静音自动播,之后再 audio.muted = false + audio.volume = 1 才能出声——尤其在 iOS 上,这是必要步骤。
别漏查基础状态:
-
audio.volume是否被代码设成0且没恢复 - 浏览器标签页右上角有没有扬声器图标显示
muted - 父容器是否设置了
display: none或visibility: hidden——部分浏览器会停掉其音频上下文 - 系统音量是否为 0,或设备本身静音
另外,<audio> 必须加 controls 属性才能看到控件、手动测试是否真能播;只写 src 会导致静默加载,用户毫无感知。
CORS 或 AudioContext 挂起导致无声
跨域音频要双向配合:HTML 侧得加 crossorigin 属性,服务端除了 Access-Control-Allow-Origin: *,还得允许 Range 请求头(音频流式加载依赖它),否则 Chrome 会拒绝解码。
如果用了 Web Audio API,AudioContext 默认挂起,首次播放前必须在用户手势中调 context.resume(),否则 play() 会报 The request is not allowed by the user agent。
最后提醒一句:iOS 上 playsinline 属性不能少,否则带视频轨道的音频(如 m3u8)会被强制全屏,行为不可控;而 Safari macOS 版还会在页面切后台几秒后主动暂停 <audio>,这不是代码问题,是系统策略。



















