cat / goq
clone

turns a linux box into a private steam streaming appliance: plug in, tap your key, play from another computer

fix/website-download-wiring
goq / portal / audio-ring.test.mjs
audio-ring.test.mjs 9.1K
1import assert from 'node:assert/strict';2import test from 'node:test';34import {5 AUDIO_FRAME_DURATION_MICROS,6 BoundedAudioMessageTracker,7 BoundedAudioRing,8} from './audio-ring.mjs';910function stereo(values) {11 return [Float32Array.from(values), Float32Array.from(values, (value) => -value)];12}1314test('prebuffers, renders in order, and re-prebuffers after underflow', () => {15 const ring = new BoundedAudioRing({ capacityFrames: 8, startFrames: 4 });16 const output = stereo([0, 0, 0, 0]);17 ring.write(stereo([1, 2]));18 assert.equal(ring.read(output), 0);19 ring.write(stereo([3, 4]));20 assert.equal(ring.read(output), 4);21 assert.deepEqual([...output[0]], [1, 2, 3, 4]);22 assert.deepEqual([...output[1]], [-1, -2, -3, -4]);2324 ring.write(stereo([5, 6, 7, 8]));25 const longOutput = stereo([0, 0, 0, 0, 0, 0]);26 assert.equal(ring.read(longOutput), 4);27 assert.equal(ring.underflows, 1);28 assert.equal(ring.started, false);29});3031test('drops oldest frames on overflow and remains strictly bounded', () => {32 const ring = new BoundedAudioRing({ capacityFrames: 4, startFrames: 2 });33 ring.write(stereo([1, 2, 3]));34 assert.equal(ring.write(stereo([4, 5, 6])), 2);35 assert.equal(ring.length, 4);36 const output = stereo([0, 0, 0, 0]);37 ring.read(output);38 assert.deepEqual([...output[0]], [3, 4, 5, 6]);39 assert.equal(ring.droppedFrames, 2);40});4142test('mute consumes current audio and clear removes all session state', () => {43 const ring = new BoundedAudioRing({ capacityFrames: 8, startFrames: 2 });44 ring.write(stereo([1, 2, 3, 4]));45 const output = stereo([9, 9]);46 assert.equal(ring.read(output, true), 2);47 assert.deepEqual([...output[0]], [0, 0]);48 assert.equal(ring.length, 2);49 ring.clear();50 assert.equal(ring.length, 0);51 assert.equal(ring.started, false);52 assert.equal(ring.read(output), 0);53});5455test('rejects malformed channel data and invalid bounds', () => {56 assert.throws(() => new BoundedAudioRing({ capacityFrames: 2, startFrames: 3 }));57 const ring = new BoundedAudioRing({ capacityFrames: 4, startFrames: 2 });58 assert.throws(() => ring.write([Float32Array.of(1)]));59 assert.throws(() => ring.write([Float32Array.of(1), Float32Array.of(1, 2)]));60 assert.throws(() => ring.read([Float32Array.of(0)]));61});6263test('bounds decoded PCM messages until the worklet accepts their exact IDs', () => {64 const tracker = new BoundedAudioMessageTracker(3);65 const first = tracker.reserve();66 const second = tracker.reserve();67 const third = tracker.reserve();68 assert.deepEqual([first, second, third], [1, 2, 3]);69 assert.equal(tracker.size, 3);70 assert.equal(tracker.reserve(), null);71 assert.equal(tracker.droppedMessages, 1);72 assert.equal(tracker.accept(99), false);73 assert.equal(tracker.accept(second), true);74 assert.equal(tracker.accept(second), false);75 assert.equal(tracker.reserve(), 4);76 assert.equal(tracker.size, 3);77});7879test('recovery preserves transferred PCM ownership until exact worklet acknowledgments', () => {80 const tracker = new BoundedAudioMessageTracker(2);81 const ring = new BoundedAudioRing({ capacityFrames: 8, startFrames: 2 });82 const first = tracker.reserve();83 const second = tracker.reserve();84 ring.write(stereo([1, 2, 3, 4]));8586 // The recovery clear is FIFO behind both transferred sample messages. It may87 // discard their ring frames, but cannot release main-thread ownership early.88 ring.clear({ recovery: true });89 assert.equal(tracker.size, 2);90 assert.equal(tracker.reserve(), null);91 assert.equal(ring.snapshot().recoveryDiscardedFrames, 4);9293 assert.equal(tracker.accept(first), true);94 assert.equal(tracker.reserve(), 3);95 assert.equal(tracker.size, 2);96 assert.equal(tracker.accept(second), true);97});9899test('clears pending worklet ownership and resets tracker telemetry', () => {100 assert.throws(() => new BoundedAudioMessageTracker(0));101 const tracker = new BoundedAudioMessageTracker(1);102 tracker.reserve();103 tracker.reserve();104 tracker.clear();105 assert.equal(tracker.size, 0);106 assert.equal(tracker.droppedMessages, 1);107 tracker.reset();108 assert.equal(tracker.droppedMessages, 0);109 assert.equal(tracker.reserve(), 1);110});111112test('ring telemetry exposes worklet overflow drops', () => {113 const ring = new BoundedAudioRing({ capacityFrames: 4, startFrames: 2 });114 ring.write(stereo([1, 2, 3]));115 ring.write(stereo([4, 5, 6]));116 assert.equal(ring.snapshot().droppedFrames, 2);117 assert.equal(ring.snapshot().recoveryDiscardedFrames, 0);118});119120test('counts only explicitly marked recovery clears in exact PCM frames', () => {121 const ring = new BoundedAudioRing({ capacityFrames: 8, startFrames: 2 });122 ring.write(stereo([1, 2, 3]));123 ring.clear();124 assert.equal(ring.snapshot().recoveryDiscardedFrames, 0);125126 ring.write(stereo([4, 5, 6, 7]));127 ring.clear({ recovery: true });128 assert.equal(ring.snapshot().recoveryDiscardedFrames, 4);129130 ring.write(stereo([8, 9]));131 ring.clear({ recovery: true });132 assert.equal(ring.snapshot().recoveryDiscardedFrames, 6);133 assert.equal(ring.snapshot().droppedFrames, 0);134});135136test('tracks media PTS exactly across partial reads', () => {137 const ring = new BoundedAudioRing({ capacityFrames: 8, startFrames: 2 });138 ring.write(stereo([1, 2, 3, 4, 5, 6]), 1_000_000);139140 assert.equal(ring.read(stereo([0, 0])), 2);141 assert.equal(142 ring.snapshot().renderedMediaEndPtsMicros,143 1_000_000 + 2 * AUDIO_FRAME_DURATION_MICROS,144 );145 assert.equal(ring.read(stereo([0, 0, 0])), 3);146 assert.equal(147 ring.snapshot().renderedMediaEndPtsMicros,148 1_000_000 + 5 * AUDIO_FRAME_DURATION_MICROS,149 );150});151152test('keeps timestamp metadata aligned while dropping oldest overflow frames', () => {153 const ring = new BoundedAudioRing({ capacityFrames: 4, startFrames: 2 });154 ring.write(stereo([1, 2, 3]), 0);155 ring.write(stereo([4, 5, 6]), 100_000);156157 const output = stereo([0, 0, 0, 0]);158 assert.equal(ring.read(output), 4);159 assert.deepEqual([...output[0]], [3, 4, 5, 6]);160 assert.equal(161 ring.snapshot().renderedMediaEndPtsMicros,162 100_000 + 3 * AUDIO_FRAME_DURATION_MICROS,163 );164});165166test('offsets PTS when an oversized packet drops its own oldest frames', () => {167 const ring = new BoundedAudioRing({ capacityFrames: 4, startFrames: 2 });168 assert.equal(ring.write(stereo([1, 2, 3, 4, 5, 6]), 500_000), 2);169170 const output = stereo([0, 0, 0, 0]);171 assert.equal(ring.read(output), 4);172 assert.deepEqual([...output[0]], [3, 4, 5, 6]);173 assert.equal(174 ring.snapshot().renderedMediaEndPtsMicros,175 500_000 + 6 * AUDIO_FRAME_DURATION_MICROS,176 );177});178179test('clears the rendered PTS anchor and accepts a discontinuous new timeline', () => {180 const ring = new BoundedAudioRing({ capacityFrames: 4, startFrames: 2 });181 ring.write(stereo([1, 2]), 10_000);182 ring.read(stereo([0, 0]));183 assert.notEqual(ring.snapshot().renderedMediaEndPtsMicros, null);184185 ring.clear();186 assert.equal(ring.snapshot().renderedMediaEndPtsMicros, null);187 ring.write(stereo([3, 4]), 9_000_000);188 ring.read(stereo([0, 0]));189 assert.equal(190 ring.snapshot().renderedMediaEndPtsMicros,191 9_000_000 + 2 * AUDIO_FRAME_DURATION_MICROS,192 );193});194195test('counts every silent and underflow frame without rounding duration', () => {196 const ring = new BoundedAudioRing({ capacityFrames: 8, startFrames: 2 });197 const quantum = stereo([0, 0, 0, 0]);198199 ring.write(stereo([1]), 0);200 assert.equal(ring.read(quantum), 0);201 assert.deepEqual(202 {203 silentFrames: ring.snapshot().silentFrames,204 underflowFrames: ring.snapshot().underflowFrames,205 },206 { silentFrames: 4, underflowFrames: 0 },207 );208209 ring.write(stereo([2]), 20_000);210 assert.equal(ring.read(quantum), 2);211 assert.equal(ring.underflows, 1);212 assert.equal(ring.snapshot().silentFrames, 6);213 assert.equal(ring.snapshot().underflowFrames, 2);214 assert.equal(ring.snapshot().renderedMediaEndPtsMicros, null);215216 assert.equal(ring.read(quantum), 0);217 const snapshot = ring.snapshot();218 assert.equal(snapshot.silentFrames, 10);219 assert.equal(snapshot.silentDurationMicros, 10 * AUDIO_FRAME_DURATION_MICROS);220 assert.equal(snapshot.underflowFrames, 6);221 assert.equal(snapshot.underflowDurationMicros, 6 * AUDIO_FRAME_DURATION_MICROS);222});223224test('untimestamped and silent quanta expose a nullable media PTS', () => {225 const ring = new BoundedAudioRing({ capacityFrames: 4, startFrames: 2 });226 ring.write(stereo([1, 2]));227 ring.read(stereo([0, 0]));228 assert.equal(ring.snapshot().renderedMediaEndPtsMicros, null);229230 ring.clear();231 ring.read(stereo([0, 0]));232 assert.equal(ring.snapshot().renderedMediaEndPtsMicros, null);233 assert.throws(() => ring.write(stereo([1, 2]), Number.NaN));234 assert.throws(() => ring.write(stereo([1, 2]), 1.5));235 assert.throws(() => ring.write(stereo([1, 2]), Number.MAX_SAFE_INTEGER + 1));236});237238test('muting consumes media timestamps while preserving intentional silence semantics', () => {239 const ring = new BoundedAudioRing({ capacityFrames: 4, startFrames: 2 });240 ring.write(stereo([1, 2]), 1_000);241 const output = stereo([9, 9]);242 assert.equal(ring.read(output, true), 2);243 assert.deepEqual([...output[0]], [0, 0]);244 assert.equal(ring.snapshot().silentFrames, 0);245 assert.equal(246 ring.snapshot().renderedMediaEndPtsMicros,247 1_000 + 2 * AUDIO_FRAME_DURATION_MICROS,248 );249});