Skip to content

Megaemce/Oscillator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Oscillator recreated using AudioWorkletProcessor

Build as an extension of OscillatorNode which by default doesn't allow user to manipulate all its attributes. By using AudioWorkletProcessor one can use any of the AudioParam methods to change oscillator type or any other exposed parameter.

This Oscillator implements (in addition to the original node):

Exposed parameters

{
    name: "signalType",
    description: "Types of waveform. 
        0: triangle, 
        1: pulse, 
        2: sawtooth, 
        3: sine, 
        4: white noise, 
        5: pink noise, 
        6: brown noise",
    defaultValue: 1,
    minValue: 0,
    maxValue: 6,
},
{
    name: "frequency",
    description: "Number of waveform cycles per second [Hz]",
    defaultValue: 400,
    minValue: 0,
    maxValue: 0.5 * sampleRate,
},
{
    name: "phaseOffset",
    description: "Phase offset of the waveform",
    defaultValue: 0,
    minValue: 0,
    maxValue: 1,
},
{
    name: "vibrato",
    description: "Amount of pulsating change of pitch",
    defaultValue: 0,
    minValue: 0,
    maxValue: 1,
},
{
    name: "duty",
    description: "Duty cycle of the pulse wave. Not working with other waves",
    defaultValue: 0.5,
    minValue: 0,
    maxValue: 1,
},
{
    name: "sync",
    description: "Frequency to hard sync to in Hz",
    defaultValue: 0,
    minValue: 0,
},
{
    name: "amplitude",
    description: "Amplitude of generated waveform",
    defaultValue: 1,
    minValue: 0,
},

Hence this Oscillator expands the processor by adding a static parameterDescriptors getter all listed parameters can be accessed via parameters property of AudioWorkletNode like so:

const oscillator = new AudioWorkletNode(audioContext, "oscillator");
const type = oscillator.parameters.get("signalType");

Example

Let's create sound similar to acceleration and gear shifting in classical Amiga game like Crazy Cars 3.

Try it online! 🏎️💨

try {
    audioContext = new AudioContext();
} catch (e) {
    alert("The Web Audio API is not supported in this browser.");
}

audioContext.audioWorklet.addModule("./oscillator.js").then(() => {
    const oscillator = new AudioWorkletNode(audioContext, "oscillator");
    oscillator.connect(audioContext.destination);

    const type = oscillator.parameters.get("signalType");
    const sync = oscillator.parameters.get("sync");
    const duty = oscillator.parameters.get("duty");
    const vibrato = oscillator.parameters.get("vibrato");
    const amplitude = oscillator.parameters.get("amplitude");
    const phaseOffset = oscillator.parameters.get("phaseOffset");

    const time = audioContext.currentTime;

    // changing type from default (pulse), to sawtooth and then to triangle
    type.setValueAtTime(2, time + 0.5);
    type.setValueAtTime(0, time + 3);

    sync.linearRampToValueAtTime(440, time + 1);
    sync.linearRampToValueAtTime(0, time + 4);

    duty.linearRampToValueAtTime(1, time + 2);
    duty.linearRampToValueAtTime(0.2, time + 4);

    vibrato.exponentialRampToValueAtTime(0.8, time + 2);
    vibrato.linearRampToValueAtTime(0, time + 3);

    amplitude.setValueAtTime(0, time);
    amplitude.linearRampToValueAtTime(0.8, time + 2);
    amplitude.setValueAtTime(0.4, time + 2.5);
    amplitude.linearRampToValueAtTime(0.2, time + 4);

    phaseOffset.linearRampToValueAtTime(0.01, time + 1);
    phaseOffset.linearRampToValueAtTime(0, time + 1.5);

    setTimeout(() => audioContext.close(), 4000);
});

Technology & limitation

Currently AudioWorkletProcessor works fine in all major browers.

Thanks to Flarp for creating inital idea.