Skip to content
Create a Pulse Oscillator using the Web Audio API
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
3rd-party Initial commit. Apr 22, 2014
assets Initial commit. Apr 22, 2014
.gitattributes Initial commit. Apr 22, 2014
.gitignore Initial commit. Apr 22, 2014
MIT-LICENSE.md Initial commit. Apr 22, 2014
README.md Corrections to markdown formatting May 8, 2017
example-pwm.html Tweaks to markdown and URLs Apr 22, 2014
example-synth.html

README.md

Create a Pulse Oscillator using the Web Audio API

Introduction

Many classic analogue synthesiser lead-synth and string-ensemble sounds were based upon a modulated "pulse" waveform. This project demonstrates a set of techniques for recreating a Pulse Wave using the Web Audio API.

The new oscillator is demonstrated in the following examples:

The pulse wave is similar to a normal "square" waveform (figure 1)

PulseWaveFigure1.gif

... but the "duty-cycle" or "mark-space ratio" is asymmetrical (figure 2).

PulseWaveFigure2.gif

This creates a very distinctive sound – especially if you can modulate the mark-space ratio (which is what we’re going to do here).

Implementing the basics

The OscillatorNode provided by the Web Audio API allows you to easily create sine, square, triangle and sawtooth waveforms. We need to be a bit devious to coax a pulse wave out of it.

We start with a normal sawtooth wave:

var ac = new AudioContext();

var sawtoothOsc = ac.createOscillator();
sawtoothOsc.type = "sawtooth";
sawtoothOsc.frequency.value = 110;

sawtoothOsc.connect(ac.destination);
sawtoothOsc.start(ac.currentTime);
sawtoothOsc.stop(ac.currentTime + 2);

Which gives us a waveform that that rises from -1 to +1 – with an average value of zero (figure 3):

PulseWaveFigure3.gif

Next we add a WaveShaper node to transform the sawtooth into a square wave:

var ac = new AudioContext();

var sawtoothOsc = ac.createOscillator();
sawtoothOsc.type = "sawtooth";
sawtoothOsc.frequency.value = 110;

var pulseCurve =new Float32Array(256);
for (var i=0; i<128; i++){
  pulseCurve [i]=-1;
  pulseCurve [i+128]=1;
}
var pulseShaper=ac.createWaveShaper();
pulseShaper.curve= pulseCurve;

sawtoothOsc.connect(pulseShaper);

//Add an offset here.

waveshaper.connect(ac.destination);
sawtoothOsc.start(ac.currentTime);
sawtoothOsc.stop(ac.currentTime + 2);

This uses a symmetrical curve to produce the waveform in figure 4. Half of the sawtooth wave is below 0 – and so translates to a value of -1. Half is above 0 – and so translates to +1.

PulseWaveFigure4.gif

To get a pulse wave, we need to add apply an offset to the sawtooth (so that it rises from -0.5 to +1.5). There are 2 ways of easily creating a constant offset value using the Web Audio API:

  • Create an AudioBufferSource (where the AudioBuffer only contains the value that we want).
  • Or create another WaveShaper node that shapes all of its input values to the desired constant value.

In this case, it is more convenient to use the WaveShaper method:

//Add an offset here.
var constantCurve = new Float32Array(2);
constantCurve[0] = 0.5;
constantCurve[1] = 0.5;
var constantShaper = ac.createWaveShaper();
constantShaper.curve = constantCurve;
sawtoothOsc.connect(constantShaper);
constantShaper.connect(pulseShaper);

The WaveShaper node will transform this into an output where a quarter of the output values are -1, and the remaining three quarters are +1.

PulseWaveFigure5.gif

This is cool, but the resulting sound is a bit static. It would be better if we use an AudioParam to modulate the pulse-width.

Adding modulation of the pulse width

The Web Audio API doesn’t allow you to create AudioParam object directly … so we’re going to be devious again – and borrow an AudioParam from the GainNode.

The following code adds a new "createPulseOscillator" function to the AudioContext – and exposes a "width" parameter that can be modulated:

var ac=new (
  window.AudioContext||
  window.webkitAudioContext||
  function() { throw "Your browser does not support Web Audio API"; }
)();

//Pre-calculate the WaveShaper curves so that we can reuse them.
var pulseCurve=new Float32Array(256);
for(var i=0;i<128;i++) {
  pulseCurve[i]= -1;
  pulseCurve[i+128]=1;
}
var constantOneCurve=new Float32Array(2);
constantOneCurve[0]=1;
constantOneCurve[1]=1;

//Add a new factory method to the AudioContext object.
ac.createPulseOscillator=function(){
  //Use a normal oscillator as the basis of our new oscillator.
  var node=this.createOscillator();
  node.type="sawtooth";

  //Shape the output into a pulse wave.
  var pulseShaper=ac.createWaveShaper();
  pulseShaper.curve=pulseCurve;
  node.connect(pulseShaper);

  //Use a GainNode as our new "width" audio parameter.
  var widthGain=ac.createGain();
  widthGain.gain.value=0; //Default width.
  node.width=widthGain.gain; //Add parameter to oscillator node.
  widthGain.connect(pulseShaper);

  //Pass a constant value of 1 into the widthGain – so the "width" setting
  //is duplicated to its output.
  var constantOneShaper=this.createWaveShaper();
  constantOneShaper.curve=constantOneCurve;
  node.connect(constantOneShaper);
  constantOneShaper.connect(widthGain);

  //Override the oscillator's "connect" and "disconnect" method so that the
  //new node's output actually comes from the pulseShaper.
  node.connect=function() {
    pulseShaper.connect.apply(pulseShaper, arguments);
  }
  node.disconnect=function() {
    pulseShaper.disconnect.apply(pulseShaper, arguments);
  }

  return node;
}

We pass a constant value of +1 into the "widthGain" node. This means that whatever we do to its "gain" parameter will be reflected onto the node’s output. We attach the "gain" parameter to the oscillator node so that it becomes part of the oscillator’s interface.

Have a play - then feel free to incorporate these techniques in your own code.

Useful links

I found the following links useful for constructing this project:

License

Copyright (c) 2014 Andy Harman and Pendragon Software Limited.

Released under the MIT License.

You can’t perform that action at this time.