I am an Audio Engineer and just started JavaScript and HTML5, Specifically WEB Audio API.
I have this oscillator code which is connected to destination(output).
here is the code
I want to have a buttons to connect and disconnect the oscillator to destination
I managed to make it start but I can't disconnect it.
<html>
<head>
<script>
//creating the context
context = new webkitAudioContext(); //allways the first code for audio API
function osc1(frequency){ // Creating a function that has an oscillator going to gainNode and then going to the destination
//creating AudioNodes and AudioParams
//creating OscillatorNode
var oscillator = context.createOscillator(); //creating Node's as Variables
oscillator.type = 0; //0 is a sine wave
oscillator.noteOn(0); // turning on the oscillator
oscillator.frequency.value = frequency;// telling that frequency in () of the function equals to what
//creating GainNode
var gain = context.createGainNode(); // creating the Gain node
gain.gain.value = 1; // setting up the value for gain node
//Making the connections
oscillator.connect(gain); // connecting oscillator to gain
gain.connect(context.destination); // connecting gain to destination (speakers)
}; // now we have a function called osc1(we can put frequency in here) then we can re call
</script>
</head>
<body>
<input type="button" value="on" onClick="osc1(500);" />
</body>
</html>
I know that the code for disconnecting it is oscillator.disconnect();, but I don't know how to to execute it.
Maybe you want declare the oscillator variable outside the function:
var context = new webkitAudioContext();
var oscillator = context.createOscillator();
function osc1(frequency){
var button = document.getElementsByTagName('input')[0];
if (button.value === 'off') {
button.value = 'on';
oscillator.disconnect();
} else {
button.value = 'off';
// same as your original code (connect, noteOn...)
I'm working on this too.
You do oscillator.disconnect(0) where zero represents the output number.
I think that means if you connect the oscillator to a gain node and a filter node separately, gain would be disconnect(0) and filter would be disconnect(1).
I think keeping track of what outputs are what is our job, since there doesn't look like a built in way.
I would hope the spec changes so that we can pass disconnect() the actual node we want disconnected.
It's been mentioned already, but you need to expose the oscillator variable (possibly to the global scope) so that your off function can disconnect it (make sure you don't redeclare the oscillator variable inside the onclick handler).
Here's a working example: http://jsbin.com/iwetiy/1/edit
Related
I'm simply trying to read the FFT values of a 1000Hz sine wave in this code. However the console output displays '-128' a 1000 times. Why doesn't the analyzer node work in this?
window.onload = init;
var sourceNode;
function init(){
var context = new AudioContext();
var osc = context.createOscillator();
var analyser = context.createAnalyser();
var gain = context.createGain();
sourceNode = context.createBufferSource();
var amplitudeArray = new Float32Array(analyser.frequencyBinCount);
osc.frequency.value=1000;
osc.start();
gain.gain.value=0.07;
osc.connect(analyser);
analyser.connect(gain);
gain.connect(context.destination);
analyser.getFloatFrequencyData(amplitudeArray);
for(var i=0;i<amplitudeArray.length;i++){
console.log(amplitudeArray[i]);
}
}
You are basically asking the analyser for the FFT data right when the oscillator starts. At this point, the internal buffers are full of zeroes, so the output is -128 dB. That is, everything is zero.
Try waiting for a second before calling analyser.getFloatFrequencyData. You'll see that the output is not a constant -128.
In this particular case, because you're IMMEDIATELY requesting the data; although you've called osc.start(), the audio system hasn't processed any data yet, so when you call getFloatFrequencyData() it's full of empty data. (FloatFrequencyData is in decibels, so -128 is the noise floor - aka "zero").
If you had enough of a time gap between start() and the getFloatFrequencyData() call for some audio to be processed, I expect you'd see some data.
I'm using the online version of Scala-js-fiddle. So far, I've been able to successfully declare an Audio Context:
val ctx = js.Dynamic.newInstance(js.Dynamic.global.AudioContext)()
Now, I want to create an oscillator node. I tried (unsuccessfully):
val oscillator = ctx.js.Dynamic.global.createOscillator()
When I saved this, the Scala-js-fiddle said compilation was successful. However, I also had error messages. The main one was:
TypeError: Cannot read property 'Dynamic' of undefined
How can I properly create an oscillator node and set the value of its frequency using js.Dynamic?
In regular Javascript, I would simply write something like this:
var oscillator = ctx.createOscillator();
oscillator.frequency.value = 400;
Would I have to use js.Global.Function(...)? How would that work?
Solved! As it turns out, after creating an AudioContext like this
val ctx = js.Dynamic.newInstance(js.Dynamic.global.AudioContext)()
you can call the JavaScript methods normally:
val o = ctx.createOscillator()
I make a new oscillator for each note I play.
function playSound(freq, duration) {
var attack = 5,
decay = duration,
gain = context.createGain(),
osc = context.createOscillator();
gain.connect(context.destination);
gain.gain.setValueAtTime(0, context.currentTime);
gain.gain.linearRampToValueAtTime(0.1, context.currentTime + attack / 1000);
gain.gain.linearRampToValueAtTime(0, context.currentTime + decay / 1000);
osc.frequency.value = freq;
osc.type = "sine";
osc.connect(gain);
osc.start(0);
setTimeout(function() {
osc.stop(0);
osc.disconnect(gain);
gain.disconnect(context.destination);
}, decay)
}
The melody is played in a for loop, where playSound is called. When I click the pause button, I want to silence the melody and pause the for loop so that if I click the play button again, the melody resumes. How do I access all the current oscillators to disconnect them?
You can't, in this code.
1) There is, by design, no introspection of the node graph in the Web Audio API - it enables optimizing garbage collection, and optimizes for large numbers of nodes. Two potential solutions - either maintain a list of playing oscillators, or connect them all to a single gain node (that is, connect their envelope gain node to a "mixer" gain node), and then disconnect (and release references to) that gain node.
2) Not sure what you mean by "pause the for loop" - I presume you have a for loop wrapped around the play note method?
You can suspend the audio context.
const audioCtx = new AudioContext();
audioCtx.suspend();
audioCtx.resume();
lets say i wan't to have an app that has variable audio sources as audio tags like so:
<audio preload="auto" src="1.mp3" controls="" class="muzz"></audio>
<audio preload="auto" src="track.mp3" controls="" class="muzz"></audio>
Depending on which of them is played it should be passed to createMediaElementSource and then the sound would be sent to analyser and various things would be done with it, but it doesn't work:
var trackName;
//get the source of clicked track
$(".muzz").on("play", function(){
trackName = $(this).attr("src");
console.log("got a source: ", trackName);
audio = new Audio();
audio.src=trackName;
context = new AudioContext();
analyser = context.createAnalyser();
source = context.createMediaElementSource(audio);
source.connect(analyser);
analyser.connect(context.destination);
letsDraw();
});
the console log displays the correct source name, the letsDraw() method is supposed to draw a spectrogram of the audio playing:
function letsDraw(){
console.log("draw called");
window.requestAnimationFrame(letsDraw);
fbc_array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(fbc_array); //get frequency from the analyser node
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle="white";
ctx.font = "bold 12px Arial";
ctx.fillText("currently playing:" + trackName, 10, 20);//this works
bars = 150;
for(var i = 0; i < analyser.frequencyBinCount; i++){ //but this doesn't
/*fill the canvas*/
x = i *2;
barWidth = 1;
barHeight = -(fbc_array[i]/1.8);
//colours react to the frequency loudness
hue = parseInt(500 * (1 - (barHeight / 200)), 10);
ctx.fillStyle = 'hsl(' + hue + ',75%,50%)';
ctx.fillRect(x, canvas.height, barWidth, barHeight);
}
}
it was working fine with one set audio source, but fails with variable sources, any ideas would be very appreciated.
of course, no errors are even thrown in the console at all.
What I don't get is why you take the src and put that in a new Audio object, as you already have them.
It is also way better to create a source from both <audio> tags. You just create a function that runs on page load (so when everything on the page is ready so you won't get any errors about elements not yet existing etc.).
Before I start writing a piece of code, what do you expect to happen? Should it be possible to have both tags playing at the same time, or should one be stopped if you click play on the other? If it shouldn't play at the same time, you'd better make one <audio> tag and create two buttons which each set the src of the tag.
Another problem with your code is that you already have the <audio> elements, and when you want them to play you just create a new audio element and append the src to it.. What is the logic behind that?
EDIT:
Here is an example of using multiple sources with only one <audio> element.
The HTML code should look like this:
<audio id="player" src="" autoplay="" controls=""></audio>
<div id="buttons">
<!--The javascript code will generate buttons with which you can play the audio-->
</div>
Then you use this JS code:
onload = function () { //this will be executed when the page is ready
window.audioFiles = ['track1.mp3', 'track2.mp3']; //this is gonna be the array with all file names
window.player = document.getElementById('player');
window.AudioContext = window.AudioContext || window.webkitAudioContext;
context = new AudioContext();
source = context.createMediaElementSource(player);
analyser = context.createAnalyser();
source.connect(analyser);
analyser.connect(context.destination);
//now we take all the files and create a button for every file
for (var x in audioFiles) {
var btn = document.createElement('button');
btn.innerHTML = audioFiles[x];
btn.onclick = function () {
player.src = audioFiles[x];
//so when the user clicks the button, the new source gets appended to the audio element
}
document.getElementById('buttons').appendChild(btn);
}
}
Hope the comments explain it good enough
EDIT2:
You want to know how to do this for multiple elements. What you want to do is to create all the audio elements when the page loads, and create all sources for it. This will decrease mess when starting to play audio. The code is here.
All you need to do is have the for loop that runs for every media file you have, which it will create an audio element for with the appropriate source, and then create a sourcenode for that (createMediaElementSource), and connect that sourcenode to the analyser.
I also want to say something about your visualiser code though. If you do not override the font, color or anything, you don't need to execute that every animationframe. Once on init is enough.
At the top of your first code block, try this:
var trackName,
context = new AudioContext();
And remove context = new AudioContext(); from the click handler.
You should only have one AudioContext for the page.
I have an oscillator to generate the frequencies of a keyboard. It all works when I output to speakers, but as well as outputting to speakers I would like to buffer it so that I can turn it into base 64 and use again later. The only examples of this I have seen use xhr which I do not need as obviously I want to be able to just add a node into the modular routing to take input, store it in an array, then output to the hardware.
Something like this:
var osc = ctx.createOscillator();
osc.type = 3;
osc.frequency.value = freq;
osc.connect(buffer);
buffer.connect(ctx.destination);
Is this possible?
Have you considered utilizing a ScriptProcessorNode?
See: http://www.w3.org/TR/webaudio/#ScriptProcessorNode
You would attach an eventListener to this node, allowing you to capture arrays of audio samples as they pass through. You could then save these buffers and manipulate them as you wish.
Have you checked out RecorderJs? https://github.com/mattdiamond/Recorderjs. I think it does what you need.
I have solved my problem by using Matt's Recorder.js https://github.com/mattdiamond/Recorderjs and connecting it to a GainNode which acts as an intermediary from a number of oscillators to the ctx.destination. I will be using localStorage but here is an example using an array (this does not include the oscillator setup).
var recorder;
recorder = new Recorder(gainNode, { workerPath: "../recorderWorker.js"});
recorder.record();
var recordedSound = [];
function recordSound() {
recorder.exportWAV(function(blob) {
recordedSound.push(blob);
});
}
function play(i) {
var audio = new Audio(window.URL.createObjectURL(recordedSound[i]));
audio.play();
}