Restart oscillator in javascript - 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.

Related

Is it possible to avoid the "AudioContext was not allowed to start" alert?

I faced a browser policy problem "The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page."
I wanted to execute that below code when page loaded.
So, I tried to mock the user click gesture using a hidden button and load event listener but failed.
Is it possible??
let my_array = [];
function my_function() {
let audioCtx = new (window.AudioContext || window.webkitAudioContext)();
let analyser = audioCtx.createAnalyser();
let oscillator = audioCtx.createOscillator();
oscillator.type = "triangle"; // Set oscillator to output triangle wave
oscillator.connect(analyser); // Connect oscillator output to analyser input
let gain = audioCtx.createGain();
let scriptProcessor = audioCtx.createScriptProcessor(4096, 1, 1);
analyser.connect(scriptProcessor); // Connect analyser output to scriptProcessor input
scriptProcessor.connect(gain); // Connect scriptProcessor output to gain input
gain.connect(audioCtx.destination); // Connect gain output to audiocontext destination
gain.gain.value = 0; // Disable volume
scriptProcessor.onaudioprocess = function (bins) {
bins = new Float32Array(analyser.frequencyBinCount);
analyser.getFloatFrequencyData(bins);
for (var i = 0; i < bins.length; i = i + 1) {
my_array.push(bins[i]);
}
analyser.disconnect();
scriptProcessor.disconnect();
gain.disconnect();
};
// audioCtx.resume().then(() => {
// oscillator.start(0);
// });
oscillator.start(0);
}

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.

Web Audio API split mono to stereo

Lets say I have a source node that's connected to the destination node.
Even if the audio is mono, I want to be able to control each ear's volume independently, like I can do when I have stereo audio with splitter and merger node.
Already tried to use splitter and merger nodes on the mono source node, but right channel comes out empty.
example for stereo:
var audioCtx = new AudioContext();
var source = audioCtx.createMediaElementSource(myAudio);
var gainNodeL = audioCtx.createGain();
var gainNodeR = audioCtx.createGain();
var splitter = audioCtx.createChannelSplitter(2);
var merger = audioCtx.createChannelMerger(2);
source.connect(splitter);
splitter.connect(gainNodeL, 0);
splitter.connect(gainNodeR, 1);
gainNodeL.connect(merger, 0, 0);
gainNodeR.connect(merger, 0, 1);
merger.connect(audioCtx.createMediaStreamDestination());
When I do this with mono audio, the right channel comes out empty.
If a signal is only mono (or in other words its channelCount is 1) the ChannelSplitterNode is not necessary. I modified the example a bit. It does now split the mono signal of an Oscillator.
var audioCtx = new AudioContext();
var oscillator = audioCtx.createOscillator();
var gainNodeL = audioCtx.createGain();
var gainNodeR = audioCtx.createGain();
var merger = audioCtx.createChannelMerger(2);
oscillator.connect(gainNodeL);
oscillator.connect(gainNodeR);
gainNodeL.connect(merger, 0, 0);
gainNodeR.connect(merger, 0, 1);
merger.connect(audioCtx.destination);
oscillator.start();
function left () {
gainNodeL.gain.value = 1;
gainNodeR.gain.value = 0;
}
function right () {
gainNodeL.gain.value = 0;
gainNodeR.gain.value = 1;
}
function center () {
gainNodeL.gain.value = 1;
gainNodeR.gain.value = 1;
}

WebAudio FM modulation

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.

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