How can I avoid this โ€œclickโ€ sound when I stop playing sound?

I really hope that this question remains a programming issue and does not end with a question of sound mechanics ... Here goes ...

I am doing some experimentation to figure out how the Web Audio API works. What I'm trying to do is a simple "Hang up phone" sound played in a loop. The problem is that when the sound ends, you can hear a rather annoying โ€œclickโ€ of the sound. I canโ€™t explain it better, but you can hear it if you check the code.

Is there any way to avoid this? Which filter could I apply or something else?

var audioContext = new (AudioContext || webkitAudioContext)(); var frequencyOffset = 0 function boop(){ // Our sound source is a simple triangle oscillator var oscillator = audioContext.createOscillator(); // Create sound source oscillator.type = 'triangle'; // Adding a gain node just to lower the volume a bit and to make the // sound less ear-piercing var gain = audioContext.createGain(); oscillator.connect(gain); gain.connect(audioContext.destination); gain.gain.value = 0.1; // Just for fun let the frequency increase on each itteration oscillator.frequency.value = 200 + frequencyOffset; oscillator.start(0); // The sound should last for 250ms setTimeout(function(){ oscillator.disconnect(); oscillator.stop(); gain.disconnect(); }, 250); frequencyOffset += 1; } setInterval(boop, 500); 
+6
source share
3 answers

The web audio API seems to give the developer an easy way to stop the sound source from playing without suddenly stopping the signal and avoid any noise and sound artifacts.

  • Create your own sound source (in my example, an oscillator)
  • Create a node gain and connect it to the sound source.
  • Start the sound source and set the gain value to 0. Thus, you will not listen to the sound, even if it technically reproduces
  • Set the gain value to 1 when you want the source to play, and 0 when it should not be played. The gain of the node will handle the rest, and the click will not be heard.

 var audioContext = new(AudioContext || webkitAudioContext)(); var frequencyOffset = 0 // Our sound source is a simple triangle oscillator var oscillator = audioContext.createOscillator(); // Create sound source oscillator.type = 'triangle'; // Adding a gain node just to lower the volume a bit and to make the // sound less ear-piercing. It will also allow us to mute and replay // our sound on demand var gainNode = audioContext.createGain(); oscillator.connect(gainNode); gainNode.connect(audioContext.destination); gainNode.gain.value = 0; oscillator.frequency.value = 200; oscillator.start(0); function boop() { gainNode.gain.value = 0.1; // The sound should last for 250ms setTimeout(function() { gainNode.gain.value = 0; }, 250); oscillator.frequency.value++; } setInterval(boop, 500); 
+4
source

This is a sound problem, not a programming problem. The click you hear occurs when the waveform stops / cuts in the middle of the wave, and not when crossing zero.

The best simple solution from the audio paradigm is to fade out very quickly, not just stop playing.

A slightly more complicated solution is to find the next zero intersection and stop playback at this point.

+7
source

Here is a brief explanation of why we hear clicks (this is the human ear) and good examples of how to get around this using the web audio API here: http://alemangui.imtqy.com/blog//2015/12/26/ramp -to-value.html

The main conclusion from the article is that exponential methods for click removal work better; exponentialRampToValueAtTime and setTargetAtTime .

Using setTargetAtTime to remove a click

 var context = new AudioContext(); var oscillator = context.createOscillator(); var gainNode = context.createGain(); oscillator.connect(gainNode); gainNode.connect(context.destination) oscillator.start(); stopButton.addEventListener('click', function() { gainNode.gain.setTargetAtTime(0, context.currentTime, 0.015); }); 

Using exponentialRampToValueAtTime to Remove a Click

 var context = new AudioContext(); var oscillator = context.createOscillator(); var gainNode = context.createGain(); oscillator.connect(gainNode); gainNode.connect(context.destination) oscillator.start(); stopButton.addEventListener('click', function() { // Important! Setting a scheduled parameter value gainNode.gain.setValueAtTime(gainNode.gain.value, context.currentTime); gainNode.gain.exponentialRampToValueAtTime(0.0001, context.currentTime + 0.03); }); 

Both of them worked for me in my use case, with exponentialRampToValueAtTime working a little better. I still heard a faint click using setTargetAtTime .

0
source

Source: https://habr.com/ru/post/984527/


All Articles