WebAudio FM modulation - javascript

I'm trying to modulate saw frequency with the pulse from the slow oscillator sine. What am I doing wrong? jsFiddle here: https://jsfiddle.net/06ua9zLo/
window.AudioContext = window.AudioContext||window.webkitAudioContext;
var context = new AudioContext();
var saw = context.createOscillator(),
sine = context.createOscillator(),
sineGain = context.createGain();
//set up our oscillator types
saw.type = 'sawtooth';
sine.type = 'sine';
//set the amplitude of the modulation
sineGain.gain.value = 10;
//connect the dots
sine.connect(sineGain);
sineGain.connect(saw.frequency);
saw.connect(context.destination);
sine.start();
saw.start();

Your setup is fine. Just try changing the frequency of the modulator and add a little more gain, and you'll see that you're in fact doing FM. For example:
sine.frequency.value = 15;
sineGain.gain.value = 100;
will definitely tell you that it works.

Related

cannot call start() more than once [duplicate]

I'm trying to make oscillator play when mouse is on canvas and stop when it's not. However, with current code, it works just once after loading page, when mouse is on canvas second time error occurs:
"Uncaught InvalidStateError: Failed to execute 'start' on 'OscillatorNode': cannot call start more than once.
var ac = new window.AudioContext() || new window.webkitAudioContext();
var osc = ac.createOscillator();
var canvas1 = document.getElementById("canvas1");
canvas1.addEventListener("mouseover", playosc);
canvas1.addEventListener("mouseout", stoposc);
function playosc() {
osc.frequency.value = 440;
osc.connect(ac.destination);
osc.start();
}
function stoposc() {
osc.stop();
}
How to restart oscillator? Thanks.
You would need to create an Oscillator object each time as the OscillatorNodes are not reusable. Example:
var canvas1 = document.getElementById("canvas1");
canvas1.addEventListener("mouseover", playosc);
canvas1.addEventListener("mouseout", stoposc);
var ac = new window.AudioContext() || new window.webkitAudioContext();
var osc;
function playosc() {
osc = ac.createOscillator()
osc.frequency.value = 440;
osc.connect(ac.destination);
osc.start();
}
function stoposc() {
osc.stop();
}
Please refer to this excellent blog post for more guidance.
A simpler way is to have the oscillator start and connect to a gain node which you modulate between 0 and 1 to determine if the oscillator outputs or not.
var ac = new window.AudioContext() || new window.webkitAudioContext();
var osc = ac.createOscillator();
var gain = ac.createGain();
var canvas1 = document.getElementById("canvas1");
canvas1.addEventListener("mouseover", playosc);
canvas1.addEventListener("mouseout", stoposc);
gain.gain.value = 0;
osc.connect(ac.gain);
gain.connect(ac.destination);
osc.start();
function playosc() {
osc.frequency.value = 440;
gain.gain.value = 1;
}
function stoposc() {
gain.gain.value = 0;
}
You can also (and should) use the ramping. Plus you can modulate the gain further (such as based on mouse position within the canvas) and so on.
Yes it will use more CPU usage (since the oscillator is always running). However gain nodes of values at 0 cheat and don't actually multiply, they just spit out arrays of zeros.

Wrong oscillator frequency set by ConstantSourceNode

