I'm trying to use the web audio API to create an audio stream with the left and right channels generated with different oscillators. The output of the left channel is correct, but the right channel is 0. Based on the spec, I can't see what I'm doing wrong.
Tested in Chrome dev.
Code:
var context = new AudioContext();
var l_osc = context.createOscillator();
l_osc.type = "sine";
l_osc.frequency.value = 100;
var r_osc = context.createOscillator();
r_osc.type = "sawtooth";
r_osc.frequency.value = 100;
// Combine the left and right channels.
var merger = context.createChannelMerger(2);
merger.channelCountMode = "explicit";
merger.channelInterpretation = "discrete";
l_osc.connect(merger, 0, 0);
r_osc.connect(merger, 0, 1);
var dest_stream = context.createMediaStreamDestination();
merger.connect(dest_stream);
// Dump the generated waveform to a MediaStream output.
l_osc.start();
r_osc.start();
var track = dest_stream.stream.getAudioTracks()[0];
var plugin = document.getElementById('plugin');
plugin.postMessage(track);
The channelInterpretation means the merger node will mix the stereo oscillator connections to two channels each - but then because you have an explicit channelCountMode, it's stacking the two-channels-per-connection to get four channels and (because it's explicit) just dropping the top two channels. Unfortunately the second two channels are the two channels from the second input - so it loses all channels from the second connection.
In general, you shouldn't need to mess with the channelCount interpretation stuff, and it will do the right thing for stereo.
Related
I have a stereo mp3 file. Is it possible using Web Audio to play the file so that channel 1 is only heard through the left speaker and channel 2 is only heard through the right speaker?
I've played with splitter, panner and merger nodes (channelCounts, Interpretation, CountMode) however the result always seem to be mixed somewhere e.g. if I attach a blank source as a second input to the merger, I still always hear channel 1 clearly in the right speaker.
See below as an example I have tried - with various permutations of connect ins and outs.
audioCtx = new window.AudioContext();
var source = audioCtx.createMediaElementSource(myAudio);
var blank = audioCtx.createBufferSource();
var splitter = audioCtx.createChannelSplitter(2);
source.connect(splitter);
var panner = audioCtx.createPanner();
panner.setPosition(-1,0,0);
splitter.connect(panner, 0, 0);
var merger = audioCtx.createChannelMerger(2);
panner.connect(merger, 0, 0);
blank.connect(merger, 0, 1);
merger.connect(audioCtx.destination);
Locked. There are disputes about this question’s content being resolved at this time. It is not currently accepting new answers or interactions.
I have created a Web Audio API Biquad filters (Lowpass, Highpass etc) using JavaScript. The application works (I think....) well, it's displaying on the canvas without errors so i'm guessing it does. Anyway, I'm not a pro at JavaScript, far from it. I showed someone a small snippet of my code and they said it was very messy and that i'm not building my audio graph properly for example, not connecting all of the nodes from start to finish.
Now I know that the Source connects to Gain. Gain connects to Filter. Filter connects to Destination. I tried to look at it but I can't figure out what's wrong and how to fix it.
JavaScript
// Play the sound.
function playSound(buffer) {
aSoundSource = audioContext.createBufferSource(); // creates a sound source.
aSoundSource.buffer = buffer; // tell the source which sound to play.
aSoundSource.connect(analyser); // Connect the source to the analyser.
analyser.connect(audioContext.destination); // Connect the analyser to the context's destination (the speakers).
aSoundSource.start(0); // play the source now.
//Create Filter
var filter = audioContext.createBiquadFilter();
//Create the audio graph
aSoundSource.connect(filter);
//Set the gain node
gainNode = audioContext.createGain();
aSoundSource.connect(gainNode); //Connect the source to the gain node
gainNode.connect(audioContext.destination);
//Set the current volume
var volume = document.getElementById('volume').value;
gainNode.gain.value = volume;
//Create and specify parameters for Low-Pass Filter
filter.type = "lowpass"; //Low pass filter
filter.frequency.value = 440;
//End Filter
//Connect source to destination(speaker)
filter.connect(audioContext.destination);
//Set the playing flag
playing = true;
//Clear the spectrogram canvas
var canvas = document.getElementById("canvas2");
var context = canvas.getContext("2d");
context.fillStyle = "rgb(255,255,255)";
context.fillRect (0, 0, spectrogramCanvasWidth, spectrogramCanvasHeight);
// Start visualizer.
requestAnimFrame(drawVisualisation);
}
Because of this, my volume bar thingy has stopped working. I also tried doing "Highpass filter" but it's displaying the same thing. I'm confused and have no one else to ask. By the way, the person I asked didn't help but just said it's messy...
Appreciate all of the help guys and thank you!
So, there's a lot of context missing because of how you posted this - e.g. you don't have your drawVisualisation() code, and you don't explain exactly what you mean by your "volume bar thingy has stopped working".
My guess is that it's just that you have a graph that connects your source node to the output (audiocontext.destination) three times in parallel - through the analyser (which is a pass-thru, and is connected to the output), through the filter, AND through the gain node. Your analyser in this case would show the unfiltered signal output only (you won't see any effect from the the filter, because that's a parallel signal path), and the actual output is summing three chains of the source node (one through the filter, one through the analyser, one through the gain node) - which might have some odd phasing effects, but will also triple the volume (approximately) and quite possibly clip.
Your graph looks like this:
source → destination
↳ filter → destination
↳ gain → destination
What you probably want is to connect each of these nodes in series, like this:
source → filter → gain → destination
I think you want something like this:
// Play the sound.
function playSound(buffer) {
aSoundSource = audioContext.createBufferSource(); // creates a sound source.
aSoundSource.buffer = buffer; // tell the source which sound to play.
//Create Filter
var filter = audioContext.createBiquadFilter();
//Create and specify parameters for Low-Pass Filter
filter.type = "lowpass"; //Low pass filter
filter.frequency.value = 440;
//Create the gain node
gainNode = audioContext.createGain();
//Set the current volume
var volume = document.getElementById('volume').value;
gainNode.gain.value = volume;
//Set up the audio graph
aSoundSource.connect(filter);
filter.connect(gainNode);
gainNode.connect(analyser);
analyser.connect(audioContext.destination);
aSoundSource.start(0); // play the source now.
aSoundSource.connect(gainNode); //Connect the source to the gain node
//Set the playing flag
playing = true;
//Clear the spectrogram canvas
var canvas = document.getElementById("canvas2");
var context = canvas.getContext("2d");
context.fillStyle = "rgb(255,255,255)";
context.fillRect (0, 0, spectrogramCanvasWidth, spectrogramCanvasHeight);
// Start visualizer.
requestAnimFrame(drawVisualisation);
}
I'm currently trying to create an audio visualisation using an web audio api, namely I'm attempting to produce lissajou figures from a given audio source.
I came across this post, but I'm missing some preconditions. How can I get the time domain data for the left / right channels? Currently it seems I'm only getting the merged data.
Any help or hint would be much appreciated.
$(document).ready(function () {
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var audioElement = document.getElementById('audioElement');
var audioSrc = audioCtx.createMediaElementSource(audioElement);
var analyser = audioCtx.createAnalyser();
// Bind analyser to media element source.
audioSrc.connect(analyser);
audioSrc.connect(audioCtx.destination);
//var timeDomainData = new Uint8Array(analyser.frequencyBinCount);
var timeDomainData = new Uint8Array(200);
// loop and update time domain data array.
function renderChart() {
requestAnimationFrame(renderChart);
// Copy frequency data to timeDomainData array.
analyser.getByteTimeDomainData(timeDomainData);
// debugging: print to console
console.log(timeDomainData);
}
// Run the loop
renderChart();
});
The observation is correct, the waveform is the down-mixed result. From the current spec (my emphasis):
Copies the current down-mixed time-domain (waveform) data into the
passed unsigned byte array. [...]
To get around this you could use a channel splitter (createChannelSplitter()) and assign each channel to two separate analyzer nodes.
For more details on createChannelSplitter() see this link.
I have a simple synth that plays a note for some length of time:
// Creating audio graph
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var oscillator = audioCtx.createOscillator();
var gainNode = audioCtx.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
// Setting parameters
oscillator.type = "sine";
oscillator.frequency.value = 2500;
// Run audio graph
var currentTime = offlineCtx.currentTime;
oscillator.start(currentTime);
oscillator.stop(currentTime + 1);
How can I get the PCM data of the sound the synthesiser makes? I've managed to do this with audio samples by using decodeAudioData, but I can't find an equivalent for an audio graph that isn't based on loading a sample.
I specifically want to render the audio graph with the OfflineAudioContext since I only care about retrieving the PCM data as fast as possible.
Thanks!
You say you want to use an offline context and then you don't actually use an offline context. So you should do
var offlineCtx = new OfflineAudioContext(nc, length, rate)
where nc = number of channels, length is the number of samples, and rate is the sample rate you want to use.
Create your graph, start everything and then do
offlineCtx.startRendering().then(function (buffer) {
// buffer has the PCM data you want. Save it somewhere,
// or whatever
})
(I'm not sure all browsers support promises from an offline context. If not, use offlineCtx.oncomplete to get the data. See the spec.)
Eventually I found an answer here: http://www.pp4s.co.uk/main/tu-sms-audio-recording.html#co-tu-sms-audio-recording__js but you will not like it. Apparently, the Audio API is not yes standardized enough for this to work on all browsers. So I have been able to run the code above in Firefox, but not Chrome.
Basic ideas:
use dest = ac.createMediaStreamDestination(); to get a destination
of the sound
use new MediaRecorder(dest.stream); to get a recorder
use the MediaRecorder ondataavailable and stop events to get the data and combine it into a Blob
I'm trying to create a custom panning control using the Web Audio API, but I can't get any sound to come out of the right channel using the channel splitter and merger nodes:
var context = new webkitAudioContext(),
destination = context.destination,
osc = context.createOscillator(),
gainL = context.createGainNode(),
gainR = context.createGainNode(),
splitter = context.createChannelSplitter(2),
merger = context.createChannelMerger(2);
osc.frequency.value = 500;
osc.connect(splitter);
splitter.connect(gainL, 0);
splitter.connect(gainR, 1);
gainL.connect(merger, 0, 0);
gainR.connect(merger, 0, 1);
osc.noteOn(0);
gainL.gain.value = 0.1;
gainR.gain.value = 0.5;
osc.noteOff(2);
merger.connect(destination);
Am I missing something obvious here? There's a JSBin preview of the above code here: http://jsbin.com/ayijoy/1/
I'm running Chrome v24.0.1312.57, just in case that's of any use.
My best guess is this happens because the Oscillator outputs a mono signal. Try using a stereo source and you should probably have more luck.
Edit: here's how you can pan a "mono" signal (bypass the splitter, since there is no stereo signal to split, and connect the oscillator directly to the two gains. Then connect the two mono signals to the merger after adjusting the gain for each channel) http://jsbin.com/ayijoy/16/