Reputation: 3
I have this little javascript that I have working, it plays a tone for 30 seconds. I wanted to see if it was possible to modify it so that it would play at say, half volume instead (it's just too loud). I have researched and found "gain" node information, but I looked at the examples and honestly I'm a noob with javascrip and I don't fully understand the examples I saw.
Here is what I have so far (it works good, I just want to "volume" it down some)
function myFunction(frequency, duration, callback) {
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var oscillator = audioCtx.createOscillator();
duration = 30000 / 1000; // the 10000 used to be 'duration'
oscillator.type = 'square';
oscillator.frequency.value = 500; // value in hertz
oscillator.connect(audioCtx.destination);
oscillator.onended = callback;
oscillator.start(0);
oscillator.stop(audioCtx.currentTime + duration);
}
Can anyone help me modify this so I have a volume parameter I can tweak until it is right?
Upvotes: -1
Views: 973
Reputation: 1832
The documentation for what you are trying to do can be found under BaseAudioContext
, specifically the BaseAudioContext.createGain()
method.
The MDN documentation is lacking a little in that it only provides snippets that will not work as is. As such, an overly simplified example is given below, bearing in mind that this may not be best practice.
The oscillator and gain control are both audio nodes. As such, you should picture them in a signal chain like that given below.
The oscillator node passes through the gain node which passes through to the audio context.
Using your current format, as self contain snippet is given below (see jsfiddle here)
<!DOCTYPE html>
<html>
<head>
<script>
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var gainNode = audioCtx.createGain();
gainNode.connect(audioCtx.destination);
var gain = 0.1;
//---------------------------------------------------------------------
gainNode.gain.setValueAtTime(gain, audioCtx.currentTime);
</script>
</head>
<!-- ===================================================================== -->
<body>
<div>
<button onclick="myFunction()">
Click me
</button>
</div>
<script>
function myFunction()
{
//---------------------------------------------------------------------
var duration = 0.5; // in seconds
//---------------------------------------------------------------------
var oscillator = audioCtx.createOscillator();
oscillator.type = 'square';
oscillator.frequency.value = 500;
oscillator.connect(gainNode);
oscillator.start(audioCtx.currentTime);
oscillator.stop(audioCtx.currentTime + duration);
//---------------------------------------------------------------------
}
</script>
</body>
<!-- ===================================================================== -->
</html>
For best practice, I would suggest you should avoid recreating nodes over and over again. Rather, I would simply turn the gain up for a short period, then turn it back down, an example of which is given below.
As far as I can tell, there is no envelope generator node for WebAudio, but you could use .linearRampToValueAtTime()
if needed.
NOTE: This is not currently working in Safari. (see jsfiddle here)
<!DOCTYPE html>
<html>
<head>
<script>
//---------------------------------------------------------------------
// Edit These
var gain = 0.1;
var duration = 1000;
//---------------------------------------------------------------------
var audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var gainNode = audioCtx.createGain();
//---------------------------------------------------------------------
var oscillator = audioCtx.createOscillator();
oscillator.type = 'square';
oscillator.frequency.value = 500;
//---------------------------------------------------------------------
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
//---------------------------------------------------------------------
gainNode.gain.setValueAtTime(0.0, audioCtx.currentTime); // turned off by default
oscillator.start(audioCtx.currentTime);
//---------------------------------------------------------------------
</script>
</head>
<!-- ===================================================================== -->
<body>
<div>
<button onclick="soundOn()">
Click me
</button>
</div>
<script>
function mute()
{
gainNode.gain.setValueAtTime(0.0, audioCtx.currentTime);
}
function soundOn()
{
gainNode.gain.setValueAtTime(gain, audioCtx.currentTime);
setTimeout(mute,duration);
}
</script>
</body>
<!-- ===================================================================== -->
</html>
If you are struggling with interacting with audio context in this was, I would suggest trying out a library such as p5.js and the p5.sound library.
Look at the https://p5js.org/reference/#/p5.Oscillator to see whether it is a more intuitive approach.
Upvotes: 1