音量控制与Web音频API

Volume control with Web Audio API

本文关键字:API 音频 Web 音量控制      更新时间:2023-09-26

我正在做一个简单的项目来创建一个乐器,使用Web Audio API,并编写了以下代码片段(您可以按'Q'来播放音符):

var audio = new AudioContext();
var volume = 0;
var attack = 1;
var release = 1;
var carrier = audio.createOscillator();
carrier.frequency.value = 440.00;
carrier.type = "sine";
carrier.start();
var carrier_volume = audio.createGain();
carrier_volume.gain.linearRampToValueAtTime(volume, 0);
carrier.connect(carrier_volume);
carrier_volume.connect(audio.destination);
document.addEventListener("keydown", function(e) {
  if(e.keyCode == 81) {
    carrier_volume.gain.linearRampToValueAtTime(1, audio.currentTime + attack);
  }
});
document.addEventListener("keyup", function(e) {
  if(e.keyCode == 81) {
    carrier_volume.gain.linearRampToValueAtTime(0, audio.currentTime + release);
  }
});

(如果你不熟悉术语:'attack'是音符达到峰值所需的时间,在我的例子中是1秒,'release'是在有人释放键后逐渐消失所需的时间,在这个例子中也是1秒)

问题是这个"点击"的声音,你可以听到之前和之后的音符播放。我做了一些调查:

如何避免'点击'当我停止播放一个声音时,会发出声音吗?

http://modernweb.com/2014/03/31/creating-sound-with-the-web-audio-api-and-oscillators/

,发现这是由切割声波引起的,所以我应该保持音符在0 dB播放,并根据需要提高/降低音量。然而,它只适用于特别是 Chrome ,只有如果我直接设置音量,像这样:carrier_volume.gain.value = 1。它不工作,即使在Chrome中,如果我使用linearRampToValueAtTime()功能。

如果我试图直接将初始音量设置为0,会发生其他奇怪的事情。在初始化时使用carrier_volume.gain.value = 0,播放的第一个音符将被切断,但接下来的音符将正常播放。

是否有人找到了解决这个恼人的点击噪音和什么是延迟问题时使用gain.valuelinearRampToValueAtTime() ?

所以,这里是交易-一个linearRampToValueAtTime需要一个START时间。你想让它是"现在"——当键被按下的时候——但是你需要通过设置键被按下的当前值来明确它。此外,你不应该在创建时使用linearRamp -直接设置值。

如果你直接将初始音量设置为0(通过.value),它不应该完全切断,但第一个斜坡不会有一个起点-所以它将保持零,直到线性斜坡的时间过去,然后它将跳转到1。

试试这个:

var audio = new AudioContext();
var volume = 0;
var attack = 1;
var release = 1;
var carrier = audio.createOscillator();
carrier.frequency.value = 440.00;
carrier.type = "sine";
carrier.start();
var carrier_volume = audio.createGain();
carrier_volume.gain.linearRampToValueAtTime(volume, 0);
carrier.connect(carrier_volume);
carrier_volume.connect(audio.destination);
// remember whether we're playing or not; otherwise the keyboard repeat will confuse us
var playing = false;
document.addEventListener("keydown", function(e) {
  if((e.keyCode == 81) && !playing) {
    // first, in case we're overlapping with a release, cancel the release ramp
    carrier_volume.gain.cancelScheduledValues(audio.currentTime);
    // now, make sure to set a "scheduling checkpoint" of the current value
    carrier_volume.gain.setValueAtTime(carrier_volume.gain.value,audio.currentTime);
    // NOW, set the ramp
    carrier_volume.gain.linearRampToValueAtTime(1, audio.currentTime + attack);
    // Note that ideally, we would check the current value from above, and calculate 
    // the length of the attack based on it to keep a constant angle of the attack,
    // rather than a constant time.  (If we're half-way through a release when we 
    // start a new attack, the attack should only take 0.5s since we're already at 0.5.)
    playing = true;
  }
});
document.addEventListener("keyup", function(e) {
  if((e.keyCode == 81) && playing) {
    // first, in case we're overlapping with an attack, cancel the attack ramp
    carrier_volume.gain.cancelScheduledValues(audio.currentTime);
    // now, make sure to set a "scheduling checkpoint" of the current value
    carrier_volume.gain.setValueAtTime(carrier_volume.gain.value,audio.currentTime);
    // NOW, set the ramp
    carrier_volume.gain.linearRampToValueAtTime(0, audio.currentTime + release);
    // Note that ideally, we would check the current value from above, and calculate 
    // the length of the release based on it to keep a constant angle of the release,
    // rather than a constant time.  (If we're half-way through an attack when we 
    // start a new release, the release should only take 0.5s since we're already at 0.5.)
    playing = false;
  }
});

音量设置键

gainNode.gain.setValueAtTime(0.5, context.currentTime);

<button id="start">playSound</button>
const audioPlay = async url => {
  const context = new AudioContext();
  var gainNode = context.createGain();
  const source = context.createBufferSource();
  const audioBuffer = await fetch(url)
    .then(res => res.arrayBuffer())
    .then(ArrayBuffer => context.decodeAudioData(ArrayBuffer));
  source.buffer = audioBuffer;
  source.connect(gainNode);
  gainNode.connect(context.destination);
  gainNode.gain.setValueAtTime(0.5, context.currentTime);  // volume, 0 means mute
  source.start();
};
document.querySelector('#start').onclick = () => audioPlay('music/2.ogg');