Add playback position to AudioBufferPlayer #192
-
It would be great to have a new output in the AudioBufferPlayer op to track the position of playback. It would enable to build animations based on the playback. I already tweaked the op code in a user op to make this work. Here is my first implementation var audioCtx = CABLES.WEBAUDIO.createAudioContext(op);
// input ports
var audioBufferPort = op.inObject("Audio Buffer");
var playPort = op.inValueBool("Start / Stop", false);
var startTimePort = op.inValue("Start Time", 0);
var stopTimePort = op.inValue("Stop Time", 0);
var offsetPort = op.inValue("Offset", 0);
var autoPlayPort = op.inValueBool("Autoplay", false);
var loopPort = op.inValueBool("Loop", false);
var detunePort = op.inValue("Detune", 0);
var playbackRatePort = op.inValue("Playback Rate", 1);
// new input port on trigger
var inTrigger = op.inTrigger("calculate position")
// output ports
var audioOutPort = op.outObject("Audio Out");
// new output port
var outAudioPosition = op.outNumber("position");
// vars
var source = null;
// new var
var startTime = null;
/* new function on trigger to calculate position each frame*/
inTrigger.onTriggered= function(){
if(source){
let pos = Math.min((source.context.currentTime - startTime) /source.buffer.duration, 1)
outAudioPosition.set(pos);
}
}
// change listeners
audioBufferPort.onChange = function() {
createAudioBufferSource();
if(
(autoPlayPort.get() && audioBufferPort.get()) ||
(playPort.get() && audioBufferPort.get())
) {
start(startTimePort.get());
}
};
playPort.onChange = function() {
if(source) {
if(playPort.get()) {
var startTime = startTimePort.get() || 0;
start(startTime);
} else {
var stopTime = stopTimePort.get() || 0;
stop(stopTime);
}
}
};
loopPort.onChange = function() {
if(source) {
source.loop = loopPort.get() ? true : false;
}
};
detunePort.onChange = setDetune;
function setDetune() {
if(source) {
var detune = detunePort.get() || 0;
if(source.detune) {
source.detune.setValueAtTime(
detune,
audioCtx.currentTime
);
}
}
}
playbackRatePort.onChange = setPlaybackRate;
function setPlaybackRate() {
if(source) {
var playbackRate = playbackRatePort.get() || 0;
if(playbackRate >= source.playbackRate.minValue && playbackRate <= source.playbackRate.maxValue) {
source.playbackRate.setValueAtTime(
playbackRate,
audioCtx.currentTime
);
}
}
}
// functions
function createAudioBufferSource() {
if(source)stop(0);
source = audioCtx.createBufferSource();
var buffer = audioBufferPort.get();
if(buffer) {
source.buffer = buffer;
}
source.onended = onPlaybackEnded;
source.loop = loopPort.get();
setPlaybackRate();
setDetune();
audioOutPort.set(source);
// op.log("load", source);
}
function start(time) {
try {
source.start(time,offsetPort.get()); // 0 = now
// reset startTime on play to offset context.curreTime
startTime = source.context.currentTime;
} catch(e){
// console.log(e);
} // already playing!?
}
function stop(time) {
try {
source.stop(time); // 0 = now
} catch(e)
{
// console.log(e);
} // not playing!?
}
function onPlaybackEnded() {
createAudioBufferSource(); // we can only play back once, so we need to create a new one
} |
Beta Was this translation helpful? Give feedback.
Replies: 5 comments 4 replies
-
thanks! could you outline your changes to the current op? is this about |
Beta Was this translation helpful? Give feedback.
-
Note that this is not perfect at all an quite hacky. For instance it doesn't take into account playback rate, nor the loop option. |
Beta Was this translation helpful? Give feedback.
-
i tried something similar once and for some reason it's not really that easy. i also don't think its a good idea to add another trigger for this. when i did this i tried to bind it to some events in the audiocontext itself but then ran into these kind of problems: https://stackoverflow.com/questions/31644060/how-can-i-get-an-audiobuffersourcenodes-current-time where people basically write their own timer and "progress counter". i solved this by patching back then. you should have all the information you need on the outside already (as your calculations show) and then synchronizing the "start / stop" input with a https://cables.gl/op/Ops.Time.TimeSinceTrigger was the way to go for me back then. |
Beta Was this translation helpful? Give feedback.
-
Well p5js sound doest it like this : calculation taking into account playback rate : the '_lastpos' variable update used in the above formula : |
Beta Was this translation helpful? Give feedback.
-
Here is a link to an example with a subpatch to make this feature in cables : https://cables.gl/p/uFpyk5 |
Beta Was this translation helpful? Give feedback.
i tried something similar once and for some reason it's not really that easy. i also don't think its a good idea to add another trigger for this. when i did this i tried to bind it to some events in the audiocontext itself but then ran into these kind of problems:
https://stackoverflow.com/questions/31644060/how-can-i-get-an-audiobuffersourcenodes-current-time
where people basically write their own timer and "progress counter". i solved this by patching back then. you should have all the information you need on the outside already (as your calculations show) and then synchronizing the "start / stop" input with a
https://cables.gl/op/Ops.Time.TimeSinceTrigger
was the way to go for me back …