I am creating a synth voice which contains three oscillators as you can see in the following snippet.
const ctx = new AudioContext();
const osc1 = ctx.createOscillator();
osc1.type = "sawtooth";
osc1.start();
const osc2 = ctx.createOscillator();
osc2.type = "sawtooth";
osc2.detune.setValueAtTime(1200, ctx.currentTime);
osc2.start();
const osc3 = ctx.createOscillator();
osc3.type = "sawtooth";
osc3.detune.setValueAtTime(1900, ctx.currentTime);
osc3.start();
const gain = ctx.createGain();
gain.gain.value = 0.2;
gain.connect(ctx.destination);
osc1.connect(gain);
osc2.connect(gain);
osc3.connect(gain);
osc1.frequency.value = 160;
osc2.frequency.value = 160;
osc3.frequency.value = 160;
To play a melody, I would like to control the frequency of all the oscillators by one AudioParam. I found that it should be done via ConstantSourceNode. But for some reason it is now playing different frequency then I set.
const ctx = new AudioContext();
const osc1 = ctx.createOscillator();
osc1.type = "sawtooth";
osc1.start();
const osc2 = ctx.createOscillator();
osc2.type = "sawtooth";
osc2.detune.setValueAtTime(1200, ctx.currentTime);
osc2.start();
const osc3 = ctx.createOscillator();
osc3.type = "sawtooth";
osc3.detune.setValueAtTime(1900, ctx.currentTime);
osc3.start();
const gain = ctx.createGain();
gain.gain.value = 0.2;
gain.connect(ctx.destination);
osc1.connect(gain);
osc2.connect(gain);
osc3.connect(gain);
const control = ctx.createConstantSource();
control.connect(osc1.frequency);
control.connect(osc2.frequency);
control.connect(osc3.frequency);
control.start();
control.offset.value = 160;
I noticed that minValue and maxValue are different for oscillator's frequency AudioParam and ConstantSource's offset AudioParam. Is it somehow scaled? Did I miss something?
I think the problem is that when you connect an AudioNode to an AudioParam the output signal of the AudioNode gets mixed with the intrinsic value of the AudioParam.
In you particular example this means that the actual frequency will be 600. The default frequency of an Oscillator is 440. This gets mixed with the value that comes from the ConstantSourceNode.
440 + 160 === 600
To achieve your desired result you can set the value of each oscillator to zero.
osc1.frequency.setValueAtTime(0, ctx.currentTime);
osc2.frequency.setValueAtTime(0, ctx.currentTime);
osc3.frequency.setValueAtTime(0, ctx.currentTime);
Here is a link to the part of the spec which describes this behavior: https://webaudio.github.io/web-audio-api/#dom-audionode-connect-destinationparam-output
The default for an OscillatorNode frequency is 440, but you're setting the default offset to 160.

Restart oscillator in javascript

I'm trying to make oscillator play when mouse is on canvas and stop when it's not. However, with current code, it works just once after loading page, when mouse is on canvas second time error occurs:
"Uncaught InvalidStateError: Failed to execute 'start' on 'OscillatorNode': cannot call start more than once.
var ac = new window.AudioContext() || new window.webkitAudioContext();
var osc = ac.createOscillator();
var canvas1 = document.getElementById("canvas1");
canvas1.addEventListener("mouseover", playosc);
canvas1.addEventListener("mouseout", stoposc);
function playosc() {
osc.frequency.value = 440;
osc.connect(ac.destination);
osc.start();
}
function stoposc() {
osc.stop();
}
How to restart oscillator? Thanks.
You would need to create an Oscillator object each time as the OscillatorNodes are not reusable. Example:
var canvas1 = document.getElementById("canvas1");
canvas1.addEventListener("mouseover", playosc);
canvas1.addEventListener("mouseout", stoposc);
var ac = new window.AudioContext() || new window.webkitAudioContext();
var osc;
function playosc() {
osc = ac.createOscillator()
osc.frequency.value = 440;
osc.connect(ac.destination);
osc.start();
}
function stoposc() {
osc.stop();
}
Please refer to this excellent blog post for more guidance.
A simpler way is to have the oscillator start and connect to a gain node which you modulate between 0 and 1 to determine if the oscillator outputs or not.
var ac = new window.AudioContext() || new window.webkitAudioContext();
var osc = ac.createOscillator();
var gain = ac.createGain();
var canvas1 = document.getElementById("canvas1");
canvas1.addEventListener("mouseover", playosc);
canvas1.addEventListener("mouseout", stoposc);
gain.gain.value = 0;
osc.connect(ac.gain);
gain.connect(ac.destination);
osc.start();
function playosc() {
osc.frequency.value = 440;
gain.gain.value = 1;
}
function stoposc() {
gain.gain.value = 0;
}
You can also (and should) use the ramping. Plus you can modulate the gain further (such as based on mouse position within the canvas) and so on.
Yes it will use more CPU usage (since the oscillator is always running). However gain nodes of values at 0 cheat and don't actually multiply, they just spit out arrays of zeros.

