简体   繁体   English

使用 Web Audio API 控制音量

[英]Volume control with Web Audio API

I'm working on a simple project to create an instrument, using Web Audio API, and wrote the following snippet (you can press ' Q ' to play the note):我正在做一个简单的项目来创建一个乐器,使用 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); } });

(If you are not familiar with the terminology: 'attack' is the time it takes to the note to reach it's peak, 1 second in my example, and 'release' is the time it takes to fade away after someone releases the key, also 1 sec on this example). (如果你不熟悉这个术语:'attack' 是音符达到峰值所需的时间,在我的例子中为 1 秒,'release' 是在有人释放键后消失所需的时间,在这个例子中也是 1 秒)。

The problem is this 'clicking' sound you can hear before and after the note plays.问题是您可以在音符播放前后听到这种“咔哒”声。 I did some research:我做了一些研究:

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/ http://modernweb.com/2014/03/31/creating-sound-with-the-web-audio-api-and-oscillators/

and found that it would be caused by cutting the sound wave, so I should keep the note playing at 0 dB and raise / lower the volume as needed.并发现它是由切割声波引起的,所以我应该将音符保持在 0 dB 并根据需要提高/降低音量。 However, it only works specifically on Chrome and only if I'm setting volume directly, like so: carrier_volume.gain.value = 1 .但是,它只能专门对Chrome和仅如果我直接设置音量,就像这样: carrier_volume.gain.value = 1 It does not work, even in Chrome, if I use the linearRampToValueAtTime() function.如果我使用linearRampToValueAtTime()函数,即使在 Chrome 中它也不起作用。

Other strange thing happens if I try to set the initial volume to 0 directly.如果我尝试将初始音量直接设置为 0,则会发生其他奇怪的事情。 By using carrier_volume.gain.value = 0 on initialization, the first note played will be cut out, but the next notes will play normally.通过在初始化时使用carrier_volume.gain.value = 0 ,播放的第一个音符将被切除,但接下来的音符将正常播放。

Does anyone have found a solution to this annoying clicking noises and what is the delay problem when using both gain.value and linearRampToValueAtTime() ?有没有人找到解决这种烦人的点击噪音的方法,以及使用gain.valuelinearRampToValueAtTime()时的延迟问题是什么?

So, here's the deal - a linearRampToValueAtTime needs a START time.所以,这就是交易 - linearRampToValueAtTime 需要一个 START 时间。 You're intending that to be "now" - when the key is pressed - but you need to make it explicit, by setting a current value when the key is pressed.您打算将其设为“现在” - 当按下键时 - 但您需要通过在按下键时设置当前值来使其明确。 Also, you shouldn't use linearRamp at the creation - just set the value directly.此外,您不应该在创建时使用 linearRamp - 只需直接设置值。

If you set the initial volume to 0 directly (via .value) it shouldn't cut out entirely, but the first ramp won't have a start point - so it will remain zero until the linearRamp's time passed, and then it will jump to 1.如果您将初始音量直接(通过 .value)设置为 0,则不应完全切断,但第一个斜坡将没有起点 - 因此它将保持为零,直到 linearRamp 的时间过去,然后它会跳到 1。

Try this:尝试这个:

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

The key to setting the volume设置音量的关键

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM