Web Audio API Analyzer Node not working as expected - javascript

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.

Related

Protect users from accidental extremely loud volume in the browser

I'm building a flexible audio application where users can accidentally make things too loud. What are best practices to prevent extremely loud volume?
It looks like I can try using Analyser Node from Web Audio API to detect the loudness, and then reduce the volume based on that. Is there a more standard way?
I've managed to solve this reasonably well by using Analyser Node to detect the loud volume, and GainNode to decrease the volume. Followed code from visualizer examples:
const safety = audioContext.createGain()
const safetyAnalyser = audioContext.createAnalyser()
safetyAnalyser.connect(audioContext.destination)
safety.connect(safetyAnalyser)
protectEars(safetyAnalyser, safety)
applicationOutput.connect(safety)
function protectEars(analyser: AnalyserNode, gain: GainNode) {
analyser.fftSize = 2048
analyser.minDecibels = -90
const bufferLength = analyser.frequencyBinCount
const fbc_array = new Uint8Array(bufferLength)
analyser.getByteTimeDomainData(fbc_array)
const dataArray = new Uint8Array(fbc_array.buffer)
let timesHit255 = 0
for (let i = 0; i < bufferLength; ++i) {
if (dataArray[i] === 255) timesHit255++
}
// Magic number found with casual experimentation
if (timesHit255 > 200) {
gain.gain.value /= 2
sendNotificationBox('VOLUME REDUCED BECAUSE IT WAS TOO LOUD!')
} else {
// Slowly restore? They can also deal with it and just keep making it "louder" on their side
}
setTimeout(protectEars.bind(null, analyser, gain), 100)
}
The reason it uses setTimeout as opposed to requestAnimationFrame is because RAF does not get executed when the user switches tabs (while setTimeout does), and the audio may still be unpredictable so you'll still want to watch it.

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

Web audio API: how to silence all oscillators and pause melody

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

Buffered Audio For Generating Sounds

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

Toggle One And Off - JavaScript - Web Audio API

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

Categories