Add [BP-DIAG] back-pressure instrumentation for Phase 21.4 browser run

Temporary, grep-tagged diagnostics at the read-loop pause, the scheduler
latch, and the chunk-result path to show whether ProductionPaused latches,
reaches C#, and parks the loop. Strip once the cause is confirmed.
This commit is contained in:
daniel-c-harvey
2026-06-24 09:00:38 -04:00
parent c7629c15a4
commit 369cb86437
3 changed files with 99 additions and 2 deletions
+28 -2
View File
@@ -269,13 +269,19 @@ export class AudioPlayer {
const headerParsed = decoder.ready;
const canStart = headerParsed && this.scheduler.hasMinimumBuffers(this.minBuffersForPlayback);
// [BP-DIAG] Phase 21.4 — value of productionPaused actually placed on the Opus chunk
// result handed to C#. Confirms the flag is populated on THIS path (not just the WAV
// path). TEMPORARY — strip once confirmed.
const opusPaused = this.scheduler.evaluateProductionPause();
this.bpDiagLogChunkResult('opus', canStart, opusPaused);
return {
success: true,
canStartStreaming: canStart,
headerParsed,
bufferCount: this.scheduler.getBufferCount(),
duration: this.duration,
productionPaused: this.scheduler.evaluateProductionPause()
productionPaused: opusPaused
};
} catch (error) {
return { success: false, error: (error as Error).message };
@@ -314,13 +320,18 @@ export class AudioPlayer {
const canStart = this.streamDecoder.headerParsed &&
this.scheduler.hasMinimumBuffers(this.minBuffersForPlayback);
// [BP-DIAG] Phase 21.4 — value of productionPaused actually placed on the WAV/MP3/FLAC
// chunk result handed to C#. TEMPORARY — strip once confirmed.
const formatPaused = this.scheduler.evaluateProductionPause();
this.bpDiagLogChunkResult('format', canStart, formatPaused);
return {
success: true,
canStartStreaming: canStart,
headerParsed: this.streamDecoder.headerParsed,
bufferCount: this.scheduler.getBufferCount(),
duration: this.duration,
productionPaused: this.scheduler.evaluateProductionPause()
productionPaused: formatPaused
};
} catch (error) {
return { success: false, error: (error as Error).message };
@@ -731,6 +742,21 @@ export class AudioPlayer {
// ==================== Private Methods ====================
// ─────────────────────────────────────────────────────────────────────────────────────────
// [BP-DIAG] Phase 21.4 back-pressure diagnostic. TEMPORARY — strip once confirmed in Daniel's
// browser run. Logs the productionPaused flag on the chunk result handed back to C#, throttled
// to ~4 Hz so it does not flood. Grep "[BP-DIAG] chunk-result" in the browser console.
private bpDiagChunkResultLastMs = 0;
private bpDiagLogChunkResult(path: 'opus' | 'format', canStart: boolean, paused: boolean): void {
const now = (typeof performance !== 'undefined' ? performance.now() : Date.now());
if (now - this.bpDiagChunkResultLastMs < 250) return;
this.bpDiagChunkResultLastMs = now;
console.log(
`[BP-DIAG] chunk-result path=${path} productionPaused=${paused} canStart=${canStart} ` +
`bufCount=${this.scheduler.getBufferCount()} streamingStarted=${this.streamingStarted} isPlaying=${this.isPlaying}`);
}
// ─────────────────────────────────────────────────────────────────────────────────────────
private resetState(): void {
this.isPlaying = false;
this.isPaused = false;
@@ -107,6 +107,14 @@ export class PlaybackScheduler {
// Mutated by evaluateProductionPause() — named to signal the state-advance on each call.
private productionPaused_: boolean = false;
// ─────────────────────────────────────────────────────────────────────────────────────────
// [BP-DIAG] Phase 21.4 back-pressure diagnostic. TEMPORARY — strip once the cause is confirmed
// in Daniel's browser run. Throttles evaluateProductionPause() logging to one line per ~250 ms
// so the console shows the live lookahead / byte-estimate / latch without flooding (the signal
// is evaluated on every chunk + every poll). Grep "[BP-DIAG]" in the browser console.
private bpDiagLastLogMs: number = 0;
// ─────────────────────────────────────────────────────────────────────────────────────────
// Callbacks
public onPlaybackEnded: (() => void) | null = null;
@@ -237,6 +245,22 @@ export class PlaybackScheduler {
this.productionPaused_ = true;
}
// [BP-DIAG] Phase 21.4 — the single source of truth for the latch decision. If `paused`
// never goes true while bytes keep arriving, the lookahead is not growing as expected:
// inspect `lookahead` vs `high` (should cross 30 during a fast fill) and `bufCount`/`bytes`
// (decode must actually be populating the scheduler). Throttled to ~4 Hz. TEMPORARY — strip
// once the cause is confirmed. (Uses performance.now when present; Date.now fallback.)
const bpNow = (typeof performance !== 'undefined' ? performance.now() : Date.now());
if (bpNow - this.bpDiagLastLogMs >= 250) {
this.bpDiagLastLogMs = bpNow;
console.log(
`[BP-DIAG] evaluateProductionPause paused=${this.productionPaused_} ` +
`lookahead=${lookahead.toFixed(2)}s high=${this.forwardHighWaterSeconds} low=${this.forwardLowWaterSeconds} ` +
`bytes=${(this.getDecodedByteEstimate() / (1024 * 1024)).toFixed(1)}MB cap=${(this.maxDecodedBytes / (1024 * 1024)).toFixed(0)}MB ` +
`overByteCeiling=${overByteCeiling} bufCount=${this.buffers.length} pos=${this.getCurrentPosition().toFixed(2)}s ` +
`decodedEnd=${(this.getTotalDuration() + this.playbackOffset).toFixed(2)}s active=${this.isActive_}`);
}
return this.productionPaused_;
}