问题描述
我使用的是 Firefox 84.0.1、Windows 10、x86_64。我有一个非常基本的 WebAudioWorklet
合成器,可以将键映射到音符的频率。当一个键被按下时,它会产生非常断断续续的音频。这让我认为没有足够的音频样本排队供扬声器播放,因此音频进出。但是,在音频处理方面,我正在执行一项非常低强度的任务。因此,我觉得默认的 Worklet
设置应该能够处理这个问题。这是我的代码:
syn.js
(async() => {
let a2_hertz = 110.0;
let twelfth_root_of_two = Math.pow(2.0,1.0 / 12.0);
let audio_cxt = new AudioContext();
await audio_cxt.audioWorklet.addModule("syn-worklet.js",{credentials: "omit"});
let audio_worklet_options = {
numberOfInputs: 0,numberOfOutputs: 1,outputChannelCount: [audio_cxt.destination.channelCount]
};
let audio_worklet = new AudioWorkletNode(audio_cxt,"synthesiser",audio_worklet_options);
audio_worklet.connect(audio_cxt.destination);
document.addEventListener("keydown",(evt) => {
for (let key = 0; key < 12; ++key) {
if (evt.code == "Key" + "QWERTYUIOPAS"[key]) {
audio_worklet.port.postMessage(a2_hertz * Math.pow(twelfth_root_of_two,key));
}
}
});
document.addEventListener("keyup",(evt) => {
audio_worklet.port.postMessage(0.0);
});
})();
syn-worklet.js
function angular_frequency(hertz) {
return hertz * 2 * Math.PI;
}
let OSC_TYPES = {"sine": 0,"square": 1,"triangle": 2};
function oscillator(hertz,osc_type) {
switch (osc_type) {
case OSC_TYPES.sine: {
return Math.sin(angular_frequency(hertz) * currentTime);
} break;
case OSC_TYPES.square: {
return Math.sin(angular_frequency(hertz) * currentTime) > 0.0 ? 1.0 : -1.0;
} break;
case OSC_TYPES.triangle: {
return Math.asin(Math.sin(angular_frequency(hertz) * currentTime)) * (2.0 / Math.PI);
} break;
default: {
return 0.0;
}
}
}
class Synthesiser extends AudioWorkletProcessor {
constructor() {
super();
this.hertz = 0.0;
this.port.onmessage = (evt) => {
this.hertz = evt.data;
};
}
process(inputs,outputs) {
let channels = outputs[0];
let num_samples_per_channel = channels[0].length;
for (let pcm_i = 0; pcm_i < num_samples_per_channel; ++pcm_i) {
let volume = 0.1;
let pcm_value = volume * oscillator(this.hertz,OSC_TYPES.sine);
for (let channel_i = 0; channel_i < channels.length; ++channel_i) {
channels[channel_i][pcm_i] = pcm_value;
}
}
return true;
}
}
registerProcessor("synthesiser",Synthesiser);
解决方法
我认为问题在于 currentTime
似乎是唯一影响 oscillator()
函数输出的因素。但是 currentTime
在调用 process()
函数期间不会改变。
我建议改用 currentFrame
。它会给你一个整数值,代表帧中的 currentTime
。如果您将其与 pcm_i
结合使用,您将获得您正在处理的样本的实际索引。
const currentSample = currentFrame + pcm_i;
const currentSampleInSeconds = (currentFrame + pcm_i) / sampleRate;