robert
robert

Reputation: 5283

How can I set a phase offset for an OscillatorNode in the Web Audio API?

I'm trying to implement Stereo Phase as it is described here: http://www.image-line.com/support/FLHelp/html/plugins/3x%20OSC.htm

"Stereo Phase (SP) - Allows you to set different phase offset for the left and right channels of the generator. The offset results in the oscillator starting at a different point on the oscillator's shape (for example, start at the highest value of the sine function instead at the zero point). Stereo phase offset adds to the richness and stereo panorama of the sound produced."

I'm trying to achieve this for an OscillatorNode. My only idea is to use createPeriodicWave (https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#dfn-createPeriodicWave) However, the description of create periodic wave from the specification is above my understanding and I have not found any examples via Google.

Any help in deciphering the description of createPeriodicWave would be helpful as would any other ideas about how to achieve this effect.

Thanks!

Upvotes: 6

Views: 3067

Answers (6)

Flarp
Flarp

Reputation: 139

With Chrome 66 adding AudioWorklet's, you can write sound processing programs the same way as the now deprecated ScriptProcessorNode.

I made a convenience library using this that's a normal WebAudio API OscillatorNode that can also have its phase (among other things) varied. You can find it here

const context = new AudioContext()
context.audioWorklet.addModule("worklet.js").then(() => {
  const osc = new BetterOscillator(context)
  osc.parameters.get("phase").value = Math.PI / 4
  osc.connect(context.destination)
  osc.start()
}

Upvotes: 0

Raúl Avila Solano
Raúl Avila Solano

Reputation: 519

function getTriangleWave(imag, n) {
    for (var i = 1; i < n; i+=2) {
      imag[i] = (8*Math.sin(i*Math.PI/2))/(Math.pow((Math.PI*i), 2));
    }
    return imag;
}

Upvotes: 0

Lucas van Heerikhuizen
Lucas van Heerikhuizen

Reputation: 105

By the looks of this article on Wolfram the triangle wave can be established like this:

/* Triangle */
for(x=1;x<n;x+=2) 
    imag[x] = 8.0 / Math.pow(Math.PI, 2) * Math.pow(-1, (x-1)/2) / Math.pow(x, 2) * Math.sin(Math.PI * x);

Also helpful by the way is the Wikipedia page that actually shows how the Fourier constructions work.

Upvotes: 0

Lucas van Heerikhuizen
Lucas van Heerikhuizen

Reputation: 105

Mcclellan and others,

This answer helped and subsequently warped me into the world of Fourier. With the help of a page on the subject and some Wikipedia, I think I got the square and sawtooth patterns down, but the triangle pattern still eludes me. Does anyone know?

It indeed gives you the ability to phase shift, as this article by Nick Thompson explains (although he calls the AudioContext methods differently, but the principle is the same).

As far as the square and sawtooth patterns go:

var n = 4096;
var real = new Float32Array(n);
var imag = new Float32Array(n);
var ac = new AudioContext();
var osc = ac.createOscillator();

/* Pick a wave pattern */

/* Sine 
imag[1] = 1; */

/* Sawtooth 
for(x=1;x<n;x++)
    imag[x] = 2.0 / (Math.pow(-1, x) * Math.PI * x); */

/* Square */
for(x=1;x<n;x+=2)
    imag[x] = 4.0 / (Math.PI * x);

var wave = ac.createPeriodicWave(real, imag);

osc.setPeriodicWave(wave);  
osc.connect(ac.destination);
osc.start();
osc.stop(2); /* Have it stop after 2 seconds */

This will play the activated pattern, the square pattern in this case. What would the triangle formula look like?

Upvotes: 5

rmcclellan
rmcclellan

Reputation: 446

A simple way to fake it would be to add separate delay nodes to the left and right channels, and give them user-controlled delay values. This would be my approach and will have more-or-less the same effect as a phase setting.

If you want to use createPeriodicWave, unfortunately you'll probably have to understand the somewhat difficult math behind it.

Basically, you'll first have to represent your waveform as a sum of sine wave "partials". All periodic waves have some representation of this form. Then, once you've found the relative magnitudes of each partial, you'll have to phase shift them separately for left and right channels by multiplying each by a complex number. You can read more details about representing periodic waves as sums of sine waves here: http://music.columbia.edu/cmc/musicandcomputers/chapter3/03_03.php

Using createPeriodicWave has a significant advantage over using a BufferSourceNode: createPeriodicWave waveforms will automatically avoid aliasing. It's rather difficult to avoid aliasing if you're generating the waveforms "by hand" in a buffer.

Upvotes: 3

zya
zya

Reputation: 850

I do not think that it would be possible to have a phase offset using a OscillatorNode.

A way to do that would be to use context.createBuffer and generate a sine wave buffer (or any type of wave that you want) and set it as the buffer for a BufferSourceNode and then use the offset parameter in its start() method. But you need to calculate the sample offset amount in seconds.

var buffer = context.createBuffer(1,1024,44100);

var data = buffer.getChannelData(0);

for(var i=0; i < data.length; i++){
    //generate waveform
}

var osc = context.createBufferSourceNode();
osc.buffer = buffer;
osc.loop = true;

osc.connect(context.destination);
osc.start(time, offset in seconds);

Upvotes: 2

Related Questions