New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Specify ABSN playback algorithm exactly. #1143
Changes from 1 commit
1755177
5239a89
ac037da
473db22
73a9fd6
f148cfe
aefdab3
42ede16
478015e
4307ea7
057d63c
5d4a6e8
56158e9
5c2d154
329a623
299a4e7
dfbd3c3
03b2510
2dc8726
c19243a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5021,55 +5021,28 @@ <h3 id="playback-AudioBufferSourceNode"> | |
The description of the algorithm is as follows: | ||
</p> | ||
<pre> | ||
// The AudioBuffer and context employed by this node | ||
var buffer; | ||
var context; | ||
let buffer; /* AudioBuffer employed by this node */; | ||
let context; /* AudioContext employed by this node */; | ||
|
||
// The following variables capture attribute and AudioParam values for the node. | ||
// They are updated k-rate, except for buffer. | ||
var loop; | ||
var detune; | ||
var loopEnd; | ||
var loopStart; | ||
var playbackRate; | ||
|
||
// State variables for the node's playback | ||
var start = 0; | ||
var offset; | ||
var stop = Infinity; | ||
|
||
var bufferTime = 0; | ||
var started = false; | ||
var enteredLoop = false; | ||
var dt; | ||
|
||
// Handle invocation of start method call | ||
function handleStart(when, pos, duration) { | ||
if (arguments.length >= 1) { | ||
start = when; | ||
} | ||
offset = pos; | ||
if (arguments.length >= 3) { | ||
stop = when + duration; | ||
} | ||
} | ||
// They are updated on a k-rate basis, prior to each invocation of process(). | ||
let loop, detune, loopStart, loopEnd, playbackRate; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know people disagree on this, but I hate the style that declares a bunch of vars all on one line. I defer to others on this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't really care -- I usually don't use this style, but thought we wanted more compactness where possible. So happy to make this change. |
||
|
||
// Handle invocation of stop method call | ||
function handleStop(when) { | ||
if (arguments.length >= 1) { | ||
stop = when; | ||
} | ||
else { | ||
stop = context.currentTime; | ||
} | ||
} | ||
// Variables for the node's playback parameters | ||
let start = 0, offset = 0; // Set by start() | ||
let stop = Infinity; // Set by stop(), or by start() with a supplied duration | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe expand these comments to include your original description (or code) on what the values are. Now it's really unclear what offset and stop are actually set to. Perhaps I was wrong about removing the functions. Maybe reinstate and do let start = function_to_initialize_start(); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, that was why I had the functions. I will put them back in at this point. However, the use of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem with having these functions is that they're never called (or mentioned that they're magically called) anywhere. So they serve no purpose. So my proposal is to call them somehow either explicitly so implicitly using comments. Or add comments to where start and stop are declared to explain how these values are initialized. |
||
|
||
// Variables for tracking node's playback state | ||
let bufferTime = 0, started = false, enteredLoop = false; | ||
let dt = 1 / context.sampleRate; | ||
|
||
// Interpolate a multi-channel signal value for some sample frame | ||
// Interpolate a multi-channel signal value for some sample frame. | ||
// Returns an array of signal values. | ||
function playbackSignal(position) { | ||
/* | ||
This function provides the playback signal function for buffer, which is a | ||
function that maps from a playhead position to a set of output signal | ||
values, one for each output channel. If position corresponds to the | ||
values, one for each output channel. If |position| corresponds to the | ||
location of an exact sample frame in the buffer, this function returns | ||
that frame. Otherwise, its return value is determined by a UA-supplied | ||
algorithm that interpolates between sample frames in the neighborhood of | ||
|
@@ -5082,24 +5055,18 @@ <h3 id="playback-AudioBufferSourceNode"> | |
... | ||
} | ||
|
||
// Render a single frame of audio into the channel data arrays given by output | ||
// at the given element index. The frame value may be an array of channel signal | ||
// values, or a single value to be rendered to all channels. | ||
function renderFrame(output, index, frame) { | ||
... | ||
} | ||
|
||
// Generate a single render quantum of audio to be placed | ||
// in the channel arrays defined by output. | ||
function process(output) { | ||
var dt = 1 / context.sampleRate; | ||
var index = 0; | ||
// in the channel arrays defined by output. Returns an array | ||
// of |numberOfFrames| sample frames to be output. | ||
function process(numberOfFrames) { | ||
let currentTime = context.currentTime; // context time of next rendered frame | ||
let output = []; // accumulates rendered sample frames | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to initialize |
||
// Combine the two k-rate parameters affecting playback rate | ||
var computedPlaybackRate = playbackRate * Math.pow(2, detune / 1200); | ||
let computedPlaybackRate = playbackRate * Math.pow(2, detune / 1200); | ||
|
||
// Determine loop endpoints as applicable | ||
var actualLoopStart, actualLoopEnd; | ||
let actualLoopStart, actualLoopEnd; | ||
if (loop) { | ||
if (loopStart >= 0 && loopEnd > 0 && loopStart < loopEnd) { | ||
actualLoopStart = loopStart; | ||
|
@@ -5112,60 +5079,62 @@ <h3 id="playback-AudioBufferSourceNode"> | |
} | ||
|
||
// Render each sample frame in the quantum | ||
while (index < output[0].length) { | ||
for (let index = 0; index < numberOfFrames; index++) { | ||
// Check that currentTime is within allowable range for playback | ||
if (currentTime < start || currentTime >= stop) { | ||
// emit silence for the output frame for the element specified by index | ||
renderFrame(output, index, 0); | ||
output.push(0); // this sample frame is silent | ||
currentTime += dt; | ||
continue; | ||
} | ||
else { | ||
if (!started) { | ||
// Take note that buffer has started playing and get initial playhead position. | ||
bufferTime = offset + ((context.currentTime - start) * computedPlaybackRate); | ||
started = true; | ||
} | ||
|
||
// Handle loop-related calculations | ||
if (loop) { | ||
// Determine if looped portion has been entered | ||
if (!enteredLoop) { | ||
if (offset < actualLoopEnd && bufferTime >= actualLoopStart) { | ||
// playback began before or within loop, and playhead is now past loop start | ||
enteredLoop = true; | ||
} | ||
if (offset >= actualLoopEnd && bufferTime < actualLoopEnd) { | ||
// playback began after loop, and playhead is now prior to the loop end | ||
enteredLoop = true; | ||
} | ||
} | ||
if (!started) { | ||
// Take note that buffer has started playing and get initial playhead position. | ||
bufferTime = offset + ((currentTime - start) * computedPlaybackRate); | ||
started = true; | ||
} | ||
|
||
// Wrap loop iterations as needed | ||
if (enteredLoop) { | ||
while (bufferTime >= actualLoopEnd) { | ||
bufferTime -= actualLoopEnd - actualLoopStart; | ||
} | ||
while (bufferTime < actualLoopStart) { | ||
bufferTime += actualLoopEnd - actualLoopStart; | ||
} | ||
// Handle loop-related calculations | ||
if (loop) { | ||
// Determine if looped portion has been entered for the first time | ||
if (!enteredLoop) { | ||
if (offset < actualLoopEnd && bufferTime >= actualLoopStart) { | ||
// playback began before or within loop, and playhead is now past loop start | ||
enteredLoop = true; | ||
} | ||
if (offset >= actualLoopEnd && bufferTime < actualLoopEnd) { | ||
// playback began after loop, and playhead is now prior to the loop end | ||
enteredLoop = true; | ||
} | ||
} | ||
|
||
if (bufferTime >= 0 && bufferTime < buffer.duration) { | ||
renderFrame(output, index, playbackSignal(bufferTime)); | ||
} | ||
else { | ||
renderFrame(output, index, 0); | ||
// Wrap loop iterations as needed | ||
if (enteredLoop) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe convert this to
to emphasize that this is the alternative case for the if on line 5099. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, but it is not the alternative case. The preceding |
||
while (bufferTime >= actualLoopEnd) { | ||
bufferTime -= actualLoopEnd - actualLoopStart; | ||
} | ||
while (bufferTime < actualLoopStart) { | ||
bufferTime += actualLoopEnd - actualLoopStart; | ||
} | ||
} | ||
bufferTime += dt * computedPlaybackRate; | ||
} // End active rendering "else" case | ||
} | ||
|
||
if (bufferTime >= 0 && bufferTime < buffer.duration) { | ||
output.push(playbackSignal(bufferTime)); | ||
} | ||
else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Style nit: Use
Here and elsewhere. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure thing |
||
output.push(0); // past end of buffer, so output silent frame | ||
} | ||
|
||
index += 1; | ||
bufferTime += dt * computedPlaybackRate; | ||
currentTime += dt; | ||
} // End of render quantum loop | ||
|
||
if (currentTime >= stop) { | ||
// end playback state of this node. | ||
// no further invocations of process() will occur. | ||
} | ||
|
||
return output; | ||
} | ||
</pre> | ||
<p> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit. Remove ";" at end of line. Maybe just use // style comments here too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, this went through a couple of iterations and got messed up in the process.