2015-10-14 2 views
0

Я работаю над проектом простой, чтобы создать инструмент, с помощью Web Audio API, и написал следующий фрагмент кода (вы можете нажать кнопку «Q», чтобы играть ноты):Регулятор громкости с Web Audio API

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 дБ и повысить/понизить громкость по мере необходимости , Тем не менее, он работает только на Chrome и только, если я задаю объем напрямую, например: carrier_volume.gain.value = 1. Он не работает даже в Chrome, если я использую функцию linearRampToValueAtTime().

Другие странные вещи случаются, если я пытаюсь напрямую установить начальный том на 0. Используя инициализацию carrier_volume.gain.value = 0, первая нота будет вырезана, но следующие ноты будут воспроизводиться нормально.

Кто-нибудь нашел решение этого раздражающего щелчка, и какая проблема с задержкой при использовании как gain.value, так и linearRampToValueAtTime()?

ответ

1

Итак, вот сделка - для линейногоRampToValueAtTime требуется время ПУСК. Вы намереваетесь, чтобы это было «сейчас» - когда нажата клавиша - но вам нужно сделать это явным, установив текущее значение при нажатии клавиши. Кроме того, вы не должны использовать 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; 
    } 
}); 
+0

большое спасибо! Теперь это звучит гладко. Я также дам вам совет по поводу расчета атаки/выпуска. – adrield

Смежные вопросы