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);
Related
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.
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.
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.
Im creating my html5 app for testing and Im working with audio api , for generating sound on keyboard I'm doing something like this
keyboard.keyDown(function (note, frequency) {
var oscillator = context.createOscillator(),
gainNode = context.createGainNode();
oscillator.type = 2;
oscillator.frequency.value = frequency;
gainNode.gain.value = 0.3;
oscillator.connect(gainNode);
if (typeof oscillator.noteOn !== 'undefined') {
oscillator.noteOn(0);
}
gainNode.connect(context.destination);
nodes.push(oscillator);
});
now my question is , cause I tryied to find examples on google but with no success ,what are the other parammetars that can be used for getting sound sounds like piano or some electronic instrument except oscillator and how to pass them ?
I'm assuming you are fairly new to synthesis. Before trying synthesis algorithms in code, I'd recommend playing with some of the software synthesizers that are available - VST or otherwise. This will give you a handle on the kind of parameters you want to be introducing into your algorithm. http://www.soundonsound.com/sos/allsynthsecrets.htm is an index for a series of really good synthesis tutorials. (Start at the bottom - part 1!)
Once you are ready to start experimenting in code, a great place to start would be to introduce an envelope to change the volume or pitch of the sound over time (changing a parameter over time like this is called 'modulation'). This video may be of interest: http://www.youtube.com/watch?v=A6pp6OMU5r8
Bear in mind that almost all acoustic instruments are difficult to convincingly synthesize algorithmically, and by far the easiest way to get close to a piano is to use samples of real piano notes.
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();
}