2

I've been trying to create an 88 key piano with the Web Audio API. The plan is to run all the 88 oscillators first in the appropriate frequencies and then use Oscillator.connect() and Oscillator.disconnect() methods on the respective oscillators as the piano keys are pressed and released. The state of the AudioContext will be "running" all the time. Now, I have two questions here,

  1. Is this the right way to do it?
  2. I get a clicking noise at the start and end of the sounds when I play them. Why is this happening and how to get rid of it?

PS: The reason for creating a piano like this is to indulge myself in the delight of having created something from scratch. So using prerecorded sounds is not an option.

Manu Soman
  • 153
  • 11

2 Answers2

2

IF you wanted to do it that way, add a gain node to each oscillator, and then turn the gain off and on, instead of disconnect and reconnect.

That's probably what's causing your clicks and snaps. More below.

BUT... that's still pretty overkill, having 88 oscillators. The standard way keyboards do this is with a limited polyphony.

Create an array of ten oscillators, all hooked to their own gain, each gain hooked to the destination.

Keep track of how many keys are being pressed, and how many oscillators are in use.

keysPressed = {}

// on key down
keysPressed["60"] = nextAvailableOsc()

At any given time there are ten oscillators ready to go, one for each finger. If for some reason you require more, add them dynamically.


The clicking sound is because you're hard disconnecting and reconnecting running oscillators. Use a gain node in between the osc and destination, and turn that on and off.

Also, you might get clicks when changing the values hard such as

gainNode.gain.value = 0

That can create a glitch in the sound stream.. It should be:

gainNode.gain.setValueAtTime(0, ctx.currentTime + 1)

Maybe the + 1 is necessary. There's also setTargetAtTime and rampToAtTime methods that make things even smoother:

https://developer.mozilla.org/en-US/docs/Web/API/AudioParam

MikeHelland
  • 1,151
  • 1
  • 7
  • 17
  • That's a cool idea. Thanks. Perhaps I would have to use a little more than 10 oscillators in order to add all the harmonics. And any comments on the clicking noise I mentioned? – Manu Soman Jul 07 '20 at 05:57
  • I missed that part. That's a standard part of Web Audio API, using setValueAtTime(). See the Edit – MikeHelland Jul 07 '20 at 08:25
  • I expanded more on the clicks. As for how many oscillators, this project (https://nicroto.github.io/viktor/) creates multiple oscs for each note, and thus has an array of arrays of oscs. – MikeHelland Jul 07 '20 at 08:34
  • Will definitely try this. Thanks. – Manu Soman Jul 07 '20 at 15:52
  • I seem to get minor popping when turning gain on/off using gainNode.gain.setValueAtTime(). I will try rampToAtTime for note entrances. – Shawn Eary Dec 26 '22 at 16:21
1

An alternative approach is to create the oscillators on demand. When a key is pressed, create one or more oscillators (for harmonics). These can feed into one (or more?) gain nodes which has an automation for the attack and sustain phase. When the key is released, automate the gain for a release phase and schedule the oscillator(s) to stop after the release phase has ended. Drop the reference to all the oscillators now.

I find this easier to reason about than having an array of oscillators, and there's no limit to the polyphony. But this approach generates more garbage that has to be handled eventually by the collector.

Raymond Toy
  • 5,490
  • 10
  • 13
  • Got it. But wouldn't this dynamic addition of oscillators cause delays? – Manu Soman Jul 07 '20 at 15:55
  • Yes, there will be some small delay. But you have that as well with the other approach because adjusting the gain (for attack and release) also doesn't happen instantly. To work the best, you have to schedule the gain changes a little in the future because the audio thread may have already progressed past the current time that you see in the main thread. – Raymond Toy Jul 07 '20 at 16:08
  • Will try this. Let me see. Thanks. – Manu Soman Jul 07 '20 at 17:37