Add ability to pause/resume AudioBufferSourceNode
croraf opened this issue ยท 11 comments
Describe the feature
Add ability to pause/resume AudioBufferSourceNode
Is there a prototype?
Describe the feature in more detail
I think people would love to have that. Not sure why is it not possible and explicitly stated as not being provided.
The requirement can be achieved using a single, dedicated AudioContext
, resume()
, suspend()
, and close()
methods of the same
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<input type="file" id="file" accept=".webm,.opus" /><br />
<button id="start">start</button>
<button id="pause">pause</button>
<button id="resume">resume</button>
<button id="close">close</button>
<script>
onload = async (_) => {
const ac = new AudioContext();
console.log(ac.state);
ac.onstatechange = (e) => console.log(ac.state);
let absn;
let buffer;
let ab;
const sources = _ => absn instanceof AudioBufferSourceNode
&& buffer instanceof AudioBuffer;
document.getElementById('file').onchange = async (e) => {
ab = await e.target.files[0].arrayBuffer();
if (ac.state === 'running') {
await ac.suspend();
}
};
document.getElementById('start').onclick = async (e) => {
if (!sources()) {
buffer = await ac.decodeAudioData(ab);
absn = new AudioBufferSourceNode(ac, { buffer, loop: true });
absn.onended = async (e) => {
console.log(e);
};
absn.connect(ac.destination);
if (ac.state === 'suspended') {
ac.resume();
console.log({ab, buffer}, ac.state);
absn.start(ac.currentTime);
}
}
};
document.getElementById('pause').onclick = async (e) => {
if (sources() && ac.state === 'running') {
await ac.suspend();
}
};
document.getElementById('resume').onclick = async (e) => {
if (sources() && ac.state === 'suspended') {
await ac.resume();
}
};
document.getElementById('close').onclick = async (e) => {
await ac.close();
};
};
</script>
</body>
</html>
Cool. Ty very much for that. I made a simpler example with just play/pause buttons and it works nice.
But this is resuming/suspending on the context level, wouldn't it be nice if you could suspend/resume on the source node level?
But this is resuming/suspending on the context level, wouldn't it be nice if you could suspend/resume on the source node level?
Would there be an observable difference?
AudioBufferSourceNode
class could probably be extended with pause()
and resume()
methods that use AudioContext
suspend()
and resume()
.
The difference is if you have 2 AudioBufferSourceNodes or AudioBufferSourceNode along some other source nodes. You wouldn't have to pause all of them as with context.suspend(), but only certain source.
One option to connect source nodes is to an MediaStreamAudioDestinationNode
with the stream
property set at HTMLMediaElement
srcObject
, use the <audio>
or <video>
element to play and pause (output audio) an AudioBufferSourceNode
and, or other connected audio nodes
const ac = new AudioContext();
const mediaElement = new Audio();
mediaElement.controls = mediaElement.autoplay = true;
document.body.appendChild(mediaElement);
const source = new MediaStreamAudioDestinationNode(ac);
absn.connect(source);
mediaElement.srcObject = source.stream;
Currently, the only way I know of to pause an AudioBufferSourceNode
is to set the playbackRate
to 0. Nice thing about this is that you can do it with sample-accurate timing using setValueAtTime()
. Well, to the accuracy of a render quantum; playbackRate
is a k-rate AudioParam
.
@guest271314 I tried your solution but it seems not to be working as expected. https://jsfiddle.net/croraf/m49rwpqn/
(Try in FF which doesn't respect the autoplay policy) The audio element shows "streaming" but there is no sound.
@rtoy Yes, I heard about the playbackRate=0 hack.
But these are all workarounds. I'm wondering why can't AudioBufferSourceNode be just paused and resumed.
OK, I forgot to add
sourceNode.start();
for the AudioBufferSourceNode to start "playing" its content into the next node, but even then the pause on the audio element just stops the sound to be outputted on my device, the AudioBufferSourceNode is not paused and continues to play and consume the buffer.
Teleconf: Current browsers take advantage of the fact that once stop is called, it can't be restarted. This includes cleaning up all internal storage and methods and possibly disconnecting (internally) from downstream nodes. These improve memory usage and performance.
If setting the playbackRate is not good enough, we also note that we're adding a playbackPosition #26 so you can schedule a stop and get the playback position at that time. Then construct a new ABSN with the same buffer, and start the ABSN where you left off using start with the appropriate offset.