JavaScript AudioContext, OscillatorNode, GainNode troubleshooting
Recently I made a piano using JavaScript. There's a lot of resources I used in order to make it, and a lot of trial and error went into it as well. I want to put together a list of those issues I ran into, as well as the solutions I went with. Perhaps this may help others as well, who may run into some of those issues when making their own app that uses the AudioContext, OscillatorNode, and GainNode to generate sounds. I'll also keep this post updated whenever I continue working on my piano app.
Contents
The audio quality degrades over time
When playing notes over a period of time, if you notice the sound quality degrading—such as sounding more static-y and muffled—then check where the AudioContext
is declared.
I learned that you only need one AudioContext
for all the oscillators and gains. Creating new ones each time makes the audio quality degrade over time, and is a lot less efficient. That helped with the sound creation being more consistent.
There's a clicking noise after an oscillator stops
When an oscillator stops, it makes a clicking noise if it's in the middle of the wave, and hasn't completed one cycle.
I learned that using a exponentialRampToValueAtTime
or linearRampToValueAtTime
to gradually turn down the volume and then stop the oscillator helps, but isn't perfect. I ended up going with linearRampToValueAtTime
since it's more gradual. This is still a learning process, so I'll probably tweak it as I learn more.
There's no sound at the lower frequencies
When the frequency is super low, for octaves 0-1 for example, it may be hard to hear the sound. Try changing the oscillator type and see if it helps. For me, changing from sine
to triangle
helped make those frequencies more audible.
The sound is too ear-piercingly loud at higher frequencies
When the frequency is super high, for octaves 6-8 for example, it may help to decrease the gain
value. For example, having something similar to this may help:
const volume =
frequency < 130
? 2
: frequency < 261
? 1
: frequency < 500
? 0.4
: frequency < 1000
? 0.2
: frequency < 2000
? 0.07
: 0.04;
// whatever you have your gain node defined as
gainNode.value = volume;
Example snippet:
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
const note = keyElement.getAttribute('data-note');
const octave = keyElement.getAttribute('data-octave');
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
gainNode.gain.value = volume;
oscillator.frequency.value = frequency;
oscillator.type = 'triangle';
oscillator.start(0);
iPhone after using headphones, audio no longer works
The audio was working fine without headphones plugged in, then after using headphones, it no longer works when headphones are unplugged. If this happens to you, try playing a silent audio file to unmute when headphones are not in.
This is a hacky way to get the JavaScript generated audio to play again, but as of now, I don't know of a better solution. If anyone has any ideas, please feel free to let me know.
Sources
- StackOverflow
- Tutorial on JavaScript audio with explanations including what the types of waves are
- AudioContext
- Notes frequencies chart
- JSON and dynamic calculation for frequency to notes
- StackOverflow: ios webaudio only works on headphones
- unmute-ios-audio
More info as well as resources can be found in the Github repo.