- The gain controller works dynamically on chrome, but in firefox it does not. I need to re-play audio file for browser to recognize new input value of the range input for firefox. I couldn't figure out why.
- You need to put an mp3 file into "example.mp3" part by the way.
- I'm new in here. Tried the reprex. Sorry if it is not as it should be
[HTML Javascript]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div>
<button >click</button>
<input
type="range"
step="0.01"
min="0"
max="1.20"
value="0.60"
/>
</div>
</body>
<script src="script.js" type="text/javascript"></script>
</html>
<script type="text/javascript">
loopify("example.mp3", function (err, loop) {
// If something went wrong, `err` is supplied
document.querySelector(".button").dataset.playing = "false";
if (err) {
return console.err(err);
}
document.querySelector(".button").addEventListener("click", function () {
if (this.dataset.playing === "false") {
loop.play();
this.dataset.playing = "true";
} else if (this.dataset.playing === "true") {
setTimeout(loop.stop, 250);
this.dataset.playing = "false";
}
});
});
</script>
[Javascript]
(function () {
function loopify(uri, cb) {
var context = new (window.AudioContext || window.webkitAudioContext)(),
request = new XMLHttpRequest();
const gainNode = context.createGain();
request.responseType = "arraybuffer";
request.open("GET", uri, true);
// XHR failed
request.onerror = function () {
cb(new Error("Couldn't load audio from " uri));
};
// XHR complete
request.onload = function () {
context.decodeAudioData(request.response, success, function (err) {
// Audio was bad
cb(new Error("Couldn't decode audio from " uri));
});
};
request.send();
function success(buffer) {
var source;
// fade-out
document.querySelector(".button").onclick = function () {
if (this.dataset.playing === "true") {
gainNode.gain.setTargetAtTime(0, context.currentTime, 0.25);
}
};
function play() {
// Stop if it's already playing
stop();
// Create a new source (can't replay an existing source)
source = context.createBufferSource();
source.connect(gainNode).connect(context.destination);
// fade-in
gainNode.gain.value = 0.1;
gainNode.gain.setTargetAtTime(
document.querySelector(".input").value,
context.currentTime,
0.25
);
//gain input assignment
document.querySelector(".input").addEventListener(
"input",
function () {
gainNode.gain.value = this.value;
},
false
);
// Set the buffer
source.buffer = buffer;
source.loop = true;
// Play it
source.start(0);
}
function stop() {
// Stop and clear if it's playing
if (source) {
source.stop();
source = null;
}
}
cb(null, {
play: play,
stop: stop,
});
}
}
loopify.version = "0.1";
if (typeof define === "function" && define.amd) {
define(function () {
return loopify;
});
} else if (typeof module === "object" && module.exports) {
module.exports = loopify;
} else {
this.loopify = loopify;
}
})();
[CSS]
div {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
CodePudding user response:
After some experimentation and trying to reproduce your code, I've stumbled on something curious.
Whenever we use gainNode.gain.setTargetAtTime
we are unable to update Gain value by using the setter gainNode.gain.value
. I could not find any explanation in the documentation of setTargetAtTime
or value
as to why this happens.
Fortunately there is a fix without breaking the fade-in that setTargetAtTime
provides.
The solution lies in setting the gain value with setValueAtTime
instead of with the setter.
const input = document.querySelector(".input");
gainNode.gain.value = 0.1;
gainNode.gain.setTargetAtTime(
input.value,
context.currentTime,
0.25
);
input.addEventListener(
"input",
function () {
gainNode.gain.setValueAtTime(
this.value,
context.currentTime
)
},
false
);