使用Web Audio API进行音量控制

时间:2015-10-14 15:39:16

标签: javascript audio volume web-audio

我正在开发一个使用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);
  }
});

(如果你不熟悉术语:'攻击'是笔记达到峰值所需的时间,在我的例子中是1秒,而'释放'是在某人释放后消失所需的时间关键,在这个例子上也是1秒。)

问题在于您可以在音符播放之前和之后听到的“咔哒”声。我做了一些研究:

How can I avoid this 'clicking' sound when I stop playing a sound?

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

并发现它是由切割声波引起的,因此我应该将音符保持在0 dB并根据需要升高/降低音量。但是,它只能在Chrome 上专门 ,如果我直接设置音量,则仅,如下所示:carrier_volume.gain.value = 1。如果我使用linearRampToValueAtTime()功能,即使在Chrome中也无效。

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

有没有人找到解决这种烦人的点击噪音的解决方案,以及使用gain.valuelinearRampToValueAtTime()时的延迟问题是什么?

2 个答案:

答案 0 :(得分:1)

所以,这是交易 - linearRampToValueAtTime需要一个START时间。您打算“现在” - 当按下键时 - 但是您需要通过在按下键时设置当前值来使其显式化。此外,您不应在创建时使用linearRamp - 只需直接设置值。

如果直接将初始音量设置为0(通过.value),它不应该完全切断,但第一个渐变不会有一个起点 - 所以它将保持为零,直到linearRamp的时间过去,然后它会跳到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;
  }
});

答案 1 :(得分:0)

设置音量的键

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');