Surround sound (5.1) Web Audio support

I'm trying to create simple aplication to play stereo / mono music in all 5.1 channels. After studying Web audio specification I spend 4 hours to try code it. Unfortunately I got nowhere. Audio is playing only over 2 channels. If I set merger.channelCountMode = "explicit" it plays only from center channel.
If I set merger.channelInterpretation = "discrete"; it plays only from left channel. What I'm doing wrong? Thank you very much.
My code:
var audio;
function PlaySurround(){
context = new AudioContext();
audio = new Audio();
audio.src = "a.mp3";
var source = context.createMediaElementSource(audio);
context.destination.channelCount = 6;
audio.currentTime = Math.random() * 200;
//Create a splitter to "separete" the stereo audio data to two channels.
var splitter = context.createChannelSplitter(6);
splitter.channelCount = 6;
//splitter.channelInterpretation = "discrete";
//splitter.channelCountMode = "explicit";
console.log(splitter);
//Connect your source to the splitter (usually, you will do it with the last audio node before context destination)
source.connect(splitter);
//Create two gain nodes (one for each side of the stereo image)
var panLeft = context.createGain();
var panRight = context.createGain();
var panLeftSurround = context.createGain();
var panRightSurround = context.createGain();
var panCenter = context.createGain();
var panSubwoofer = context.createGain();
//Connect the splitter channels to the Gain Nodes that we've just created
splitter.connect(panLeft, 0);
splitter.connect(panRight, 1);
splitter.connect(panCenter, 2);
splitter.connect(panSubwoofer, 3);
splitter.connect(panLeftSurround, 4);
splitter.connect(panRightSurround, 5);
panLeft.gain.value = 1;
panRight.gain.value = 1;
panLeftSurround.gain.value = 1;
panRightSurround.gain.value = 1;
panCenter.gain.value = 1;
panSubwoofer.gain.value = 1;
//Create a merger node, to get both signals back together
var merger = context.createChannelMerger(6);
merger.channelCount = 6;
//merger.channelInterpretation = "discrete";
merger.channelCountMode = "explicit";
console.log(merger);
panLeft.connect(merger, 0, 0);
panRight.connect(merger, 0, 1);
panCenter.connect(merger, 0, 2);
panSubwoofer.connect(merger, 0, 3);
panLeftSurround.connect(merger, 0, 4);
panRightSurround.connect(merger, 0, 5);
//Connect the Merger Node to the final audio destination (your speakers)
merger.connect(context.destination);
source.mediaElement.play();
}

Audio Synthesis in Javascript: Can I use an oscillator node to adjust the frequency of a biquad filter?

I've been experimenting with sound design in the web audio API, and run into a point of frustration: I'd like to connect an oscillator to the frequency value on a lowpass biquad filter, which can easily be done to a gain node's gain value. Thus far, I've had no success, but no helpful error messages, either.
var ctx = new window.webkitAudioContext();
var currentKeysDown = [];
var filter = ctx.createBiquadFilter();
filter.connect(ctx.destination);
filter.type = 'lowpass';
filter.frequency.value = 400;
var osc = ctx.createOscillator();
osc.connect(filter);
osc.type = 'square';
osc.frequency.value = 200;
osc.start(0)
//Osc2 should modify the filter's frequency value.
var osc2 = ctx.createOscillator();
osc2.connect(filter.frequency);
osc2.frequency.value = 1;
osc2.start(0)
Does anyone know how to make this work, or perhaps there is some other work around?
Thanks!

Categories