🌞

纯前端实现渐入渐出切换播放(可能是个失败方案)

在客户端听歌的时候,经常能够碰到声音基于音量控制进入/渐出。在前端角度,想当然的会想到,不就基于音量渐大或者减小就可以实现了吗?—— 现实可能不是你想的这么简单(认真读完,你会觉得也没那么复杂)。业内

文链接在语雀:https://www.yuque.com/wumingshi/rkh1qq/

在客户端听歌的时候,经常能够碰到声音基于音量控制进入/渐出。在前端角度,想当然的会想到,不就基于音量渐大或者减小就可以实现了吗?—— 现实可能不是你想的这么简单(认真读完,你会觉得也没那么复杂)。

业内都是怎么做的

著名的音频播放器 howler ,其本身就有渐入渐出的能力。然而当我们使用的时候,发现在iOS上无法出现有效效果。我们的使用姿势是:

  • 开启html5进行流式播放,需要进行大文件载入播放
  • 调用 fade 能力,进行声音的渐变。

在研究源码后,发现在启动 html5 属性后,会启动流式加载,播放是基于 Audio dom进行音视频的加载并播放,同时在播放的时候进行音量的调整;而这是因为这种方式导致在iOS播放的时候无法进行进行动态的音量调整

手动调整audio dom的volume在iOS下是无效的

既然如此,声音的渐入渐出仍然只能寄托于 WebAudio。

解决方案

依托 GainNode 可以实现音量的输出控制,而其中的 linearRampToValueAtTimeexponentialRampToValueAtTime 方法正好可以用于进行声音的渐变处理,这二者的主要区别主要是渐变的行进速度不一样。例如如下方式就简单实现了一个声音渐入的过程。

this.gainNode.gain.setValueAtTime(0.01, this.autdioContext.currentTime);
this.gainNode.gain.exponentialRampToValueAtTime(1.0, this.autdioContext.currentTime + this.fadeInTime);

到这似乎已经可以完美解决了,而在实际过程中我们在使用 Audio Dom进行声音播放的时候一般会进行流式加载,而 WebAudio 该如何解决这个问题呢?方法也很简单,可以借助 WebAudio 的 MediaElementAudioSourceNode 进行加载,这样就可以借助 Audio Dom进行流式加载了。

this.audioElement = document.createElement('audio');
this.audioElement.crossOrigin = 'anonymous';
this.audioElement.controls = false;
this.audioElement.preload = 'auto';
document.body.appendChild(this.audioElement);
this.autdioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
const source = this.autdioContext.createMediaElementSource(this.audioElement);

存在的问题

看似一切都能运行,然而iOS再次出现了诡异问题。这也是标题为啥加了一个可能是一个失败方案。

通过以上的方式似乎已经可以解决声音渐入渐出的核心问题了,但是在实际应用的过程中,发现部分iOS设备存在声音扭曲的现象(开始播放的前几秒),猜想应该是Audio Dom 和 WebAudio 的采样率不同导致的。目前暂未找到解决办法,即使提前预置一部分内容进行播放同样没法解决(网上拼凑的解决)。目前尚不明确在iOS上的具体兼容性(一台iPad设备得到复现)。目前能够想到的可能可以解决的办法,则是放弃使用MediaElementAudioSourceNode的加载方式,改为自行控制流式加载的过程。但这只是一个想法,目前尚未实施。

updatedupdated2022-08-022022-08-02