Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | 18x 18x 18x 18x 18x 18x 18x 18x 20x 20x 537x 537x 537x 63924x 63924x 63924x 537x 537x 275x 24x 251x 275x 275x 269x 269x 29069x 29069x 29069x 269x 269x 275x 275x 21x 21x 21x 251x 251x 251x 23x 23x | // Between a bursty producer and an audio worklet's steady reads: an underrun
// plays silence and waits for targetFrames again; a lapping writer drops the oldest.
export class StereoRing {
constructor(capacityFrames, { targetFrames, excessFrames, persistentExcessFrames }) {
this.capacityFrames = capacityFrames;
this.excessFrames = excessFrames;
this.persistentExcessFrames = persistentExcessFrames;
this.frames = new Float32Array(capacityFrames * 2);
this.readFrame = 0;
this.occupancy = 0;
this.running = false;
this.setTargetFrames(targetFrames);
}
setTargetFrames(targetFrames) {
this.targetFrames = targetFrames;
this.excessRunFrames = 0;
}
write(interleaved, scale = 1) {
const count = interleaved.length >> 1;
let writeFrame = (this.readFrame + this.occupancy) % this.capacityFrames;
for (let i = 0; i < count; ++i) {
this.frames[2 * writeFrame] = interleaved[2 * i] * scale;
this.frames[2 * writeFrame + 1] = interleaved[2 * i + 1] * scale;
if (++writeFrame === this.capacityFrames) writeFrame = 0;
}
this.occupancy += count;
if (this.occupancy > this.capacityFrames) this._discard(this.occupancy - this.capacityFrames);
}
read(left, right) {
if (!this.running) {
if (this.occupancy >= this.targetFrames) this._restart();
} else {
this._skipPersistentExcess(left.length);
}
let played = 0;
if (this.running) {
played = Math.min(left.length, this.occupancy);
for (let i = 0; i < played; ++i) {
left[i] = this.frames[2 * this.readFrame];
right[i] = this.frames[2 * this.readFrame + 1];
if (++this.readFrame === this.capacityFrames) this.readFrame = 0;
}
this.occupancy -= played;
if (played < left.length) this.running = false;
}
left.fill(0, played);
right.fill(0, played);
}
// Trims to the target at a point where the output is already
// discontinuous: after an underrun, or when skipping a persistent excess.
_restart() {
this._discard(Math.max(0, this.occupancy - this.targetFrames));
this.excessRunFrames = 0;
this.running = true;
}
_skipPersistentExcess(frames) {
const excess = this.occupancy > this.targetFrames + this.excessFrames;
this.excessRunFrames = excess ? this.excessRunFrames + frames : 0;
if (this.excessRunFrames >= this.persistentExcessFrames) this._restart();
}
_discard(count) {
this.readFrame = (this.readFrame + count) % this.capacityFrames;
this.occupancy -= count;
}
}
|