使用 WebAudio 制作字节跳动
Making byte-beats using WebAudio
Byte-beats 是一种制作低保真音乐的有趣方式。我想使用 WebAudio API 自己制作一些音乐。这是我当前的代码:
const sampleRate = 8000;
const frameCount = sampleRate * 5;
const audioCtx = new AudioContext({ sampleRate: sampleRate });
const src = audioCtx.createBufferSource();
const buf = audioCtx.createBuffer(1, frameCount, sampleRate);
buf.getChannelData(0).set(buf.getChannelData(0).map((_, t) => {
return (Math.sin(t / 10 + Math.sin(t * Math.pow(2, t >> 10)))) * 64 + 128;
}));
src.buffer = buf;
src.connect(audioCtx.destination);
src.start(0, 0, 100);
console.log('Reached the end :/');
我对这个解决方案的问题是我必须创建一个必须保存在内存中的巨大缓冲区。我希望有一种动态的方式来设置声音的振幅以节省内存。
byte-beats 将是完整的音乐作品,可能会很长。因此,帧数可能会变得非常庞大。
有人可以建议我怎么做吗?使用其他库是一种选择,但我宁愿避免这种情况。
这听起来像是 AudioWorklet 的一个很好的用例。使用 AudioWorklet 时,您一次只需提供 128 个样本。出于性能原因,它在另一个线程上运行。这使得编码有点复杂。这是一个基本示例,它使用动态创建的 URL 来加载 AudioWorklet 的代码。
const play = async () => {
const audioContext = new AudioContext({ sampleRate: 8000 });
const source = `registerProcessor(
'byte-beats-processor',
class extends AudioWorkletProcessor {
process (_, [ output ]) {
for (let i = 0; i < 128; i += 1) {
const t = currentFrame + i;
output[0][i] = Math.sin(t);
}
return true;
}
}
);`
const blob = new Blob([ source ], { type: 'application/javascript' });
const url = URL.createObjectURL(blob);
await audioContext.audioWorklet.addModule(url);
const audioWorkletNode = new AudioWorkletNode(audioContext, 'byte-beats-processor');
audioWorkletNode.connect(audioContext.destination);
};
play();
当然使用Math.sin(t)
只是一个例子。您可能想用更有趣的东西替换它。
AudioWorklet 目前仅在 Chrome 中可用。这意味着您仍然需要为其他浏览器使用已弃用的 ScriptProcessorNode,或者您可以使用诸如 standardized-audio-context 之类的 polyfill,它允许您为所有浏览器使用相同的代码。
Byte-beats 是一种制作低保真音乐的有趣方式。我想使用 WebAudio API 自己制作一些音乐。这是我当前的代码:
const sampleRate = 8000;
const frameCount = sampleRate * 5;
const audioCtx = new AudioContext({ sampleRate: sampleRate });
const src = audioCtx.createBufferSource();
const buf = audioCtx.createBuffer(1, frameCount, sampleRate);
buf.getChannelData(0).set(buf.getChannelData(0).map((_, t) => {
return (Math.sin(t / 10 + Math.sin(t * Math.pow(2, t >> 10)))) * 64 + 128;
}));
src.buffer = buf;
src.connect(audioCtx.destination);
src.start(0, 0, 100);
console.log('Reached the end :/');
我对这个解决方案的问题是我必须创建一个必须保存在内存中的巨大缓冲区。我希望有一种动态的方式来设置声音的振幅以节省内存。
byte-beats 将是完整的音乐作品,可能会很长。因此,帧数可能会变得非常庞大。
有人可以建议我怎么做吗?使用其他库是一种选择,但我宁愿避免这种情况。
这听起来像是 AudioWorklet 的一个很好的用例。使用 AudioWorklet 时,您一次只需提供 128 个样本。出于性能原因,它在另一个线程上运行。这使得编码有点复杂。这是一个基本示例,它使用动态创建的 URL 来加载 AudioWorklet 的代码。
const play = async () => {
const audioContext = new AudioContext({ sampleRate: 8000 });
const source = `registerProcessor(
'byte-beats-processor',
class extends AudioWorkletProcessor {
process (_, [ output ]) {
for (let i = 0; i < 128; i += 1) {
const t = currentFrame + i;
output[0][i] = Math.sin(t);
}
return true;
}
}
);`
const blob = new Blob([ source ], { type: 'application/javascript' });
const url = URL.createObjectURL(blob);
await audioContext.audioWorklet.addModule(url);
const audioWorkletNode = new AudioWorkletNode(audioContext, 'byte-beats-processor');
audioWorkletNode.connect(audioContext.destination);
};
play();
当然使用Math.sin(t)
只是一个例子。您可能想用更有趣的东西替换它。
AudioWorklet 目前仅在 Chrome 中可用。这意味着您仍然需要为其他浏览器使用已弃用的 ScriptProcessorNode,或者您可以使用诸如 standardized-audio-context 之类的 polyfill,它允许您为所有浏览器使用相同的代码。