8

I would like to play a chord using OscillatorNodes:

var ac = new (window.AudioContext || window.webkitAudioContext);
// C4, E4, G4
var freqs = [261.63, 329.63, 392.00];
for(var i=0;i<freqs.length;i++) {
  var o = ac.createOscillator();
  o.frequency.value = freqs[i];
  o.connect(ac.destination);
  o.noteOn(0);
  setTimeout(function() {o.noteOff(0)}, 1000);
}

But this approach sounds like a mess (here's what it sounds like). If I try creating new AudioContexts for each note in the chord, then it sounds fine (like this):

// C4, E4, G4
var freqs = [261.63, 329.63, 392.00];
for(var i=0;i<freqs.length;i++) {
  var ac = new (window.AudioContext || window.webkitAudioContext);
  var o = ac.createOscillator();
  o.frequency.value = freqs[i];
  o.connect(ac.destination);
  o.noteOn(0);
  setTimeout(function() {o.noteOff(0)}, 1000);
}

But I read that you're only supposed to have one AudioContext. What am I doing wrong?

1
  • The problem is the loop & closure with using o which is reassigned each pass. Moreover avoid setTimeout altogether, use the functions you're given. See janesconference solution below. Commented Oct 16, 2013 at 20:50

2 Answers 2

8

Not sure this can be a solution, but I found out inserting a GainNode and setting it's value so that the gain will sum to 1 eliminates the issue:

var ac = new (window.AudioContext || window.webkitAudioContext);
// C4, E4, G4
var freqs = [261.63, 329.63, 392.00];
for(var i=0;i<freqs.length;i++) {
  var o = ac.createOscillator();
  var g = ac.createGainNode();
  o.frequency.value = freqs[i];
  o.connect(g);
  g.gain.value = 1/freqs.length;
  g.connect(ac.destination);
  o.start(0);
  setTimeout(function(s) {s.stop(0)}, 1000, o);
}

I tried this on Chrome 23.0.1271.101

Updated to use the new start and stop methods: createOscillator noteOn not working

3
  • Thanks, this certainly helped.
    – Labbekak
    Commented Jan 17, 2013 at 15:20
  • Fixes it but doesn't explain his mistake with reassignment of o and introduces gain for no real value. Commented Oct 16, 2013 at 20:50
  • I've seen something like this happen before in Cubase, LMMS and my 1212M PatchMix app when I overdrove the associated digital mixers. I would get clipping like when old school electric guitarists used to blast their amps. I'm surprised I'm not finding anything in the Web Audio API to automatically perform the compression, mixing and/or normalization. It's 2020 now and I was drawn to this post because I was apparently saturating my audio context. When I pulled the gain down like kuu said, my problem went away.
    – Shawn Eary
    Commented Aug 2, 2020 at 23:58
4

noteOn(0) starts the oscillator / note immediately.

Since your for loop takes time to create the oscillators, the start time is slightly delayed for every note after the first. I would initialize each oscillator, put them in an array, then call noteOn() on every one of them in another for loop.

And you don't need to call setTimeout, which is unreliable: noteOff will be executed after in a second by calling noteOff(1).

var ac = new (window.AudioContext || window.webkitAudioContext);
// C4, E4, G4
var freqs = [261.63, 329.63, 392.00];
var oscs = [];
// initialize the oscillators
for(var i=0;i<freqs.length;i++) {
    var o = ac.createOscillator();
    o.frequency.value = freqs[i];
    o.connect(ac.destination);
    oscs.push(o);
}
// schedule noteOn and noteOff (deprecated: the methods will be renamed to start() and   stop() soon)
for (i = 0; i < oscs.length; i +=1) {
    oscs[i].noteOn(0);
    oscs[i].noteOff(1);
}
7
  • You're still creating seperate AudioContexts for each oscillator, is this acceptable?
    – Labbekak
    Commented Jan 14, 2013 at 14:28
  • If I run your code with one AudioContext, it still sounds really bad.
    – Labbekak
    Commented Jan 14, 2013 at 14:43
  • What do you mean with "sounds messy"? I can't hear the "mess" with my solution (while I heard single tones playing before joining the chord in yours) Commented Jan 14, 2013 at 15:01
  • What browser are you using? Can you hear a difference between the multiple AudioContexts and the single one? I don't hear single notes playing in all cases I hear chords. Only with one AudioContext I hear lots of cracking (hard to explain) and you can barely hear the chord, it also sounds lower than it's suppose to be and more like a square wave the n a sine wave.
    – Labbekak
    Commented Jan 14, 2013 at 15:15
  • Chrome Version 23.0.1271.97 on Ubuntu here. I hear no gaps and / or crackling with the one-context version, just the chord. Commented Jan 14, 2013 at 15:49

Not the answer you're looking for? Browse other questions tagged or ask your own question.