Buffered Audio For Generating Sounds - javascript

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();
}

Related

How to make a frequency sound stereo in JavaScript

I have some troubles while trying to reproduce different frequencies using two different audio channels (left and right) in JavaScript. I've been searching in StackOverflow and Internet for a while, but I didn't find anything that could help me, so I decided to ask here for help.
Let me explain first why I'm doing this. There's a lot of people in the world that have tinnitus (an "illness" where you hear a specific frequency in an ear or in both). Sometimes, people sat that tinnitus is not a big trouble. The website is gonna allow the users to know how a "tinnitus person" hear. For accomplishing that, the audio must be different in both ears, so I need to send different frequencies in two different channel audio.
This is the code I already have, it reproduces a specific frequency in mono (full app here: replit.com/Tupiet/hearing):
function letsStart() {
try{
window.AudioContext = window.AudioContext || window.webKitAudioContext;
context = new AudioContext();
}
catch(e) {
alert("API isn't working");
}
}
function initFrequency() {
let range = document.getElementById('range').value;
osc = context.createOscillator();
osc.frequency.value = range;
osc.connect(context.destination);
osc
osc.start(0);
document.querySelector(".show-frequency").innerHTML = range + "Hz";
}
The code above is playing a specific frequency in mono mode, but as I expected, I need to play it in a specific channel audio.
By the way, the only question I found that I thought it could help me was this one, but I think it's not what I'm searching since it doesn't work with frequencies.
How can I do it? I couldn't an explanation anywhere. Really really thanks!
You can achieve the desired result by using a ChannelMergerNode. It can be used to piece together a stereo signal.
Here is an example with two independent oscillators.
const audioContext = new AudioContext();
const leftOscillator = audioContext.createOscillator();
const leftGain = audioContext.createGain();
const rightOscillator = audioContext.createOscillator();
const rightGain = audioContext.createGain();
const merger = audioContext.createChannelMerger(2);
leftOscillator.connect(leftGain).connect(merger, 0, 0);
rightOscillator.connect(rightGain).connect(merger, 0, 1);
merger.connect(audioContext.destination);
leftOscillator.frequency.value = 800;
leftGain.gain.value = 0.5;
leftOscillator.start(0);
rightOscillator.frequency.value = 1400;
rightGain.gain.value = 0.8;
rightOscillator.start(0);

Javascript - Can I change the volume of ALL audio elements at once?

I have a program with several Audio elements like this new Audio( url ).play()
How can I change the volume of ALL Audio elements in my program at once.
I've seen other posts that show how to change volume for a single item like this...
x=new Audio( url ) x.volume=0.2 x.play()
but my sounds might have no variable or id applied to them or might have many different variables or ids applied to them. Can I change the volume of EVERYTHING in the program that is an Audio element?
You can try to put every Audio element into an array and create a function that will iterate over this array and set volume to each element:
const audios = [];
const audio1 = new Audio(url1);
audio1.play();
audios.push(audio1);
const audio2 = new Audio(url2);
audio2.play();
audios.push(audio2);
function changeVolume(volume) {
audios.forEach(audioElement => audioElement.volume = volume);
}
changeVolume(0.2);

ToneJS: Multi-pitch keyboard entry

This pen uses the ToneJS library to play pitches on the computer keyboard. However, it can only play one note at a time. How can I code this to play multiple notes at once?
The code:
var keyToPitch = { "z":"C3", "s":"C#3", "x":"D3", "d":"D#3", "c":"E3", "v":"F3", "g":"F#3", "b":"G3", "h":"G#3", "n":"A3", "j":"A#3", "m":"B3", ",":"C4" }
var synth = new Tone.Synth()
synth.oscillator.type = "sawtooth"
synth.toMaster()
window.addEventListener('keydown', this.onkeydown)
window.addEventListener('keyup', this.onkeyup)
function onkeydown(e){
synth.triggerAttack(keyToPitch[e.key], Tone.context.currentTime)
}
function onkeyup(e){
synth.triggerRelease()
}
Oscillators in ToneJS are audio sources, and Master is the output that plays all the inputs connected to it. So to play multiple overlapping sounds, you just create multiple oscillators and connect them all to Master.
For the kind of demo you're linking to, a typical thing to do might be to make a fixed number (5, say) of oscillators, and rotate through them in turn when you want to trigger a sound:
var synthIndex = 0
function startSound(){
synthIndex = (synthIndex + 1) % voices
var synth = synths[synthIndex]
synth.triggerAttack(/* ... */)
}
Or similar. In principle you could make a separate oscillator for every pitch, but this would probably hurt performance in a less trivial demo.

Web Audio API Analyzer Node not working as expected

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.

Creating an Oscillator Node in Scala.js using js.Dynamic

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()

Categories