前天尝试了一波那个什么 MediaSourceExtension,结果发现那套 API 目前限制蛮大的,而且对我来说没什么帮助(audio/x-wav 完全不正常支持,audio/mpeg 也只能在 Chrome 上使用)于是只能放弃折腾了 QAQ
昨天突然想起之前写 nanoPlayer 的时候,使用了一个叫 Audio Context 的接口,nanoPlayer 用了这个 API 里的 createAnalyzer
方法,来获得音频的频率数据,进而实现了一个频谱可视化功能。之前就注意到了这个接口中有个自定义 AudioBufferSource
的方法,可以指定若干 Float32Array
并交给浏览器播放,应该是蛮有意思的。
这里就实现一个可制定频率的正弦波音频吧,如果这个实现起来没有什么难度的话,就准备试试浏览器端解码 WAV 音频。
首先是 HTML,我们要准备一个文本框来获得指定的频率,两个按钮分别控制播放的开始与停止:
1
2
3
4
5
6
7
8
9
10
11
|
<!DOCTYPE html>
<html>
<body>
<input id="freq" value="440" />
<button start>start</button>
<button stop>stop</button>
<script>
// TODO: code here.
</script>
</body>
</html>
|
然后初始化若干变量:
1
2
3
4
5
|
let ctx = new AudioContext();
let frames = ctx.sampleRate;
let start = document.querySelector("button[start]");
let stop = document.querySelector("button[stop]");
let last = null;
|
其中 ctx.sampleRate 是 AudioContext 的采样率,这里直接将一秒钟的采样数作为帧数,产生一段持续时间为一秒的 AudioSourceBuffer 就足够了,还有 last 是用来保存上一次的 AudioSource,可以随时调用 stop 方法来终止播放。
然后就是为 start 按钮增加一个点击事件:根据文本框里的数值产生一段指定频率的音频采样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
start.onclick = (e) => {
if (last) last.stop();
let freq = document.querySelector('#freq').value;
// 初始化一个单声道,采样率和 AudioCotnext 一致,持续时间为 1 sec 的 AudioBuffer
let audioBuffer = ctx.createBuffer(1, ctx.sampleRate, ctx.sampleRate);
// 获得其中第一个声道的数据源,类型是 Float32Array
let nowBuffering = audioBuffer.getChannelData(0);
// 填充数据,取值范围是 [-1, 1],直接用正弦函数就行了
for (let i = 0; i < frames; ++i) {
nowBuffering[i] = Math.sin((Math.PI _ 2 _ freq) \* (i / ctx.sampleRate));
}
// 初始化一个 AudioBufferSource,并将上面的 AudioBuffer 与之绑定,并输出到 AudioContext
let source = ctx.createBufferSource();
source.loop = true;
source.buffer = audioBuffer;
source.connect(ctx.destination);
// 开始回放
source.start();
// 保存以备后续使用
last = source;
}
|
当然还需要对停止按钮绑定一个事件,来停止回放:
1
2
3
4
|
stop.onclick = (e) => {
if (last) last.stop();
last = null;
};
|
以上就是所有代码啦,效果的话,我在这里直接放个 demo 就行: