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?

Josh Lee
  • 171,072
  • 38
  • 269
  • 275
Labbekak
  • 517
  • 3
  • 14
  • 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. – justinjmoses Oct 16 '13 at 20:50

2 Answers2

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

hexerei software
  • 3,100
  • 2
  • 15
  • 19
kuu
  • 815
  • 1
  • 7
  • 16
  • Thanks, this certainly helped. – Labbekak Jan 17 '13 at 15:20
  • Fixes it but doesn't explain his mistake with reassignment of `o` and introduces gain for no real value. – justinjmoses Oct 16 '13 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 Aug 02 '20 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);
}
janesconference
  • 6,333
  • 8
  • 55
  • 73
  • You're still creating seperate AudioContexts for each oscillator, is this acceptable? – Labbekak Jan 14 '13 at 14:28
  • If I run your code with one AudioContext, it still sounds really bad. – Labbekak Jan 14 '13 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) – janesconference Jan 14 '13 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 Jan 14 '13 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. – janesconference Jan 14 '13 at 15:49
  • I'm on Windows 7, Chrome 24.0.1312.52. I'll record what your jsfiddle sounds like. – Labbekak Jan 14 '13 at 16:11
  • I recorded what it sounds like: [one AudioContext](https://dl.dropbox.com/u/56227900/one_audiocontext.wav) and [multiple AudioContexts](https://dl.dropbox.com/u/56227900/multiple_audiocontexts.wav). – Labbekak Jan 14 '13 at 17:57