@buffered-audio/nodes 0.23.0 → 0.24.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,15 +1,16 @@
1
- import { __commonJS, __export, __toESM, deinterleaveBuffer, TruePeakAccumulator, LoudnessAccumulator, AmplitudeHistogramAccumulator, hanningWindow, createFftWorkspace, detectFftBackend, getFftAddon, interleave, IntegratedLufsAccumulator, initFftBackend, linearToDb, stft, applyDfttSmoothing, istft, ResampleStream, bandpass, MixedRadixFft, fft, TruePeakUpsampler, SlidingWindowMaxStream, getLraConsideredStats, BidirectionalIir, applyNlmSmoothingRange, dbToLinear, SlidingWindowMinStream } from './chunk-YJWZP2OD.js';
1
+ import { __commonJS, __export, __toESM, deinterleaveBuffer, TruePeakAccumulator, LoudnessAccumulator, AmplitudeHistogramAccumulator, hanningWindow, createFftWorkspace, detectFftBackend, getFftAddon, interleave, IntegratedLufsAccumulator, initFftBackend, linearToDb, stft, applyDfttSmoothing, istft, bandpass, MixedRadixFft, fft, TruePeakUpsampler, SlidingWindowMaxStream, getLraConsideredStats, BidirectionalIir, applyNlmSmoothingRange, dbToLinear, SlidingWindowMinStream } from './chunk-BKEMKXGG.js';
2
2
  import path, { extname, join } from 'path';
3
3
  import { spawn } from 'child_process';
4
+ import { open, stat, readFile, unlink, mkdtemp, writeFile, rm } from 'fs/promises';
5
+ import { Readable } from 'stream';
4
6
  import { randomUUID } from 'crypto';
5
7
  import { createWriteStream, createReadStream, existsSync } from 'fs';
6
- import { open, stat, readFile, unlink, mkdtemp, writeFile, rm } from 'fs/promises';
7
8
  import os, { tmpdir } from 'os';
8
- import { Readable } from 'stream';
9
9
  import { EventEmitter } from 'events';
10
10
  import { fileURLToPath } from 'url';
11
11
  import { Worker } from 'worker_threads';
12
12
  import { createRequire } from 'module';
13
+ import { StringDecoder } from 'string_decoder';
13
14
 
14
15
  // ../../node_modules/wavefile/dist/wavefile.js
15
16
  var require_wavefile = __commonJS({
@@ -15548,30 +15549,96 @@ var __export2 = (target, all) => {
15548
15549
  for (var name in all)
15549
15550
  __defProp(target, name, { get: all[name], enumerable: true });
15550
15551
  };
15551
- var HIGH_WATER_MARK = 10 * 1024 * 1024;
15552
- var REVERSE_STRIPE_BYTES = 10 * 1024 * 1024;
15553
- function deinterleave(buf, channels) {
15552
+ var BufferedStream = class {
15553
+ constructor(node, context) {
15554
+ this.processingMs = 0;
15555
+ this.destroyed = false;
15556
+ this.node = node;
15557
+ this.events = context.events;
15558
+ const constructor = node.constructor;
15559
+ this.identity = { nodeName: constructor.nodeName, nodeId: node.id, streamId: context.nextStreamId() };
15560
+ }
15561
+ get properties() {
15562
+ return this.node.properties;
15563
+ }
15564
+ emitStarted() {
15565
+ this.events.emit("started", this.identity, { createdAt: Date.now() });
15566
+ }
15567
+ emitFinished(payload) {
15568
+ this.events.emit("finished", this.identity, { ...payload, createdAt: Date.now() });
15569
+ }
15570
+ emitProgress(phase2, framesDone, framesTotal) {
15571
+ this.events.emit("progress", this.identity, { phase: phase2, framesDone, framesTotal, createdAt: Date.now() });
15572
+ }
15573
+ log(message, data, level = "info") {
15574
+ this.events.emit("log", this.identity, { level, message, data, createdAt: Date.now() });
15575
+ }
15576
+ async *timed(source) {
15577
+ const iterator = (async function* () {
15578
+ yield* source;
15579
+ })();
15580
+ try {
15581
+ for (; ; ) {
15582
+ const start = performance.now();
15583
+ const result = await iterator.next();
15584
+ this.processingMs += performance.now() - start;
15585
+ if (result.done) return;
15586
+ yield result.value;
15587
+ }
15588
+ } finally {
15589
+ await iterator.return();
15590
+ }
15591
+ }
15592
+ async destroy() {
15593
+ if (this.destroyed) return;
15594
+ this.destroyed = true;
15595
+ await this._destroy();
15596
+ }
15597
+ _destroy() {
15598
+ return;
15599
+ }
15600
+ };
15601
+ var PROGRESS_PERCENT_QUANTUM = 0.01;
15602
+ var PROGRESS_MIN_INTERVAL_MS = 1e4;
15603
+ function createProgressGate(framesTotal) {
15604
+ let lastBucket = -1;
15605
+ let lastEmitAt;
15606
+ return (framesDone, now) => {
15607
+ const bucket = framesTotal !== void 0 ? Math.floor(framesDone / framesTotal / PROGRESS_PERCENT_QUANTUM) : void 0;
15608
+ const bucketAdvanced = bucket === void 0 || bucket > lastBucket;
15609
+ const intervalPassed = lastEmitAt === void 0 || now - lastEmitAt >= PROGRESS_MIN_INTERVAL_MS;
15610
+ if (!bucketAdvanced || !intervalPassed) return false;
15611
+ if (bucket !== void 0) lastBucket = bucket;
15612
+ lastEmitAt = now;
15613
+ return true;
15614
+ };
15615
+ }
15616
+ async function awaitStreamClose(stream) {
15617
+ if (stream.closed) return;
15618
+ await new Promise((resolve) => {
15619
+ stream.once("close", () => resolve());
15620
+ });
15621
+ }
15622
+ function deinterleave(buffer, channels) {
15554
15623
  const bytesPerFrame = channels * 4;
15555
- const frames = Math.floor(buf.length / bytesPerFrame);
15556
- const interleaved = new Float32Array(buf.buffer, buf.byteOffset, frames * channels);
15557
- const out = [];
15558
- for (let ch = 0; ch < channels; ch++) out.push(new Float32Array(frames));
15624
+ const frames = Math.floor(buffer.length / bytesPerFrame);
15625
+ const interleaved = new Float32Array(buffer.buffer, buffer.byteOffset, frames * channels);
15626
+ const samples = [];
15627
+ for (let channel = 0; channel < channels; channel++) samples.push(new Float32Array(frames));
15559
15628
  for (let frame = 0; frame < frames; frame++) {
15560
15629
  const base = frame * channels;
15561
- for (let ch = 0; ch < channels; ch++) {
15562
- out[ch][frame] = interleaved[base + ch];
15563
- }
15630
+ for (let channel = 0; channel < channels; channel++) samples[channel][frame] = interleaved[base + channel];
15564
15631
  }
15565
- return out;
15632
+ return samples;
15566
15633
  }
15567
15634
  function buildBlock(samples, offset, sampleRate, bitDepth) {
15568
15635
  return { samples, offset, sampleRate: sampleRate ?? 0, bitDepth: bitDepth ?? 0 };
15569
15636
  }
15570
- async function pullBytes(rs, isEnded, bytesNeeded) {
15637
+ async function pullBytes(stream, isEnded, bytesNeeded) {
15571
15638
  const chunks = [];
15572
15639
  let collected = 0;
15573
15640
  while (collected < bytesNeeded) {
15574
- const chunk = rs.read();
15641
+ const chunk = stream.read();
15575
15642
  if (chunk !== null) {
15576
15643
  const remaining = bytesNeeded - collected;
15577
15644
  if (chunk.length <= remaining) {
@@ -15580,35 +15647,154 @@ async function pullBytes(rs, isEnded, bytesNeeded) {
15580
15647
  } else {
15581
15648
  chunks.push(chunk.subarray(0, remaining));
15582
15649
  collected += remaining;
15583
- rs.unshift(chunk.subarray(remaining));
15650
+ stream.unshift(chunk.subarray(remaining));
15584
15651
  }
15585
15652
  continue;
15586
15653
  }
15587
15654
  if (isEnded()) break;
15588
15655
  await new Promise((resolve) => {
15589
15656
  const wake = () => {
15590
- rs.off("readable", wake);
15591
- rs.off("end", wake);
15592
- rs.off("error", wake);
15593
- rs.off("close", wake);
15657
+ stream.off("readable", wake);
15658
+ stream.off("end", wake);
15659
+ stream.off("error", wake);
15660
+ stream.off("close", wake);
15594
15661
  resolve();
15595
15662
  };
15596
- rs.once("readable", wake);
15597
- rs.once("end", wake);
15598
- rs.once("error", wake);
15599
- rs.once("close", wake);
15663
+ stream.once("readable", wake);
15664
+ stream.once("end", wake);
15665
+ stream.once("error", wake);
15666
+ stream.once("close", wake);
15600
15667
  });
15601
15668
  }
15602
15669
  if (chunks.length === 0) return Buffer.alloc(0);
15603
15670
  if (chunks.length === 1) return chunks[0];
15604
15671
  return Buffer.concat(chunks);
15605
15672
  }
15606
- async function awaitStreamClose(stream) {
15607
- if (stream.closed) return;
15608
- await new Promise((resolve) => {
15609
- stream.once("close", () => resolve());
15610
- });
15611
- }
15673
+ var REVERSE_STRIPE_BYTES = 10 * 1024 * 1024;
15674
+ var ReverseBlockReader = class {
15675
+ constructor(path2, metadata, stripeBytes = REVERSE_STRIPE_BYTES, parent) {
15676
+ this.framesReturned = 0;
15677
+ this.closed = false;
15678
+ this.path = path2;
15679
+ this.frames = path2 === void 0 ? 0 : metadata.frames;
15680
+ this.channels = metadata.channels;
15681
+ this.sampleRate = metadata.sampleRate;
15682
+ this.bitDepth = metadata.bitDepth;
15683
+ this.bytesPerFrame = metadata.channels * 4;
15684
+ this.windowBytes = metadata.channels === 0 ? stripeBytes : Math.max(this.bytesPerFrame, Math.floor(stripeBytes / this.bytesPerFrame) * this.bytesPerFrame);
15685
+ this.parent = parent;
15686
+ }
15687
+ async read(frames) {
15688
+ if (this.closed) throw new Error("ReverseBlockReader: read() after close()");
15689
+ const offset = this.framesReturned;
15690
+ const remaining = this.frames - this.framesReturned;
15691
+ if (this.path === void 0 || this.channels === 0 || frames <= 0 || remaining <= 0) {
15692
+ return buildBlock([], offset, this.sampleRate, this.bitDepth);
15693
+ }
15694
+ const count = Math.min(frames, remaining);
15695
+ const stream = this.ensureStream();
15696
+ const buffer = await pullBytes(stream, () => stream.destroyed || stream.readableEnded, count * this.bytesPerFrame);
15697
+ const actualFrames = Math.floor(buffer.length / this.bytesPerFrame);
15698
+ if (actualFrames < count) throw this.streamError ?? new Error("ReverseBlockReader: unexpected end of reverse stream");
15699
+ this.framesReturned += actualFrames;
15700
+ return buildBlock(deinterleave(buffer, this.channels), offset, this.sampleRate, this.bitDepth);
15701
+ }
15702
+ async *iterate(frames) {
15703
+ for (; ; ) {
15704
+ const block = await this.read(frames);
15705
+ if ((block.samples[0]?.length ?? 0) === 0) return;
15706
+ yield block;
15707
+ }
15708
+ }
15709
+ async close() {
15710
+ if (this.closed) return;
15711
+ this.closed = true;
15712
+ const stream = this.stream;
15713
+ this.stream = void 0;
15714
+ if (stream) {
15715
+ stream.destroy();
15716
+ await awaitStreamClose(stream);
15717
+ }
15718
+ this.parent?.deregisterReverseReader(this);
15719
+ }
15720
+ ensureStream() {
15721
+ if (this.stream) return this.stream;
15722
+ if (this.path === void 0) throw new Error("ReverseBlockReader: no source file");
15723
+ const totalBytes = this.frames * this.bytesPerFrame;
15724
+ const stream = new ReverseReadable(this.path, totalBytes, this.bytesPerFrame, this.windowBytes);
15725
+ stream.once("error", (error50) => {
15726
+ this.streamError = error50;
15727
+ });
15728
+ this.stream = stream;
15729
+ return stream;
15730
+ }
15731
+ };
15732
+ var ReverseReadable = class extends Readable {
15733
+ constructor(path2, totalBytes, bytesPerFrame, windowBytes) {
15734
+ super({ highWaterMark: windowBytes });
15735
+ this.path = path2;
15736
+ this.bytesPerFrame = bytesPerFrame;
15737
+ this.windowBytes = windowBytes;
15738
+ this.position = totalBytes;
15739
+ }
15740
+ _read() {
15741
+ void this.readWindow();
15742
+ }
15743
+ async readWindow() {
15744
+ try {
15745
+ if (this.position <= 0) {
15746
+ this.push(null);
15747
+ return;
15748
+ }
15749
+ const startByte = Math.max(0, this.position - this.windowBytes);
15750
+ const length = this.position - startByte;
15751
+ const buffer = Buffer.alloc(length);
15752
+ await this.readFully(buffer, startByte);
15753
+ this.reverseFramesInPlace(buffer);
15754
+ this.position = startByte;
15755
+ this.push(buffer);
15756
+ } catch (error50) {
15757
+ this.destroy(error50);
15758
+ }
15759
+ }
15760
+ reverseFramesInPlace(buffer) {
15761
+ const frameCount = Math.floor(buffer.length / this.bytesPerFrame);
15762
+ const scratch = Buffer.alloc(this.bytesPerFrame);
15763
+ for (let low = 0, high = frameCount - 1; low < high; low++, high--) {
15764
+ const lowByte = low * this.bytesPerFrame;
15765
+ const highByte = high * this.bytesPerFrame;
15766
+ buffer.copy(scratch, 0, lowByte, lowByte + this.bytesPerFrame);
15767
+ buffer.copy(buffer, lowByte, highByte, highByte + this.bytesPerFrame);
15768
+ scratch.copy(buffer, highByte, 0, this.bytesPerFrame);
15769
+ }
15770
+ }
15771
+ async readFully(target, position) {
15772
+ const handle = await this.ensureHandle();
15773
+ let filled = 0;
15774
+ while (filled < target.length) {
15775
+ const { bytesRead } = await handle.read(target, filled, target.length - filled, position + filled);
15776
+ if (bytesRead === 0) throw new Error(`ReverseReadable: unexpected EOF at byte ${position + filled}`);
15777
+ filled += bytesRead;
15778
+ }
15779
+ }
15780
+ ensureHandle() {
15781
+ this.handlePromise ?? (this.handlePromise = open(this.path, "r"));
15782
+ return this.handlePromise;
15783
+ }
15784
+ _destroy(error50, callback) {
15785
+ const handlePromise = this.handlePromise;
15786
+ this.handlePromise = void 0;
15787
+ if (!handlePromise) {
15788
+ callback(error50);
15789
+ return;
15790
+ }
15791
+ void handlePromise.then((handle) => handle.close()).then(
15792
+ () => callback(error50),
15793
+ (closeError) => callback(error50 ?? (closeError instanceof Error ? closeError : new Error(String(closeError))))
15794
+ );
15795
+ }
15796
+ };
15797
+ var HIGH_WATER_MARK = 10 * 1024 * 1024;
15612
15798
  var BlockBuffer = class {
15613
15799
  constructor() {
15614
15800
  this._frames = 0;
@@ -15645,50 +15831,37 @@ var BlockBuffer = class {
15645
15831
  const channels = this._channels;
15646
15832
  const interleaved = new Float32Array(duration4 * channels);
15647
15833
  for (let frame = 0; frame < duration4; frame++) {
15648
- for (let ch = 0; ch < channels; ch++) {
15649
- const src = samples[ch];
15650
- interleaved[frame * channels + ch] = src ? src[frame] ?? 0 : 0;
15834
+ for (let channel = 0; channel < channels; channel++) {
15835
+ const sourceSamples = samples[channel];
15836
+ interleaved[frame * channels + channel] = sourceSamples ? sourceSamples[frame] ?? 0 : 0;
15651
15837
  }
15652
15838
  }
15653
- const buf = Buffer.from(interleaved.buffer, interleaved.byteOffset, interleaved.byteLength);
15839
+ const writeBuffer = Buffer.from(interleaved.buffer, interleaved.byteOffset, interleaved.byteLength);
15654
15840
  const ws = this.ensureWriteStream();
15655
- const ok = ws.write(buf);
15841
+ const ok = ws.write(writeBuffer);
15656
15842
  if (!ok) {
15657
15843
  await new Promise((resolve) => ws.once("drain", () => resolve()));
15658
15844
  }
15659
- this.writePositionByte += buf.length;
15845
+ this.writePositionByte += writeBuffer.length;
15660
15846
  const writtenFrames = Math.floor(this.writePositionByte / (this._channels * 4));
15661
15847
  if (writtenFrames > this._frames) this._frames = writtenFrames;
15662
15848
  }
15663
15849
  async flushWrites() {
15664
15850
  await this.endWriteStream();
15665
15851
  }
15666
- // Opens a read-only reverse view over this buffer's temp file. Flushes pending writes first, then
15667
- // snapshots frames/channels/sampleRate/bitDepth so the reader is stable against later reads on the
15668
- // source. The reader is registered here and closed by clear()/close() (Windows blocks unlink while a
15669
- // handle is open — same EBUSY guard the forward read stream uses in endReadStream). See the
15670
- // ReverseBlockReader class comment for the full borrow contract.
15671
15852
  async openReverseReader() {
15672
15853
  await this.flushWrites();
15673
- const reader = new ReverseBlockReader(
15674
- this.tempPath,
15675
- { frames: this._frames, channels: this._channels, sampleRate: this._sampleRate, bitDepth: this._bitDepth },
15676
- REVERSE_STRIPE_BYTES,
15677
- this
15678
- );
15854
+ const reader = new ReverseBlockReader(this.tempPath, { frames: this._frames, channels: this._channels, sampleRate: this._sampleRate, bitDepth: this._bitDepth }, void 0, this);
15679
15855
  this.reverseReaders.add(reader);
15680
15856
  return reader;
15681
15857
  }
15682
- // Called by a factory-created reader from its close() so it deregisters itself. Idempotent.
15683
15858
  deregisterReverseReader(reader) {
15684
15859
  this.reverseReaders.delete(reader);
15685
15860
  }
15686
15861
  async closeReverseReaders() {
15687
15862
  const readers = [...this.reverseReaders];
15688
15863
  this.reverseReaders.clear();
15689
- for (const reader of readers) {
15690
- await reader.close().catch(() => void 0);
15691
- }
15864
+ for (const reader of readers) await reader.close().catch(() => void 0);
15692
15865
  }
15693
15866
  async read(frames) {
15694
15867
  const channels = this._channels;
@@ -15698,11 +15871,11 @@ var BlockBuffer = class {
15698
15871
  }
15699
15872
  const bytesPerFrame = channels * 4;
15700
15873
  const rs = this.ensureReadStream();
15701
- const buf = await pullBytes(rs, () => rs.destroyed || this.readStreamEnded, frames * bytesPerFrame);
15702
- const actualFrames = Math.floor(buf.length / bytesPerFrame);
15874
+ const readBuffer = await pullBytes(rs, () => rs.destroyed || this.readStreamEnded, frames * bytesPerFrame);
15875
+ const actualFrames = Math.floor(readBuffer.length / bytesPerFrame);
15703
15876
  if (actualFrames <= 0) return buildBlock([], startFrame, this._sampleRate, this._bitDepth);
15704
15877
  this.framesReadInSession += actualFrames;
15705
- const out = deinterleave(buf, channels);
15878
+ const out = deinterleave(readBuffer, channels);
15706
15879
  return buildBlock(out, startFrame, this._sampleRate, this._bitDepth);
15707
15880
  }
15708
15881
  async *iterate(frames) {
@@ -15820,210 +15993,6 @@ var BlockBuffer = class {
15820
15993
  await awaitStreamClose(rs);
15821
15994
  }
15822
15995
  };
15823
- var ReverseBlockReader = class {
15824
- constructor(path2, meta4, stripeBytes = REVERSE_STRIPE_BYTES, parent) {
15825
- this.framesReturned = 0;
15826
- this.closed = false;
15827
- this.path = path2;
15828
- this.frames = path2 === void 0 ? 0 : meta4.frames;
15829
- this.channels = meta4.channels;
15830
- this.sampleRate = meta4.sampleRate;
15831
- this.bitDepth = meta4.bitDepth;
15832
- this.bytesPerFrame = meta4.channels * 4;
15833
- this.windowBytes = meta4.channels === 0 ? stripeBytes : Math.max(this.bytesPerFrame, Math.floor(stripeBytes / this.bytesPerFrame) * this.bytesPerFrame);
15834
- this.parent = parent;
15835
- }
15836
- async read(frames) {
15837
- if (this.closed) {
15838
- throw new Error("ReverseBlockReader: read() after close()");
15839
- }
15840
- const offset = this.framesReturned;
15841
- const remaining = this.frames - this.framesReturned;
15842
- if (this.path === void 0 || this.channels === 0 || frames <= 0 || remaining <= 0) {
15843
- return buildBlock([], offset, this.sampleRate, this.bitDepth);
15844
- }
15845
- const count = Math.min(frames, remaining);
15846
- const rs = this.ensureStream();
15847
- const buf = await pullBytes(rs, () => rs.destroyed || rs.readableEnded, count * this.bytesPerFrame);
15848
- const actualFrames = Math.floor(buf.length / this.bytesPerFrame);
15849
- if (actualFrames < count) {
15850
- throw this.streamError ?? new Error("ReverseBlockReader: unexpected end of reverse stream");
15851
- }
15852
- this.framesReturned += actualFrames;
15853
- const out = deinterleave(buf, this.channels);
15854
- return buildBlock(out, offset, this.sampleRate, this.bitDepth);
15855
- }
15856
- async *iterate(frames) {
15857
- for (; ; ) {
15858
- const block = await this.read(frames);
15859
- if ((block.samples[0]?.length ?? 0) === 0) return;
15860
- yield block;
15861
- }
15862
- }
15863
- async close() {
15864
- if (this.closed) return;
15865
- this.closed = true;
15866
- const stream = this.stream;
15867
- this.stream = void 0;
15868
- if (stream) {
15869
- stream.destroy();
15870
- await awaitStreamClose(stream);
15871
- }
15872
- this.parent?.deregisterReverseReader(this);
15873
- }
15874
- ensureStream() {
15875
- if (this.stream) return this.stream;
15876
- if (this.path === void 0) {
15877
- throw new Error("ReverseBlockReader: no source file");
15878
- }
15879
- const totalBytes = this.frames * this.bytesPerFrame;
15880
- const stream = new ReverseReadable(this.path, totalBytes, this.bytesPerFrame, this.windowBytes);
15881
- stream.once("error", (error50) => {
15882
- this.streamError = error50;
15883
- });
15884
- this.stream = stream;
15885
- return stream;
15886
- }
15887
- };
15888
- var ReverseReadable = class extends Readable {
15889
- constructor(path2, totalBytes, bytesPerFrame, windowBytes) {
15890
- super({ highWaterMark: windowBytes });
15891
- this.path = path2;
15892
- this.bytesPerFrame = bytesPerFrame;
15893
- this.windowBytes = windowBytes;
15894
- this.pos = totalBytes;
15895
- }
15896
- // Emits the next backward window [max(0, pos - windowBytes), pos), with the frame order reversed in
15897
- // place (channel order INSIDE each frame preserved — interleaved layout intact), then walks pos toward
15898
- // 0 and pushes null at start-of-file. Node does not consume a rejected _read promise — an uncaught
15899
- // throw becomes an unhandled rejection and the stream never errors — so any failure is routed to
15900
- // destroy(err), which fires the 'error'/'close' events pullBytes waits on.
15901
- _read() {
15902
- void this.readWindow();
15903
- }
15904
- async readWindow() {
15905
- try {
15906
- if (this.pos <= 0) {
15907
- this.push(null);
15908
- return;
15909
- }
15910
- const startByte = Math.max(0, this.pos - this.windowBytes);
15911
- const length = this.pos - startByte;
15912
- const buf = Buffer.alloc(length);
15913
- await this.readFully(buf, startByte);
15914
- this.reverseFramesInPlace(buf);
15915
- this.pos = startByte;
15916
- this.push(buf);
15917
- } catch (error50) {
15918
- this.destroy(error50);
15919
- }
15920
- }
15921
- // Reverse the frame order within the window in place: frame i swaps with frame (frameCount - 1 - i),
15922
- // each frame being one bytesPerFrame-wide slice. Channel order inside a frame is untouched, so the
15923
- // interleaved layout survives — only the time order flips.
15924
- reverseFramesInPlace(buf) {
15925
- const frameCount = Math.floor(buf.length / this.bytesPerFrame);
15926
- const scratch = Buffer.alloc(this.bytesPerFrame);
15927
- for (let low = 0, high = frameCount - 1; low < high; low++, high--) {
15928
- const lowByte = low * this.bytesPerFrame;
15929
- const highByte = high * this.bytesPerFrame;
15930
- buf.copy(scratch, 0, lowByte, lowByte + this.bytesPerFrame);
15931
- buf.copy(buf, lowByte, highByte, highByte + this.bytesPerFrame);
15932
- scratch.copy(buf, highByte, 0, this.bytesPerFrame);
15933
- }
15934
- }
15935
- // The one centralized positioned read-fully helper. Loops on handle.read and throws on a zero-byte
15936
- // read (unexpected EOF) — the shared guard against the former unchecked-bytesRead short-read bug.
15937
- async readFully(target, position) {
15938
- const handle = await this.ensureHandle();
15939
- let filled = 0;
15940
- while (filled < target.length) {
15941
- const { bytesRead } = await handle.read(target, filled, target.length - filled, position + filled);
15942
- if (bytesRead === 0) {
15943
- throw new Error(`ReverseReadable: unexpected EOF at byte ${position + filled}`);
15944
- }
15945
- filled += bytesRead;
15946
- }
15947
- }
15948
- async ensureHandle() {
15949
- if (this.handle) return this.handle;
15950
- this.handle = await open(this.path, "r");
15951
- return this.handle;
15952
- }
15953
- _destroy(error50, callback) {
15954
- const handle = this.handle;
15955
- this.handle = void 0;
15956
- if (!handle) {
15957
- callback(error50);
15958
- return;
15959
- }
15960
- handle.close().then(() => callback(error50)).catch((closeError) => callback(error50 ?? closeError));
15961
- }
15962
- };
15963
- var PROGRESS_PERCENT_QUANTUM = 0.01;
15964
- var PROGRESS_MIN_INTERVAL_MS = 1e4;
15965
- function createProgressGate(framesTotal) {
15966
- let lastBucket = -1;
15967
- let lastEmitAt;
15968
- return (framesDone, now) => {
15969
- const bucket = framesTotal !== void 0 ? Math.floor(framesDone / framesTotal / PROGRESS_PERCENT_QUANTUM) : void 0;
15970
- const bucketAdvanced = bucket === void 0 || bucket > lastBucket;
15971
- const intervalPassed = lastEmitAt === void 0 || now - lastEmitAt >= PROGRESS_MIN_INTERVAL_MS;
15972
- if (!bucketAdvanced || !intervalPassed) return false;
15973
- if (bucket !== void 0) lastBucket = bucket;
15974
- lastEmitAt = now;
15975
- return true;
15976
- };
15977
- }
15978
- var BufferedStream = class {
15979
- constructor(node, context) {
15980
- this.processingMs = 0;
15981
- this.destroyed = false;
15982
- this.node = node;
15983
- this.events = context.events;
15984
- const constructor = node.constructor;
15985
- this.identity = { nodeName: constructor.nodeName, nodeId: node.id, streamId: context.nextStreamId() };
15986
- }
15987
- get properties() {
15988
- return this.node.properties;
15989
- }
15990
- emitStarted() {
15991
- this.events.emit("started", this.identity, { createdAt: Date.now() });
15992
- }
15993
- emitFinished(payload) {
15994
- this.events.emit("finished", this.identity, { ...payload, createdAt: Date.now() });
15995
- }
15996
- emitProgress(phase2, framesDone, framesTotal) {
15997
- this.events.emit("progress", this.identity, { phase: phase2, framesDone, framesTotal, createdAt: Date.now() });
15998
- }
15999
- log(message, data, level = "info") {
16000
- this.events.emit("log", this.identity, { level, message, data, createdAt: Date.now() });
16001
- }
16002
- async *timed(source) {
16003
- const iterator = (async function* () {
16004
- yield* source;
16005
- })();
16006
- try {
16007
- for (; ; ) {
16008
- const start = performance.now();
16009
- const result = await iterator.next();
16010
- this.processingMs += performance.now() - start;
16011
- if (result.done) return;
16012
- yield result.value;
16013
- }
16014
- } finally {
16015
- await iterator.return();
16016
- }
16017
- }
16018
- async destroy() {
16019
- if (this.destroyed) return;
16020
- this.destroyed = true;
16021
- await this._destroy();
16022
- }
16023
- _destroy() {
16024
- return;
16025
- }
16026
- };
16027
15996
  function sliceBlock(block, offset, frames) {
16028
15997
  if (offset === 0 && frames === (block.samples[0]?.length ?? 0)) return block;
16029
15998
  return {
@@ -16072,7 +16041,7 @@ var BufferedTransformStream = class extends BufferedStream {
16072
16041
  return this.properties.streamChunkSize;
16073
16042
  }
16074
16043
  async setup(input, context) {
16075
- this.sourceTotalFrames = context.durationFrames;
16044
+ this.sourceTotalFrames = context.sourceTotalFrames;
16076
16045
  await this._setup(context);
16077
16046
  return this._pipe(input);
16078
16047
  }
@@ -16168,7 +16137,7 @@ var UnbufferedTransformStream = class extends BufferedStream {
16168
16137
  this.hasStarted = false;
16169
16138
  }
16170
16139
  async setup(input, context) {
16171
- this.sourceTotalFrames = context.durationFrames;
16140
+ this.sourceTotalFrames = context.sourceTotalFrames;
16172
16141
  await this._setup(context);
16173
16142
  return this._pipe(input);
16174
16143
  }
@@ -23372,11 +23341,11 @@ var capitalizeFirstCharacter2 = (text) => {
23372
23341
  };
23373
23342
  function getUnitTypeFromNumber2(number42) {
23374
23343
  const abs = Math.abs(number42);
23375
- const last = abs % 10;
23376
- const last2 = abs % 100;
23377
- if (last2 >= 11 && last2 <= 19 || last === 0)
23344
+ const last2 = abs % 10;
23345
+ const last22 = abs % 100;
23346
+ if (last22 >= 11 && last22 <= 19 || last2 === 0)
23378
23347
  return "many";
23379
- if (last === 1)
23348
+ if (last2 === 1)
23380
23349
  return "one";
23381
23350
  return "few";
23382
23351
  }
@@ -29856,75 +29825,6 @@ var BufferedAudioNode = class {
29856
29825
  BufferedAudioNode.apiVersion = 1;
29857
29826
  BufferedAudioNode.description = "";
29858
29827
  BufferedAudioNode.schema = external_exports2.object({});
29859
- var BufferedSourceStream = class extends BufferedStream {
29860
- constructor() {
29861
- super(...arguments);
29862
- this.framesRead = 0;
29863
- this.hasStarted = false;
29864
- }
29865
- setup(context) {
29866
- return Promise.resolve(this._setup(context));
29867
- }
29868
- _setup(context) {
29869
- let done = false;
29870
- this.framesRead = 0;
29871
- this.processingMs = 0;
29872
- this.hasStarted = false;
29873
- const { signal, durationFrames: sourceTotalFrames, highWaterMark } = context;
29874
- const readGate = createProgressGate(sourceTotalFrames);
29875
- return new ReadableStream(
29876
- {
29877
- pull: async (controller) => {
29878
- if (done) return;
29879
- if (signal?.aborted) {
29880
- done = true;
29881
- await this.destroy();
29882
- controller.close();
29883
- return;
29884
- }
29885
- try {
29886
- if (!this.hasStarted) {
29887
- this.hasStarted = true;
29888
- this.emitStarted();
29889
- }
29890
- const start = performance.now();
29891
- const chunk = await this._read();
29892
- this.processingMs += performance.now() - start;
29893
- if (!chunk) {
29894
- done = true;
29895
- this.emitProgress("read", this.framesRead, sourceTotalFrames);
29896
- this.emitFinished({ framesDone: this.framesRead, processingMs: this.processingMs });
29897
- await this.destroy();
29898
- controller.close();
29899
- return;
29900
- }
29901
- this.framesRead += chunk.samples[0]?.length ?? 0;
29902
- controller.enqueue(chunk);
29903
- if (readGate(this.framesRead, Date.now())) this.emitProgress("read", this.framesRead, sourceTotalFrames);
29904
- } catch (error482) {
29905
- done = true;
29906
- await this.destroy();
29907
- controller.error(error482);
29908
- }
29909
- },
29910
- cancel: async () => {
29911
- done = true;
29912
- await this.destroy();
29913
- }
29914
- },
29915
- { highWaterMark }
29916
- );
29917
- }
29918
- };
29919
- var SourceNode = class extends BufferedAudioNode {
29920
- to(child) {
29921
- const head = "head" in child ? child.head : child;
29922
- this.properties = { ...this.properties, children: [...this.properties.children ?? [], head] };
29923
- }
29924
- createRenderJob(options) {
29925
- return new RenderJob(this, options);
29926
- }
29927
- };
29928
29828
  var BufferedTargetStream = class extends BufferedStream {
29929
29829
  constructor() {
29930
29830
  super(...arguments);
@@ -29932,7 +29832,7 @@ var BufferedTargetStream = class extends BufferedStream {
29932
29832
  this.framesWritten = 0;
29933
29833
  }
29934
29834
  setup(readable, context) {
29935
- this.sourceTotalFrames = context.durationFrames;
29835
+ this.sourceTotalFrames = context.sourceTotalFrames;
29936
29836
  return Promise.resolve(this._setup(readable, context));
29937
29837
  }
29938
29838
  _setup(input, _context) {
@@ -29971,23 +29871,106 @@ var BufferedTargetStream = class extends BufferedStream {
29971
29871
  };
29972
29872
  var TargetNode = class extends BufferedAudioNode {
29973
29873
  };
29874
+ function assertFirstBlockSampleRate(readable, expected, nodeName) {
29875
+ const reader = readable.getReader();
29876
+ let checked = false;
29877
+ return new ReadableStream({
29878
+ pull: async (controller) => {
29879
+ const result = await reader.read();
29880
+ if (result.done) {
29881
+ controller.close();
29882
+ return;
29883
+ }
29884
+ if (!checked) {
29885
+ checked = true;
29886
+ if (result.value.sampleRate !== expected) {
29887
+ controller.error(
29888
+ new Error(
29889
+ `${nodeName}: emitted ${result.value.sampleRate} Hz where ${expected} Hz was declared \u2014 a rate-changing stream must assign context.sampleRate in _setup`
29890
+ )
29891
+ );
29892
+ await reader.cancel();
29893
+ return;
29894
+ }
29895
+ }
29896
+ controller.enqueue(result.value);
29897
+ },
29898
+ cancel: async (reason) => {
29899
+ await reader.cancel(reason);
29900
+ }
29901
+ });
29902
+ }
29974
29903
  function teeReadable(readable, items) {
29975
29904
  if (items.length === 0) return [];
29976
29905
  const first = items[0];
29977
29906
  if (items.length === 1) return [[readable, first]];
29978
- const pairs = [];
29979
- let current = readable;
29980
- for (let offset = 0; offset < items.length - 1; offset++) {
29981
- const [left, right] = current.tee();
29982
- pairs.push([left, items[offset]]);
29983
- current = right;
29984
- }
29985
- pairs.push([current, items[items.length - 1]]);
29986
- return pairs;
29987
- }
29988
- function isSourceStream(stream) {
29989
- return stream instanceof BufferedSourceStream;
29907
+ const reader = readable.getReader();
29908
+ const branches = [];
29909
+ let serving = false;
29910
+ let finished = false;
29911
+ const settleDemand = (branch) => {
29912
+ branch.demandResolvers.shift()?.();
29913
+ };
29914
+ const settleAllDemand = (branch) => {
29915
+ while (branch.demandResolvers.length > 0) settleDemand(branch);
29916
+ };
29917
+ const liveBranches = () => branches.filter((branch) => !branch.cancelled);
29918
+ const serve = async () => {
29919
+ if (serving) return;
29920
+ serving = true;
29921
+ try {
29922
+ while (!finished) {
29923
+ const demanding = liveBranches();
29924
+ if (demanding.length === 0) return;
29925
+ if (!demanding.every((branch) => branch.demandResolvers.length > 0)) return;
29926
+ let result;
29927
+ try {
29928
+ result = await reader.read();
29929
+ } catch (error482) {
29930
+ finished = true;
29931
+ for (const branch of liveBranches()) {
29932
+ branch.controller.error(error482);
29933
+ settleAllDemand(branch);
29934
+ }
29935
+ return;
29936
+ }
29937
+ for (const branch of liveBranches()) {
29938
+ if (result.done) branch.controller.close();
29939
+ else branch.controller.enqueue(result.value);
29940
+ settleDemand(branch);
29941
+ }
29942
+ if (result.done) finished = true;
29943
+ }
29944
+ } finally {
29945
+ serving = false;
29946
+ }
29947
+ };
29948
+ const streams = items.map(() => {
29949
+ let branch;
29950
+ return new ReadableStream({
29951
+ start: (controller) => {
29952
+ branch = { controller, demandResolvers: [], cancelled: false };
29953
+ branches.push(branch);
29954
+ },
29955
+ pull: () => new Promise((resolve) => {
29956
+ branch.demandResolvers.push(resolve);
29957
+ void serve();
29958
+ }),
29959
+ cancel: async (reason) => {
29960
+ branch.cancelled = true;
29961
+ settleAllDemand(branch);
29962
+ if (branches.every((entry) => entry.cancelled)) {
29963
+ finished = true;
29964
+ await reader.cancel(reason);
29965
+ return;
29966
+ }
29967
+ void serve();
29968
+ }
29969
+ });
29970
+ });
29971
+ return streams.map((stream, offset) => [stream, items[offset]]);
29990
29972
  }
29973
+ var RENDER_LIVENESS_INTERVAL_MS = 3e4;
29991
29974
  var RenderJob = class {
29992
29975
  constructor(source, options) {
29993
29976
  this.options = options;
@@ -29998,10 +29981,11 @@ var RenderJob = class {
29998
29981
  let streamIdCounter = 0;
29999
29982
  this.renderContext = { events: this.events, nextStreamId: () => streamIdCounter++ };
30000
29983
  this.root = this.build(source, /* @__PURE__ */ new Set());
30001
- if (!isSourceStream(this.root.stream)) {
29984
+ const sourceStream = this.root.stream;
29985
+ if (!(sourceStream instanceof BufferedSourceStream)) {
30002
29986
  throw new Error("Source node did not produce a source stream");
30003
29987
  }
30004
- this.sourceStream = this.root.stream;
29988
+ this.sourceStream = sourceStream;
30005
29989
  }
30006
29990
  get streams() {
30007
29991
  return this.streamsMap;
@@ -30052,53 +30036,65 @@ var RenderJob = class {
30052
30036
  async render() {
30053
30037
  if (this.started) throw new Error("RenderJob is single-use; render() was already called");
30054
30038
  this.started = true;
30055
- const meta32 = await this.sourceStream.getMetadata();
30056
- const defaultProviders = ["gpu", "cpu-native", "cpu"];
30057
- const memoryLimit = this.options?.memoryLimit ?? 256 * 1024 * 1024;
30058
- const stages = Math.max(1, this.countStreams());
30059
- const chunkSize = this.options?.chunkSize ?? 128 * 1024;
30060
- const bytesPerChunk = meta32.channels * chunkSize * 4;
30061
- const computedHighWaterMark = Math.max(1, Math.floor(memoryLimit / (stages * bytesPerChunk)));
30062
- const context = {
30063
- executionProviders: this.options?.executionProviders ?? defaultProviders,
30064
- memoryLimit,
30065
- durationFrames: meta32.durationFrames,
30066
- highWaterMark: this.options?.highWaterMark ?? computedHighWaterMark,
30067
- signal: this.signal()
30068
- };
30069
- const start = performance.now();
30039
+ const renderCalledAt = performance.now();
30040
+ const livenessInterval = setInterval(() => {
30041
+ this.events.emit("liveness", { createdAt: Date.now(), elapsedMs: performance.now() - renderCalledAt });
30042
+ }, RENDER_LIVENESS_INTERVAL_MS);
30070
30043
  try {
30071
- const readable = await this.sourceStream.setup(context);
30072
- const promises = await this.wireChildren(this.root.children, readable, context);
30073
- await Promise.all(promises);
30074
- } finally {
30075
- for (const streams of this.streamsMap.values()) {
30076
- for (const stream of streams) {
30077
- await stream.destroy();
30044
+ const meta32 = await this.sourceStream.getMetadata();
30045
+ const defaultProviders = ["gpu", "cpu-native", "cpu"];
30046
+ const memoryLimit = this.options?.memoryLimit ?? 256 * 1024 * 1024;
30047
+ const stages = Math.max(1, this.countStreams());
30048
+ const chunkSize = this.options?.chunkSize ?? 128 * 1024;
30049
+ const bytesPerChunk = meta32.channels * chunkSize * 4;
30050
+ const computedHighWaterMark = Math.max(1, Math.floor(memoryLimit / (stages * bytesPerChunk)));
30051
+ const context = {
30052
+ executionProviders: this.options?.executionProviders ?? defaultProviders,
30053
+ memoryLimit,
30054
+ sourceSampleRate: meta32.sampleRate,
30055
+ sampleRate: meta32.sampleRate,
30056
+ sourceTotalFrames: meta32.durationFrames,
30057
+ highWaterMark: this.options?.highWaterMark ?? computedHighWaterMark,
30058
+ signal: this.signal()
30059
+ };
30060
+ const start = performance.now();
30061
+ try {
30062
+ const readable = await this.sourceStream.setup(context);
30063
+ const sourceName = this.root.node.constructor.nodeName;
30064
+ const promises = await this.setupChildren(this.root.children, assertFirstBlockSampleRate(readable, context.sampleRate, sourceName), context);
30065
+ await Promise.all(promises);
30066
+ } finally {
30067
+ for (const streams of this.streamsMap.values()) {
30068
+ for (const stream of streams) {
30069
+ await stream.destroy();
30070
+ }
30078
30071
  }
30072
+ const totalMs = performance.now() - start;
30073
+ const audioDurationMs = meta32.durationFrames !== void 0 ? meta32.durationFrames / meta32.sampleRate * 1e3 : 0;
30074
+ this.timingData = {
30075
+ totalMs,
30076
+ audioDurationMs,
30077
+ realTimeMultiplier: audioDurationMs > 0 ? audioDurationMs / totalMs : 0
30078
+ };
30079
30079
  }
30080
- const totalMs = performance.now() - start;
30081
- const audioDurationMs = meta32.durationFrames !== void 0 ? meta32.durationFrames / meta32.sampleRate * 1e3 : 0;
30082
- this.timingData = {
30083
- totalMs,
30084
- audioDurationMs,
30085
- realTimeMultiplier: audioDurationMs > 0 ? audioDurationMs / totalMs : 0
30086
- };
30080
+ } finally {
30081
+ clearInterval(livenessInterval);
30087
30082
  }
30088
30083
  }
30089
- async wireChildren(children, readable, context) {
30084
+ async setupChildren(children, readable, context) {
30090
30085
  const pairs = teeReadable(readable, children);
30091
- const nested = await Promise.all(pairs.map(([branch, child]) => this.wire(child, branch, context)));
30086
+ const nested = await Promise.all(pairs.map(([branch, child]) => this.setup(child, branch, { ...context })));
30092
30087
  return nested.flat();
30093
30088
  }
30094
- async wire(plan, input, context) {
30089
+ async setup(plan, input, context) {
30095
30090
  const { stream } = plan;
30096
30091
  if (stream instanceof BufferedTargetStream) {
30097
30092
  return [stream.setup(input, context)];
30098
30093
  }
30099
30094
  if (stream instanceof BufferedTransformStream || stream instanceof UnbufferedTransformStream) {
30095
+ const nodeName = plan.node.constructor.nodeName;
30100
30096
  const output = await stream.setup(input, context);
30101
- return this.wireChildren(plan.children, output, context);
30097
+ return this.setupChildren(plan.children, assertFirstBlockSampleRate(output, context.sampleRate, nodeName), context);
30102
30098
  }
30103
30099
  throw new Error(`Unexpected stream type for node "${plan.node.constructor.nodeName}"`);
30104
30100
  }
@@ -30112,12 +30108,114 @@ var RenderJob = class {
30112
30108
  return AbortSignal.any([this.options.signal, this.abortController.signal]);
30113
30109
  }
30114
30110
  };
30111
+ var BufferedSourceStream = class extends BufferedStream {
30112
+ constructor() {
30113
+ super(...arguments);
30114
+ this.framesRead = 0;
30115
+ this.hasStarted = false;
30116
+ }
30117
+ setup(context) {
30118
+ return Promise.resolve(this._setup(context));
30119
+ }
30120
+ _setup(context) {
30121
+ let done = false;
30122
+ this.framesRead = 0;
30123
+ this.processingMs = 0;
30124
+ this.hasStarted = false;
30125
+ const { signal, sourceTotalFrames, highWaterMark } = context;
30126
+ const readGate = createProgressGate(sourceTotalFrames);
30127
+ return new ReadableStream(
30128
+ {
30129
+ pull: async (controller) => {
30130
+ if (done) return;
30131
+ if (signal?.aborted) {
30132
+ done = true;
30133
+ await this.destroy();
30134
+ controller.close();
30135
+ return;
30136
+ }
30137
+ try {
30138
+ if (!this.hasStarted) {
30139
+ this.hasStarted = true;
30140
+ this.emitStarted();
30141
+ }
30142
+ const start = performance.now();
30143
+ const chunk = await this._read();
30144
+ this.processingMs += performance.now() - start;
30145
+ if (!chunk) {
30146
+ done = true;
30147
+ this.emitProgress("read", this.framesRead, sourceTotalFrames);
30148
+ this.emitFinished({ framesDone: this.framesRead, processingMs: this.processingMs });
30149
+ await this.destroy();
30150
+ controller.close();
30151
+ return;
30152
+ }
30153
+ this.framesRead += chunk.samples[0]?.length ?? 0;
30154
+ controller.enqueue(chunk);
30155
+ if (readGate(this.framesRead, Date.now())) this.emitProgress("read", this.framesRead, sourceTotalFrames);
30156
+ } catch (error482) {
30157
+ done = true;
30158
+ await this.destroy();
30159
+ controller.error(error482);
30160
+ }
30161
+ },
30162
+ cancel: async () => {
30163
+ done = true;
30164
+ await this.destroy();
30165
+ }
30166
+ },
30167
+ { highWaterMark }
30168
+ );
30169
+ }
30170
+ };
30171
+ var SourceNode = class extends BufferedAudioNode {
30172
+ to(child) {
30173
+ const head = "head" in child ? child.head : child;
30174
+ this.properties = { ...this.properties, children: [...this.properties.children ?? [], head] };
30175
+ }
30176
+ createRenderJob(options) {
30177
+ return new RenderJob(this, options);
30178
+ }
30179
+ };
30115
30180
  var TransformNode = class extends BufferedAudioNode {
30116
30181
  to(child) {
30117
30182
  const head = "head" in child ? child.head : child;
30118
30183
  this.properties = { ...this.properties, children: [...this.properties.children ?? [], head] };
30119
30184
  }
30120
30185
  };
30186
+ new Map(
30187
+ Object.entries({
30188
+ \u00C6: "Ae",
30189
+ \u00D0: "D",
30190
+ \u00D8: "O",
30191
+ \u00DE: "Th",
30192
+ \u00DF: "ss",
30193
+ \u00E6: "ae",
30194
+ \u00F0: "d",
30195
+ \u00F8: "o",
30196
+ \u00FE: "th",
30197
+ \u0110: "D",
30198
+ \u0111: "d",
30199
+ \u0126: "H",
30200
+ \u0127: "h",
30201
+ \u0131: "i",
30202
+ \u0132: "IJ",
30203
+ \u0133: "ij",
30204
+ \u0138: "k",
30205
+ \u013F: "L",
30206
+ \u0140: "l",
30207
+ \u0141: "L",
30208
+ \u0142: "l",
30209
+ \u0149: "'n",
30210
+ \u014A: "N",
30211
+ \u014B: "n",
30212
+ \u0152: "Oe",
30213
+ \u0153: "oe",
30214
+ \u0166: "T",
30215
+ \u0167: "t",
30216
+ \u017F: "s"
30217
+ })
30218
+ );
30121
30219
  var graphNodeSchema = external_exports2.object({
30122
30220
  id: external_exports2.string().min(1),
30123
30221
  packageName: external_exports2.string().min(1),
@@ -30457,13 +30555,13 @@ var ReadWavStream = class extends BufferedSourceStream {
30457
30555
  const fileChannels = format.channels;
30458
30556
  const selectedChannels = this.properties.channels;
30459
30557
  const allChannels = [];
30460
- for (let ch = 0; ch < fileChannels; ch++) {
30558
+ for (let fileChannel = 0; fileChannel < fileChannels; fileChannel++) {
30461
30559
  allChannels.push(new Float32Array(frames));
30462
30560
  }
30463
30561
  for (let frame = 0; frame < frames; frame++) {
30464
- for (let ch = 0; ch < fileChannels; ch++) {
30465
- const byteOffset = frame * format.blockAlign + ch * (format.bitsPerSample / 8);
30466
- const channel = allChannels[ch];
30562
+ for (let fileChannel = 0; fileChannel < fileChannels; fileChannel++) {
30563
+ const byteOffset = frame * format.blockAlign + fileChannel * (format.bitsPerSample / 8);
30564
+ const channel = allChannels[fileChannel];
30467
30565
  if (channel) {
30468
30566
  channel[frame] = readSample(chunk, byteOffset, format.bitsPerSample, format.audioFormat);
30469
30567
  }
@@ -30824,7 +30922,7 @@ var SpectrogramStream = class extends BufferedTargetStream {
30824
30922
  this.outputBins = numBands;
30825
30923
  }
30826
30924
  this.sampleBuffers = [];
30827
- for (let ch = 0; ch < this.channels; ch++) {
30925
+ for (let channel = 0; channel < this.channels; channel++) {
30828
30926
  this.sampleBuffers.push(new Float32Array(this.sampleBufferCapacity));
30829
30927
  }
30830
30928
  if (!this.fileHandle) return;
@@ -30845,17 +30943,17 @@ var SpectrogramStream = class extends BufferedTargetStream {
30845
30943
  const frames = chunk.samples[0]?.length ?? 0;
30846
30944
  if (this.sampleBufferOffset + frames > this.sampleBufferCapacity) {
30847
30945
  const newCapacity = Math.max(this.sampleBufferCapacity * 2, this.sampleBufferOffset + frames);
30848
- for (let ch = 0; ch < this.channels; ch++) {
30946
+ for (let channel = 0; channel < this.channels; channel++) {
30849
30947
  const newBuf = new Float32Array(newCapacity);
30850
- newBuf.set(this.sampleBuffers[ch].subarray(0, this.sampleBufferOffset));
30851
- this.sampleBuffers[ch] = newBuf;
30948
+ newBuf.set(this.sampleBuffers[channel].subarray(0, this.sampleBufferOffset));
30949
+ this.sampleBuffers[channel] = newBuf;
30852
30950
  }
30853
30951
  this.sampleBufferCapacity = newCapacity;
30854
30952
  }
30855
- for (let ch = 0; ch < this.channels; ch++) {
30856
- const src = chunk.samples[ch];
30857
- if (!src) continue;
30858
- this.sampleBuffers[ch].set(src, this.sampleBufferOffset);
30953
+ for (let channel = 0; channel < this.channels; channel++) {
30954
+ const channelSamples = chunk.samples[channel];
30955
+ if (!channelSamples) continue;
30956
+ this.sampleBuffers[channel].set(channelSamples, this.sampleBufferOffset);
30859
30957
  }
30860
30958
  this.sampleBufferOffset += frames;
30861
30959
  await this.processAccumulatedSamples(false);
@@ -30879,9 +30977,9 @@ var SpectrogramStream = class extends BufferedTargetStream {
30879
30977
  const halfSize = this.linearBins;
30880
30978
  const magScale = 2 / fftSize;
30881
30979
  if (flush && this.sampleBufferOffset > 0 && this.sampleBufferOffset < fftSize) {
30882
- for (let ch = 0; ch < this.channels; ch++) {
30883
- const buf = this.sampleBuffers[ch];
30884
- buf.fill(0, this.sampleBufferOffset, fftSize);
30980
+ for (let channel = 0; channel < this.channels; channel++) {
30981
+ const buffer = this.sampleBuffers[channel];
30982
+ buffer.fill(0, this.sampleBufferOffset, fftSize);
30885
30983
  }
30886
30984
  this.sampleBufferOffset = fftSize;
30887
30985
  }
@@ -30895,9 +30993,9 @@ var SpectrogramStream = class extends BufferedTargetStream {
30895
30993
  this.writeBufferOffset = 0;
30896
30994
  this.writeBufferFileOffset = this.fileOffset;
30897
30995
  }
30898
- for (let ch = 0; ch < this.channels; ch++) {
30996
+ for (let channel = 0; channel < this.channels; channel++) {
30899
30997
  const frames = computeSpectrogramFrames(
30900
- this.sampleBuffers[ch],
30998
+ this.sampleBuffers[channel],
30901
30999
  batchFrames,
30902
31000
  fftSize,
30903
31001
  hopSize,
@@ -30911,30 +31009,30 @@ var SpectrogramStream = class extends BufferedTargetStream {
30911
31009
  this.magnitudes
30912
31010
  );
30913
31011
  for (const frame of frames) {
30914
- await this.writeFrame(ch, frame);
31012
+ await this.writeFrame(channel, frame);
30915
31013
  }
30916
31014
  }
30917
31015
  await this.flushWriteBuffer();
30918
31016
  const keepFrom = batchFrames * hopSize;
30919
31017
  const keepCount = this.sampleBufferOffset - keepFrom;
30920
31018
  if (keepCount > 0) {
30921
- for (let ch = 0; ch < this.channels; ch++) {
30922
- const buf = this.sampleBuffers[ch];
30923
- buf.copyWithin(0, keepFrom, keepFrom + keepCount);
31019
+ for (let channel = 0; channel < this.channels; channel++) {
31020
+ const buffer = this.sampleBuffers[channel];
31021
+ buffer.copyWithin(0, keepFrom, keepFrom + keepCount);
30924
31022
  }
30925
31023
  }
30926
31024
  this.sampleBufferOffset = keepCount > 0 ? keepCount : 0;
30927
31025
  }
30928
- async writeFrame(ch, frame) {
31026
+ async writeFrame(channel, frame) {
30929
31027
  const frameByteSize = this.outputBins * this.channels * 4;
30930
31028
  if (this.writeBuffer && this.writeBufferOffset + frameByteSize > this.writeBuffer.length) {
30931
31029
  await this.flushWriteBuffer();
30932
31030
  }
30933
- const buf = this.writeBuffer;
30934
- if (!buf) return;
31031
+ const writeBuffer = this.writeBuffer;
31032
+ if (!writeBuffer) return;
30935
31033
  const offset = this.writeBufferOffset;
30936
31034
  for (let bin = 0; bin < this.outputBins; bin++) {
30937
- buf.writeFloatLE(frame[bin], offset + (ch * this.outputBins + bin) * 4);
31035
+ writeBuffer.writeFloatLE(frame[bin], offset + (channel * this.outputBins + bin) * 4);
30938
31036
  }
30939
31037
  this.writeBufferOffset += frameByteSize;
30940
31038
  this.fileOffset += frameByteSize;
@@ -30960,18 +31058,18 @@ function spectrogram(outputPath, options) {
30960
31058
 
30961
31059
  // src/targets/waveform/utils/minmax.ts
30962
31060
  function updateMinMax(samples, frame, channels, min, max) {
30963
- for (let ch = 0; ch < channels; ch++) {
30964
- const sample = samples[ch]?.[frame] ?? 0;
30965
- const currentMin = min[ch];
30966
- const currentMax = max[ch];
30967
- if (currentMin !== void 0 && sample < currentMin) min[ch] = sample;
30968
- if (currentMax !== void 0 && sample > currentMax) max[ch] = sample;
31061
+ for (let channel = 0; channel < channels; channel++) {
31062
+ const sample = samples[channel]?.[frame] ?? 0;
31063
+ const currentMin = min[channel];
31064
+ const currentMax = max[channel];
31065
+ if (currentMin !== void 0 && sample < currentMin) min[channel] = sample;
31066
+ if (currentMax !== void 0 && sample > currentMax) max[channel] = sample;
30969
31067
  }
30970
31068
  }
30971
31069
  function writeMinMaxPoint(min, max, channels, target, offset) {
30972
- for (let ch = 0; ch < channels; ch++) {
30973
- target.writeFloatLE(min[ch] ?? 0, offset + ch * 8);
30974
- target.writeFloatLE(max[ch] ?? 0, offset + ch * 8 + 4);
31070
+ for (let channel = 0; channel < channels; channel++) {
31071
+ target.writeFloatLE(min[channel] ?? 0, offset + channel * 8);
31072
+ target.writeFloatLE(max[channel] ?? 0, offset + channel * 8 + 4);
30975
31073
  }
30976
31074
  }
30977
31075
 
@@ -31218,7 +31316,7 @@ function bitDepthToPcmFormat(bitDepth) {
31218
31316
  }
31219
31317
  var encodingSchema = external_exports.object({
31220
31318
  format: external_exports.enum(["wav", "flac", "mp3", "aac"]),
31221
- bitrate: external_exports.string().optional(),
31319
+ bitrate: external_exports.number().int().min(8).max(1024).optional().describe("Constant bitrate in kbps for MP3 and AAC. Defaults to 192 kbps."),
31222
31320
  vbr: external_exports.number().min(0).max(9).optional(),
31223
31321
  sampleRate: external_exports.number().int().positive().optional().describe("Output sample rate (Hz). When set, ffmpeg resamples on encode.")
31224
31322
  });
@@ -31336,8 +31434,8 @@ var WriteStream = class extends BufferedTargetStream {
31336
31434
  const buffer = Buffer.alloc(frames * channels * bytesPerSample);
31337
31435
  let offset = 0;
31338
31436
  for (let frame = 0; frame < frames; frame++) {
31339
- for (let ch = 0; ch < channels; ch++) {
31340
- const sample = chunk.samples[ch]?.[frame] ?? 0;
31437
+ for (let channel = 0; channel < channels; channel++) {
31438
+ const sample = chunk.samples[channel]?.[frame] ?? 0;
31341
31439
  offset = writeSample(buffer, offset, sample, this.properties.bitDepth);
31342
31440
  }
31343
31441
  }
@@ -31355,11 +31453,11 @@ var WriteStream = class extends BufferedTargetStream {
31355
31453
  if (encoding.vbr !== void 0) {
31356
31454
  args.push("-q:a", String(encoding.vbr));
31357
31455
  } else {
31358
- args.push("-b:a", encoding.bitrate ?? "192k");
31456
+ args.push("-b:a", `${encoding.bitrate ?? 192}k`);
31359
31457
  }
31360
31458
  break;
31361
31459
  case "aac":
31362
- args.push("-codec:a", "aac", "-b:a", encoding.bitrate ?? "192k");
31460
+ args.push("-codec:a", "aac", "-b:a", `${encoding.bitrate ?? 192}k`);
31363
31461
  break;
31364
31462
  }
31365
31463
  if (encoding.sampleRate !== void 0) {
@@ -31440,8 +31538,8 @@ var CutStream = class extends UnbufferedTransformStream {
31440
31538
  }
31441
31539
  const channels = chunk.samples.length;
31442
31540
  const output = [];
31443
- for (let ch = 0; ch < channels; ch++) {
31444
- const channel = chunk.samples[ch];
31541
+ for (let channelIndex = 0; channelIndex < channels; channelIndex++) {
31542
+ const channel = chunk.samples[channelIndex];
31445
31543
  if (!channel) {
31446
31544
  output.push(new Float32Array(totalKept));
31447
31545
  continue;
@@ -31493,18 +31591,18 @@ var DitherStream = class extends UnbufferedTransformStream {
31493
31591
  while (this.lastError.length < chunk.samples.length) {
31494
31592
  this.lastError.push(0);
31495
31593
  }
31496
- const samples = chunk.samples.map((channel, ch) => {
31594
+ const samples = chunk.samples.map((channel, channelIndex) => {
31497
31595
  const output = new Float32Array(channel.length);
31498
31596
  for (let index = 0; index < channel.length; index++) {
31499
31597
  const sample = channel[index] ?? 0;
31500
31598
  const tpdfNoise = (Math.random() - Math.random()) * lsb;
31501
31599
  let dithered = sample + tpdfNoise;
31502
31600
  if (noiseShaping) {
31503
- dithered += this.lastError[ch] ?? 0;
31601
+ dithered += this.lastError[channelIndex] ?? 0;
31504
31602
  }
31505
31603
  const quantized = quantizeSample(dithered, levels);
31506
31604
  if (noiseShaping) {
31507
- this.lastError[ch] = dithered - quantized;
31605
+ this.lastError[channelIndex] = dithered - quantized;
31508
31606
  }
31509
31607
  output[index] = quantized;
31510
31608
  }
@@ -31712,9 +31810,9 @@ var PhaseStream = class extends UnbufferedTransformStream {
31712
31810
  while (this.allpassState.length < chunk.samples.length) {
31713
31811
  this.allpassState.push(0);
31714
31812
  }
31715
- const samples = chunk.samples.map((channel, ch) => {
31716
- const { output, state } = applyAllpass(channel, coefficient, this.allpassState[ch] ?? 0);
31717
- this.allpassState[ch] = state;
31813
+ const samples = chunk.samples.map((channel, channelIndex) => {
31814
+ const { output, state } = applyAllpass(channel, coefficient, this.allpassState[channelIndex] ?? 0);
31815
+ this.allpassState[channelIndex] = state;
31718
31816
  return output;
31719
31817
  });
31720
31818
  return { samples, offset: chunk.offset, sampleRate: chunk.sampleRate, bitDepth: chunk.bitDepth };
@@ -31829,9 +31927,9 @@ var SpliceStream = class extends UnbufferedTransformStream {
31829
31927
  const { samples, sampleRate } = await readWavSamples(this.properties.insertPath);
31830
31928
  const targetChannels = this.properties.channels;
31831
31929
  if (targetChannels) {
31832
- for (const ch of targetChannels) {
31833
- if (ch < 0) {
31834
- throw new Error(`Splice: target channel ${ch} is out of range`);
31930
+ for (const targetChannel of targetChannels) {
31931
+ if (targetChannel < 0) {
31932
+ throw new Error(`Splice: target channel ${targetChannel} is out of range`);
31835
31933
  }
31836
31934
  }
31837
31935
  }
@@ -31864,9 +31962,9 @@ var SpliceStream = class extends UnbufferedTransformStream {
31864
31962
  applyInsert(channelSamples, insertChannel, overlap);
31865
31963
  }
31866
31964
  } else {
31867
- for (let ch = 0; ch < samples.length; ch++) {
31868
- const channelSamples = samples[ch];
31869
- const insertChannel = this.insertSamples[ch];
31965
+ for (let channel = 0; channel < samples.length; channel++) {
31966
+ const channelSamples = samples[channel];
31967
+ const insertChannel = this.insertSamples[channel];
31870
31968
  if (!channelSamples || !insertChannel) continue;
31871
31969
  applyInsert(channelSamples, insertChannel, overlap);
31872
31970
  }
@@ -31991,8 +32089,8 @@ function downmixToMono(samples) {
31991
32089
  const mono = new Float32Array(frames);
31992
32090
  if (channels === 0) return mono;
31993
32091
  const scale = 1 / channels;
31994
- for (let ch = 0; ch < channels; ch++) {
31995
- const channel = samples[ch] ?? new Float32Array(0);
32092
+ for (let channelIndex = 0; channelIndex < channels; channelIndex++) {
32093
+ const channel = samples[channelIndex] ?? new Float32Array(0);
31996
32094
  for (let index = 0; index < frames; index++) {
31997
32095
  mono[index] = (mono[index] ?? 0) + (channel[index] ?? 0) * scale;
31998
32096
  }
@@ -32036,7 +32134,7 @@ var DuplicateChannelsStream = class extends UnbufferedTransformStream {
32036
32134
  const source = chunk.samples[0] ?? new Float32Array(0);
32037
32135
  const outputCount = this.properties.channels;
32038
32136
  const samples = [];
32039
- for (let ch = 0; ch < outputCount; ch++) {
32137
+ for (let channel = 0; channel < outputCount; channel++) {
32040
32138
  samples.push(Float32Array.from(source));
32041
32139
  }
32042
32140
  yield { samples, offset: chunk.offset, sampleRate: chunk.sampleRate, bitDepth: chunk.bitDepth };
@@ -32146,15 +32244,11 @@ var STDERR_CAP_BYTES = 64 * 1024;
32146
32244
  function spawnFfmpegChild(options) {
32147
32245
  const child = spawn(options.ffmpegPath, [...options.args], { stdio: ["pipe", "pipe", "pipe"] });
32148
32246
  child.stderr.on("data", options.onStderr);
32149
- child.stdout.on("data", options.onStdout);
32150
32247
  child.stdin.on("error", options.onStdinError);
32151
32248
  const exitPromise = new Promise((resolve) => {
32152
32249
  child.once("exit", (code, signal) => resolve({ code, signal }));
32153
32250
  });
32154
- const stdoutEndPromise = new Promise((resolve) => {
32155
- child.stdout.once("end", () => resolve());
32156
- });
32157
- return { child, exitPromise, stdoutEndPromise };
32251
+ return { child, exitPromise };
32158
32252
  }
32159
32253
  function buildInputArgs(sampleRate, channels) {
32160
32254
  return ["-f", "f32le", "-ar", String(sampleRate), "-ac", String(channels), "-i", "pipe:0"];
@@ -32189,14 +32283,14 @@ function parseStdoutFrames(stash, bytes, channels, offset, sampleRate) {
32189
32283
  floatView = new Float32Array(aligned.buffer, aligned.byteOffset, totalFloats);
32190
32284
  }
32191
32285
  const samples = [];
32192
- for (let ch = 0; ch < channels; ch++) {
32286
+ for (let channel = 0; channel < channels; channel++) {
32193
32287
  samples.push(new Float32Array(frameCount));
32194
32288
  }
32195
32289
  for (let frame = 0; frame < frameCount; frame++) {
32196
- for (let ch = 0; ch < channels; ch++) {
32197
- const channelArray = samples[ch];
32290
+ for (let channel = 0; channel < channels; channel++) {
32291
+ const channelArray = samples[channel];
32198
32292
  if (channelArray) {
32199
- channelArray[frame] = floatView[frame * channels + ch] ?? 0;
32293
+ channelArray[frame] = floatView[frame * channels + channel] ?? 0;
32200
32294
  }
32201
32295
  }
32202
32296
  }
@@ -32219,15 +32313,16 @@ var TEARDOWN_KILL_GRACE_MS2 = 2e3;
32219
32313
  var FfmpegStream = class extends UnbufferedTransformStream {
32220
32314
  constructor() {
32221
32315
  super(...arguments);
32222
- this.pending = [];
32223
32316
  this.stdoutStash = Buffer.alloc(0);
32224
32317
  this.outputOffset = 0;
32225
32318
  this.stderr = "";
32319
+ this.stdoutEnded = false;
32226
32320
  this.inputSampleRate = 0;
32227
32321
  this.inputChannels = 0;
32228
32322
  }
32229
32323
  _setup(context) {
32230
32324
  this.streamContext = context;
32325
+ if (this.properties.outputSampleRate !== void 0) context.sampleRate = this.properties.outputSampleRate;
32231
32326
  }
32232
32327
  _buildArgs(context) {
32233
32328
  const { args } = this.properties;
@@ -32240,29 +32335,72 @@ var FfmpegStream = class extends UnbufferedTransformStream {
32240
32335
  this.inputChannels = channels;
32241
32336
  const outRate = this.properties.outputSampleRate ?? sampleRate;
32242
32337
  const args = [...buildInputArgs(sampleRate, channels), ...this._buildArgs(this.streamContext), ...buildOutputArgs(outRate, channels)];
32243
- const { child, exitPromise, stdoutEndPromise } = spawnFfmpegChild({
32338
+ const { child, exitPromise } = spawnFfmpegChild({
32244
32339
  ffmpegPath: this.properties.ffmpegPath,
32245
32340
  args,
32246
32341
  onStderr: (chunk) => {
32247
32342
  this.stderr = appendStderr(this.stderr, chunk);
32248
32343
  },
32249
- onStdout: (bytes) => this.handleStdoutBytes(bytes),
32250
32344
  onStdinError: (error50) => {
32251
32345
  if (error50.code === "EPIPE") return;
32252
32346
  this.stdinError ?? (this.stdinError = new Error(`ffmpeg stdin error: ${error50.message}`));
32253
32347
  }
32254
32348
  });
32349
+ child.stdout.on("readable", () => this.wakeStdout());
32350
+ child.stdout.on("end", () => {
32351
+ this.stdoutEnded = true;
32352
+ this.wakeStdout();
32353
+ });
32255
32354
  this.child = child;
32256
32355
  this.exitPromise = exitPromise;
32257
- this.stdoutEndPromise = stdoutEndPromise;
32258
32356
  }
32259
- handleStdoutBytes(bytes) {
32357
+ wakeStdout() {
32358
+ const notify = this.stdoutNotify;
32359
+ this.stdoutNotify = void 0;
32360
+ this.stdoutWait = void 0;
32361
+ notify?.();
32362
+ }
32363
+ *readAvailableStdout() {
32364
+ const stdout = this.child?.stdout;
32365
+ if (!stdout) return;
32260
32366
  const outRate = this.properties.outputSampleRate ?? this.inputSampleRate;
32261
- const { block, stash, frameCount } = parseStdoutFrames(this.stdoutStash, bytes, this.inputChannels, this.outputOffset, outRate);
32262
- this.stdoutStash = stash;
32263
- if (!block) return;
32264
- this.pending.push(block);
32265
- this.outputOffset += frameCount;
32367
+ for (; ; ) {
32368
+ const bytes = stdout.read();
32369
+ if (!bytes) return;
32370
+ const { block, stash, frameCount } = parseStdoutFrames(this.stdoutStash, bytes, this.inputChannels, this.outputOffset, outRate);
32371
+ this.stdoutStash = stash;
32372
+ if (block) {
32373
+ this.outputOffset += frameCount;
32374
+ yield block;
32375
+ }
32376
+ }
32377
+ }
32378
+ waitForStdoutReadableOrEnd() {
32379
+ const stdout = this.child?.stdout;
32380
+ if (!stdout || this.stdoutEnded) return Promise.resolve();
32381
+ if (stdout.readableLength > 0) return Promise.resolve();
32382
+ if (stdout.readableEnded) {
32383
+ this.stdoutEnded = true;
32384
+ return Promise.resolve();
32385
+ }
32386
+ if (this.stdoutWait) return this.stdoutWait;
32387
+ const wait = new Promise((resolve) => {
32388
+ this.stdoutNotify = resolve;
32389
+ });
32390
+ this.stdoutWait = wait;
32391
+ return wait;
32392
+ }
32393
+ async *serveWhileParked() {
32394
+ for (; ; ) {
32395
+ yield* this.readAvailableStdout();
32396
+ const drain = this.pendingDrain;
32397
+ if (drain === void 0) return;
32398
+ if (this.stdoutEnded) {
32399
+ await drain;
32400
+ return;
32401
+ }
32402
+ await Promise.race([drain, this.waitForStdoutReadableOrEnd()]);
32403
+ }
32266
32404
  }
32267
32405
  async *_transform(block) {
32268
32406
  if (this.stdinError) throw this.stdinError;
@@ -32275,11 +32413,8 @@ var FfmpegStream = class extends UnbufferedTransformStream {
32275
32413
  const child = this.child;
32276
32414
  if (!child) throw new Error("FfmpegStream.child not initialized");
32277
32415
  const interleaved = interleave(block.samples, frames, channels);
32278
- const buf = Buffer.from(interleaved.buffer, interleaved.byteOffset, interleaved.byteLength);
32279
- if (this.pendingDrain) {
32280
- await this.pendingDrain;
32281
- }
32282
- const ok = child.stdin.write(buf);
32416
+ const interleavedBuffer = Buffer.from(interleaved.buffer, interleaved.byteOffset, interleaved.byteLength);
32417
+ const ok = child.stdin.write(interleavedBuffer);
32283
32418
  if (!ok) {
32284
32419
  this.pendingDrain = new Promise((resolve) => {
32285
32420
  child.stdin.once("drain", () => {
@@ -32288,27 +32423,31 @@ var FfmpegStream = class extends UnbufferedTransformStream {
32288
32423
  });
32289
32424
  });
32290
32425
  }
32291
- yield* this.pending.splice(0);
32426
+ yield* this.serveWhileParked();
32427
+ yield* this.readAvailableStdout();
32292
32428
  }
32293
32429
  async *_flush() {
32294
32430
  const child = this.child;
32295
32431
  if (!child) return;
32296
- if (this.pendingDrain) {
32297
- await this.pendingDrain;
32298
- }
32432
+ yield* this.serveWhileParked();
32299
32433
  child.stdin.end();
32300
32434
  if (this.stdinError) throw this.stdinError;
32301
- const stdoutEnd = this.stdoutEndPromise ?? Promise.resolve();
32302
- const exit = this.exitPromise ?? Promise.resolve({ code: 0, signal: null });
32303
- const [, exitResult] = await Promise.all([stdoutEnd, exit]);
32435
+ for (; ; ) {
32436
+ yield* this.readAvailableStdout();
32437
+ if (this.stdoutEnded) break;
32438
+ await this.waitForStdoutReadableOrEnd();
32439
+ }
32440
+ if (this.stdoutStash.length >= this.inputChannels * 4) {
32441
+ const outRate = this.properties.outputSampleRate ?? this.inputSampleRate;
32442
+ const { block } = parseStdoutFrames(this.stdoutStash, Buffer.alloc(0), this.inputChannels, this.outputOffset, outRate);
32443
+ this.stdoutStash = Buffer.alloc(0);
32444
+ if (block) yield block;
32445
+ }
32446
+ const exitResult = await (this.exitPromise ?? Promise.resolve({ code: 0, signal: null }));
32304
32447
  if (exitResult.code !== null && exitResult.code !== 0) {
32305
32448
  const detail = this.stderr ? `: ${this.stderr.slice(0, 1024)}` : "";
32306
32449
  throw new Error(`ffmpeg exited ${exitResult.code}${detail}`);
32307
32450
  }
32308
- if (this.stdoutStash.length >= this.inputChannels * 4) {
32309
- this.handleStdoutBytes(Buffer.alloc(0));
32310
- }
32311
- yield* this.pending.splice(0);
32312
32451
  }
32313
32452
  async _destroy() {
32314
32453
  const child = this.child;
@@ -32404,8 +32543,9 @@ function windowSamplesFromMs(smoothingMs, sampleRate) {
32404
32543
  return Math.max(1, Math.round(smoothingMs * sampleRate / 1e3));
32405
32544
  }
32406
32545
  async function applyBackwardPassOverChunkBuffer(args) {
32407
- const { sourceBuffer, destBuffer, iir, chunkSize, minHeldBuffer } = args;
32546
+ const { sourceBuffer, destBuffer, iir, chunkSize, minHeldBuffer, progress } = args;
32408
32547
  const totalFrames = sourceBuffer.frames;
32548
+ const totalWork = totalFrames * 2;
32409
32549
  if (totalFrames === 0) return;
32410
32550
  if (chunkSize <= 0) {
32411
32551
  throw new Error(`applyBackwardPassOverChunkBuffer: chunkSize must be > 0 (got ${chunkSize})`);
@@ -32421,6 +32561,7 @@ async function applyBackwardPassOverChunkBuffer(args) {
32421
32561
  try {
32422
32562
  const backwardState = { value: 0 };
32423
32563
  let seeded = false;
32564
+ let filteredFrames = 0;
32424
32565
  const sourceReader = await sourceBuffer.openReverseReader();
32425
32566
  try {
32426
32567
  for (; ; ) {
@@ -32433,12 +32574,15 @@ async function applyBackwardPassOverChunkBuffer(args) {
32433
32574
  }
32434
32575
  const filtered = iir.applyForwardPass(reversed, backwardState);
32435
32576
  await filteredReversed.write([filtered], sr, bd);
32577
+ filteredFrames += reversed.length;
32578
+ progress?.(filteredFrames, totalWork);
32436
32579
  }
32437
32580
  } finally {
32438
32581
  await sourceReader.close();
32439
32582
  }
32440
32583
  if (minHeldBuffer !== void 0) await minHeldBuffer.reset();
32441
32584
  const filteredReader = await filteredReversed.openReverseReader();
32585
+ let restoredFrames = 0;
32442
32586
  try {
32443
32587
  for (; ; ) {
32444
32588
  const chunk = await filteredReader.read(chunkSize);
@@ -32460,6 +32604,8 @@ async function applyBackwardPassOverChunkBuffer(args) {
32460
32604
  }
32461
32605
  }
32462
32606
  await destBuffer.write([forwardOrder], sr, bd);
32607
+ restoredFrames += stripeFrames;
32608
+ progress?.(totalFrames + restoredFrames, totalWork);
32463
32609
  }
32464
32610
  } finally {
32465
32611
  await filteredReader.close();
@@ -32618,7 +32764,8 @@ async function iterateForTargets(args) {
32618
32764
  tolerance = DEFAULT_TOLERANCE,
32619
32765
  peakTolerance,
32620
32766
  seedB,
32621
- onAttempt
32767
+ onAttempt,
32768
+ progress
32622
32769
  } = args;
32623
32770
  const channelCount = buffer.channels;
32624
32771
  const frames = buffer.frames;
@@ -32680,7 +32827,10 @@ async function iterateForTargets(args) {
32680
32827
  let winnerLufsErr = Infinity;
32681
32828
  let winnerPeakErr = Infinity;
32682
32829
  let previousStepMagnitude = Infinity;
32830
+ const attemptWork = frames * 4;
32831
+ const totalWork = maxAttempts * attemptWork;
32683
32832
  for (let attemptIdx = 0; attemptIdx < maxAttempts; attemptIdx++) {
32833
+ const attemptBase = attemptIdx * attemptWork;
32684
32834
  const tAttempt0 = Date.now();
32685
32835
  const anchors = {
32686
32836
  floorDb: anchorBase.floorDb,
@@ -32695,16 +32845,24 @@ async function iterateForTargets(args) {
32695
32845
  iir,
32696
32846
  halfWidth,
32697
32847
  forwardEnvelopeBuffer,
32698
- minHeldEnvelopeBuffer
32848
+ minHeldEnvelopeBuffer,
32849
+ progress: (done) => progress?.(attemptBase + done, totalWork)
32699
32850
  });
32700
32851
  await applyBackwardPassOverChunkBuffer({
32701
32852
  sourceBuffer: forwardEnvelopeBuffer,
32702
32853
  destBuffer: activeRef,
32703
32854
  iir,
32704
32855
  chunkSize: CHUNK_FRAMES3,
32705
- minHeldBuffer: minHeldEnvelopeBuffer
32856
+ minHeldBuffer: minHeldEnvelopeBuffer,
32857
+ progress: (done) => progress?.(attemptBase + frames + done, totalWork)
32858
+ });
32859
+ const measured = await measureAttemptOutput({
32860
+ source: buffer,
32861
+ sampleRate,
32862
+ channelCount,
32863
+ gSmoothed: activeRef,
32864
+ progress: (done) => progress?.(attemptBase + frames * 3 + done, totalWork)
32706
32865
  });
32707
- const measured = await measureAttemptOutput({ source: buffer, sampleRate, channelCount, gSmoothed: activeRef });
32708
32866
  const lufsErr = measured.outputLufs - targetLufs;
32709
32867
  const peakErr = measured.outputTruePeakDb - effectiveTargetTp;
32710
32868
  const attempt = {
@@ -32785,7 +32943,7 @@ async function iterateForTargets(args) {
32785
32943
  }
32786
32944
  }
32787
32945
  async function streamCurveAndForwardIir(args) {
32788
- const { detectionEnvelope, anchors, iir, halfWidth, forwardEnvelopeBuffer, minHeldEnvelopeBuffer } = args;
32946
+ const { detectionEnvelope, anchors, iir, halfWidth, forwardEnvelopeBuffer, minHeldEnvelopeBuffer, progress } = args;
32789
32947
  const totalFrames = detectionEnvelope.frames;
32790
32948
  if (totalFrames === 0) return;
32791
32949
  await detectionEnvelope.reset();
@@ -32820,13 +32978,14 @@ async function streamCurveAndForwardIir(args) {
32820
32978
  await forwardEnvelopeBuffer.write([forwardChunk], detectionSampleRate, detectionBitDepth);
32821
32979
  await minHeldEnvelopeBuffer.write([minHeldChunk], detectionSampleRate, detectionBitDepth);
32822
32980
  }
32981
+ progress?.(consumedFrames, totalFrames);
32823
32982
  if (chunkLength < CHUNK_FRAMES3) break;
32824
32983
  }
32825
32984
  await forwardEnvelopeBuffer.flushWrites();
32826
32985
  await minHeldEnvelopeBuffer.flushWrites();
32827
32986
  }
32828
32987
  async function measureAttemptOutput(args) {
32829
- const { source, sampleRate, channelCount, gSmoothed } = args;
32988
+ const { source, sampleRate, channelCount, gSmoothed, progress } = args;
32830
32989
  const accumulator = new LoudnessAccumulator(sampleRate, channelCount);
32831
32990
  const truePeakAccumulator = new TruePeakAccumulator(sampleRate, channelCount);
32832
32991
  const applyOutputScratch = [];
@@ -32835,6 +32994,8 @@ async function measureAttemptOutput(args) {
32835
32994
  }
32836
32995
  await source.reset();
32837
32996
  await gSmoothed.reset();
32997
+ const totalFrames = source.frames;
32998
+ let framesDone = 0;
32838
32999
  for (; ; ) {
32839
33000
  const sourceChunk = await source.read(CHUNK_FRAMES3);
32840
33001
  const chunkFrames = sourceChunk.samples[0]?.length ?? 0;
@@ -32856,6 +33017,8 @@ async function measureAttemptOutput(args) {
32856
33017
  });
32857
33018
  accumulator.push(transformed, chunkFrames);
32858
33019
  truePeakAccumulator.push(transformed, chunkFrames);
33020
+ framesDone += chunkFrames;
33021
+ progress?.(framesDone, totalFrames);
32859
33022
  if (chunkFrames < CHUNK_FRAMES3) break;
32860
33023
  }
32861
33024
  const result = accumulator.finalize();
@@ -33311,7 +33474,8 @@ var LoudnessTargetStream = class extends BufferedTransformStream {
33311
33474
  tolerance
33312
33475
  });
33313
33476
  const tIterate0 = Date.now();
33314
- const attemptGate = createProgressGate(maxAttempts);
33477
+ const totalWork = maxAttempts * buffer.frames * 4;
33478
+ const progressGate = createProgressGate(totalWork);
33315
33479
  const result = await iterateForTargets({
33316
33480
  buffer,
33317
33481
  sampleRate,
@@ -33328,6 +33492,9 @@ var LoudnessTargetStream = class extends BufferedTransformStream {
33328
33492
  peakTolerance,
33329
33493
  seedB,
33330
33494
  detectionEnvelope,
33495
+ progress: (done, total) => {
33496
+ if (progressGate(done, Date.now())) this.emitProgress("process", done, total);
33497
+ },
33331
33498
  onAttempt: (attempt, attemptIndex) => {
33332
33499
  this.log("attempt", {
33333
33500
  attempt: attemptIndex + 1,
@@ -33338,7 +33505,6 @@ var LoudnessTargetStream = class extends BufferedTransformStream {
33338
33505
  outputLra: attempt.outputLra,
33339
33506
  elapsedMs: attempt.elapsedMs
33340
33507
  });
33341
- if (attemptGate(attemptIndex + 1, Date.now())) this.emitProgress("process", attemptIndex + 1, maxAttempts);
33342
33508
  }
33343
33509
  });
33344
33510
  this.learnTimingMs.iteration = Date.now() - tIterate0;
@@ -34083,10 +34249,10 @@ async function streamLatticeTrajectory(buffer, frameSize, hopSize, backend, addo
34083
34249
  for (let index = 0; index < got; index++) {
34084
34250
  let sample = 0;
34085
34251
  const ringPos = consumed % frameSize;
34086
- for (let ch = 0; ch < channelCount; ch++) {
34087
- const value = chunk.samples[ch]?.[index] ?? 0;
34252
+ for (let channel = 0; channel < channelCount; channel++) {
34253
+ const value = chunk.samples[channel]?.[index] ?? 0;
34088
34254
  sample = Math.fround(sample + value);
34089
- const channelRing = channelRings[ch];
34255
+ const channelRing = channelRings[channel];
34090
34256
  if (channelRing) channelRing[ringPos] = value;
34091
34257
  }
34092
34258
  sumRing[ringPos] = sample;
@@ -34094,9 +34260,9 @@ async function streamLatticeTrajectory(buffer, frameSize, hopSize, backend, addo
34094
34260
  while (nextFrame < frameCount && consumed >= nextFrame * hopSize + frameSize) {
34095
34261
  const start = nextFrame * hopSize;
34096
34262
  for (let pos = 0; pos < frameSize; pos++) window2[pos] = sumRing[(start + pos) % frameSize] ?? 0;
34097
- for (let ch = 0; ch < channelCount; ch++) {
34098
- const channelRing = channelRings[ch];
34099
- const channelWindow = channelWindows[ch];
34263
+ for (let channel = 0; channel < channelCount; channel++) {
34264
+ const channelRing = channelRings[channel];
34265
+ const channelWindow = channelWindows[channel];
34100
34266
  if (!channelRing || !channelWindow) continue;
34101
34267
  for (let pos = 0; pos < frameSize; pos++) channelWindow[pos] = channelRing[(start + pos) % frameSize] ?? 0;
34102
34268
  }
@@ -34183,10 +34349,10 @@ var _LatticeApplyState = class _LatticeApplyState {
34183
34349
  const fraction = framePos - frame0;
34184
34350
  const row0 = rows[frame0] ?? this.trajectory.identity;
34185
34351
  const row1 = rows[frame1] ?? this.trajectory.identity;
34186
- for (let ch = 0; ch < channelCount; ch++) {
34187
- const inputValue = channels[ch]?.[index] ?? 0;
34188
- const outChannel = out[ch];
34189
- const chState = this.state[ch] ?? new Float32Array(order);
34352
+ for (let channel = 0; channel < channelCount; channel++) {
34353
+ const inputValue = channels[channel]?.[index] ?? 0;
34354
+ const outChannel = out[channel];
34355
+ const chState = this.state[channel] ?? new Float32Array(order);
34190
34356
  let signalValue = inputValue;
34191
34357
  for (let section = 0; section < order; section++) {
34192
34358
  const interpolated = (row0[section] ?? 0) + fraction * ((row1[section] ?? 0) - (row0[section] ?? 0));
@@ -34279,7 +34445,7 @@ var CrestReduceStream = class extends BufferedTransformStream {
34279
34445
  }
34280
34446
  applyState ?? (applyState = new LatticeApplyState(smoothedTrajectory, order, hopSize, blockChannelCount));
34281
34447
  const transformed = applyState.apply(block.samples, frames);
34282
- const samples = block.samples.map((inputChannel, ch) => transformed[ch] ?? inputChannel);
34448
+ const samples = block.samples.map((inputChannel, channelIndex) => transformed[channelIndex] ?? inputChannel);
34283
34449
  yield { samples, offset: block.offset, sampleRate: block.sampleRate, bitDepth: block.bitDepth };
34284
34450
  appliedFrames += frames;
34285
34451
  const doneFrames = Math.min(appliedFrames, totalFrames);
@@ -34746,13 +34912,13 @@ var WindowReader = class {
34746
34912
  this.channels = channels;
34747
34913
  this.windowSamples = windowSamples;
34748
34914
  this.scratch = [];
34749
- for (let ch = 0; ch < channels; ch++) this.scratch.push(new Float32Array(windowSamples));
34915
+ for (let channelIndex = 0; channelIndex < channels; channelIndex++) this.scratch.push(new Float32Array(windowSamples));
34750
34916
  }
34751
34917
  getScratch() {
34752
34918
  return this.scratch;
34753
34919
  }
34754
34920
  async preload(buffer, edgePadSamples) {
34755
- for (let ch = 0; ch < this.channels; ch++) this.scratch[ch].fill(0);
34921
+ for (let channelIndex = 0; channelIndex < this.channels; channelIndex++) this.scratch[channelIndex].fill(0);
34756
34922
  this.virtualCursor = 0;
34757
34923
  this.bufferDrained = false;
34758
34924
  const headPad = Math.min(edgePadSamples, this.windowSamples);
@@ -34763,8 +34929,8 @@ var WindowReader = class {
34763
34929
  async advance(buffer, step) {
34764
34930
  if (step <= 0) return;
34765
34931
  const keep = this.windowSamples - step;
34766
- for (let ch = 0; ch < this.channels; ch++) {
34767
- const view = this.scratch[ch];
34932
+ for (let channelIndex = 0; channelIndex < this.channels; channelIndex++) {
34933
+ const view = this.scratch[channelIndex];
34768
34934
  if (keep > 0) view.copyWithin(0, step, this.windowSamples);
34769
34935
  view.fill(0, keep, this.windowSamples);
34770
34936
  }
@@ -34782,10 +34948,10 @@ var WindowReader = class {
34782
34948
  this.bufferDrained = true;
34783
34949
  return;
34784
34950
  }
34785
- for (let ch = 0; ch < this.channels; ch++) {
34786
- const src = chunk.samples[ch];
34787
- const dest = this.scratch[ch];
34788
- if (src) dest.set(src.subarray(0, chunkFrames), outOffset);
34951
+ for (let channelIndex = 0; channelIndex < this.channels; channelIndex++) {
34952
+ const sourceSamples = chunk.samples[channelIndex];
34953
+ const dest = this.scratch[channelIndex];
34954
+ if (sourceSamples) dest.set(sourceSamples.subarray(0, chunkFrames), outOffset);
34789
34955
  }
34790
34956
  outOffset += chunkFrames;
34791
34957
  remaining -= chunkFrames;
@@ -34947,8 +35113,8 @@ async function readSequentialPadded(chunkBuffer, channelIndex, frames, out, hopS
34947
35113
  const chunk = await chunkBuffer.read(toRead);
34948
35114
  const chunkFrames = chunk.samples[0]?.length ?? 0;
34949
35115
  if (chunkFrames === 0) return;
34950
- const src = chunk.samples[channelIndex];
34951
- if (src) out.set(src.subarray(0, chunkFrames), headPad + written);
35116
+ const sourceSamples = chunk.samples[channelIndex];
35117
+ if (sourceSamples) out.set(sourceSamples.subarray(0, chunkFrames), headPad + written);
34952
35118
  written += chunkFrames;
34953
35119
  toRead -= chunkFrames;
34954
35120
  }
@@ -35030,7 +35196,7 @@ var DeBleedStream = class extends BufferedTransformStream {
35030
35196
  const targetPaddeds = Array.from({ length: channels }, () => new Float32Array(warmupFrames * hopSize + (fftSize - hopSize)));
35031
35197
  const refPaddeds = Array.from({ length: refCount }, () => new Float32Array(warmupFrames * hopSize + (fftSize - hopSize)));
35032
35198
  await buffer.reset();
35033
- for (let ch = 0; ch < channels; ch++) targetPaddeds[ch].fill(0);
35199
+ for (let channelIndex = 0; channelIndex < channels; channelIndex++) targetPaddeds[channelIndex].fill(0);
35034
35200
  const targetSamples = warmupFrames * hopSize + (fftSize - hopSize);
35035
35201
  let written = 0;
35036
35202
  let toRead = Math.min(targetSamples, buffer.frames);
@@ -35038,9 +35204,9 @@ var DeBleedStream = class extends BufferedTransformStream {
35038
35204
  const chunk = await buffer.read(toRead);
35039
35205
  const chunkFrames = chunk.samples[0]?.length ?? 0;
35040
35206
  if (chunkFrames === 0) break;
35041
- for (let ch = 0; ch < channels; ch++) {
35042
- const src = chunk.samples[ch];
35043
- if (src) targetPaddeds[ch].set(src.subarray(0, chunkFrames), written);
35207
+ for (let channelIndex = 0; channelIndex < channels; channelIndex++) {
35208
+ const sourceSamples = chunk.samples[channelIndex];
35209
+ if (sourceSamples) targetPaddeds[channelIndex].set(sourceSamples.subarray(0, chunkFrames), written);
35044
35210
  }
35045
35211
  written += chunkFrames;
35046
35212
  toRead -= chunkFrames;
@@ -35051,13 +35217,13 @@ var DeBleedStream = class extends BufferedTransformStream {
35051
35217
  }
35052
35218
  const targetStftOutputs = Array.from({ length: channels }, () => allocateStftOutput(warmupFrames, numBins));
35053
35219
  const refStftOutputs = Array.from({ length: refCount }, () => allocateStftOutput(warmupFrames, numBins));
35054
- const targetStfts = targetPaddeds.map((padded, ch) => stft(padded, fftSize, hopSize, targetStftOutputs[ch], this.fftBackend, this.fftAddonOptions));
35220
+ const targetStfts = targetPaddeds.map((padded, channelIndex) => stft(padded, fftSize, hopSize, targetStftOutputs[channelIndex], this.fftBackend, this.fftAddonOptions));
35055
35221
  const refStfts = refPaddeds.map((padded, refIndex) => stft(padded, fftSize, hopSize, refStftOutputs[refIndex], this.fftBackend, this.fftAddonOptions));
35056
35222
  const maxRefPows = refStfts.map((refStft) => findMaxRefPower(refStft.real, refStft.imag, refStft.frames, numBins));
35057
35223
  const weightEpsilons = maxRefPows.map((maxPow) => 1e-10 * (maxPow + 1e-20));
35058
35224
  const seedsByChannel = [];
35059
- for (let ch = 0; ch < channels; ch++) {
35060
- const targetStft = targetStfts[ch];
35225
+ for (let channelIndex = 0; channelIndex < channels; channelIndex++) {
35226
+ const targetStft = targetStfts[channelIndex];
35061
35227
  const accumulators = refStfts.map(() => createTransferAccumulator(numBins));
35062
35228
  for (let refIndex = 0; refIndex < refCount; refIndex++) {
35063
35229
  const refStft = refStfts[refIndex];
@@ -35157,8 +35323,8 @@ var DeBleedStream = class extends BufferedTransformStream {
35157
35323
  const _tstft = _profStart();
35158
35324
  const targetScratch = targetReader.getScratch();
35159
35325
  const targetStfts = [];
35160
- for (let ch = 0; ch < channels; ch++) {
35161
- const stftOut = stft(targetScratch[ch].subarray(0, winSamples), fftSize, hopSize, targetStftOutputs[ch], this.fftBackend, this.fftAddonOptions);
35326
+ for (let channelIndex = 0; channelIndex < channels; channelIndex++) {
35327
+ const stftOut = stft(targetScratch[channelIndex].subarray(0, winSamples), fftSize, hopSize, targetStftOutputs[channelIndex], this.fftBackend, this.fftAddonOptions);
35162
35328
  targetStfts.push(stftOut);
35163
35329
  }
35164
35330
  const refStftsForChunk = [];
@@ -35171,12 +35337,12 @@ var DeBleedStream = class extends BufferedTransformStream {
35171
35337
  const cleanedByChannel = [];
35172
35338
  const sHatRe = new Float32Array(numBins);
35173
35339
  const sHatIm = new Float32Array(numBins);
35174
- for (let ch = 0; ch < channels; ch++) {
35175
- const kalmanStates = kalmanStatesByCh[ch];
35176
- const interfererPsd = interfererPsdByCh[ch];
35177
- const msadChannelStates = msadChannelStatesByCh[ch];
35178
- const ispStates = ispStatesByCh[ch];
35179
- const targetStft = targetStfts[ch];
35340
+ for (let channelIndex = 0; channelIndex < channels; channelIndex++) {
35341
+ const kalmanStates = kalmanStatesByCh[channelIndex];
35342
+ const interfererPsd = interfererPsdByCh[channelIndex];
35343
+ const msadChannelStates = msadChannelStatesByCh[channelIndex];
35344
+ const ispStates = ispStatesByCh[channelIndex];
35345
+ const targetStft = targetStfts[channelIndex];
35180
35346
  for (let frame = 0; frame < winFrames; frame++) {
35181
35347
  const frameOffset = frame * numBins;
35182
35348
  const frameReal = targetStft.real.subarray(frameOffset, frameOffset + numBins);
@@ -35274,13 +35440,13 @@ var DeBleedStream = class extends BufferedTransformStream {
35274
35440
  if (!clip) continue;
35275
35441
  const { clipStart, sliceFromOffset, sliceLength } = clip;
35276
35442
  const writeSamplesByChannel = [];
35277
- for (let ch = 0; ch < channels; ch++) {
35278
- writeSamplesByChannel.push(cleanedByChannel[ch].subarray(sliceFromOffset, sliceFromOffset + sliceLength));
35443
+ for (let channelIndex = 0; channelIndex < channels; channelIndex++) {
35444
+ writeSamplesByChannel.push(cleanedByChannel[channelIndex].subarray(sliceFromOffset, sliceFromOffset + sliceLength));
35279
35445
  }
35280
35446
  if (clipStart > outputBuffer.frames) {
35281
35447
  const padFrames = clipStart - outputBuffer.frames;
35282
35448
  const zeroSamples = [];
35283
- for (let ch = 0; ch < channels; ch++) zeroSamples.push(new Float32Array(padFrames));
35449
+ for (let channelIndex = 0; channelIndex < channels; channelIndex++) zeroSamples.push(new Float32Array(padFrames));
35284
35450
  const _twritePad = _profStart();
35285
35451
  await outputBuffer.write(zeroSamples, sampleRate, bitDepth);
35286
35452
  _profAdd("write", _twritePad);
@@ -35294,7 +35460,7 @@ var DeBleedStream = class extends BufferedTransformStream {
35294
35460
  if (outputBuffer.frames < totalFrames) {
35295
35461
  const padFrames = totalFrames - outputBuffer.frames;
35296
35462
  const zeroSamples = [];
35297
- for (let ch = 0; ch < channels; ch++) zeroSamples.push(new Float32Array(padFrames));
35463
+ for (let channelIndex = 0; channelIndex < channels; channelIndex++) zeroSamples.push(new Float32Array(padFrames));
35298
35464
  await outputBuffer.write(zeroSamples, sampleRate, bitDepth);
35299
35465
  }
35300
35466
  await outputBuffer.reset();
@@ -35376,6 +35542,31 @@ function createOnnxSession(addonPath, modelPath, options, log) {
35376
35542
  };
35377
35543
  }
35378
35544
 
35545
+ // src/utils/resample-composition.ts
35546
+ function createResampleComposition(options) {
35547
+ const edgeRate = options.context.sampleRate;
35548
+ if (edgeRate === options.modelRate) return void 0;
35549
+ const upResample = new FfmpegStream(
35550
+ ffmpeg({
35551
+ ffmpegPath: options.ffmpegPath,
35552
+ args: ["-af", `aresample=${options.modelRate}`],
35553
+ outputSampleRate: options.modelRate
35554
+ }),
35555
+ options.streamContext
35556
+ );
35557
+ const downResample = new FfmpegStream(
35558
+ ffmpeg({
35559
+ ffmpegPath: options.ffmpegPath,
35560
+ args: ["-af", `aresample=${edgeRate}`],
35561
+ outputSampleRate: edgeRate
35562
+ }),
35563
+ options.streamContext
35564
+ );
35565
+ upResample._setup(options.context);
35566
+ downResample._setup(options.context);
35567
+ return { upResample, downResample };
35568
+ }
35569
+
35379
35570
  // src/transforms/deep-filter-net-3/utils/dfn.ts
35380
35571
  var DFN3_SAMPLE_RATE = 48e3;
35381
35572
  var DFN3_HOP_SIZE = 480;
@@ -35428,9 +35619,8 @@ function processDfnBlock(dfnState, signal, session, attenLimDb) {
35428
35619
  var DFN3_BUFFER_SIZE = 100 * DFN3_HOP_SIZE;
35429
35620
  var schema23 = external_exports.object({
35430
35621
  modelPath: external_exports.string().default("").meta({ input: "file", mode: "open", accept: ".onnx", binary: "dfn3", download: "https://github.com/yuyun2000/SpeechDenoiser" }).describe("DeepFilterNet3 48 kHz denoiser model (.onnx)"),
35431
- ffmpegPath: external_exports.string().default("").meta({ input: "file", mode: "open", binary: "ffmpeg", download: "https://ffmpeg.org/download.html" }).describe("FFmpeg \u2014 only used when sampleRate \u2260 48000 to chain up/down resamplers around the inference stream; can be left blank when sampleRate === 48000."),
35622
+ ffmpegPath: external_exports.string().default("").meta({ input: "file", mode: "open", binary: "ffmpeg", download: "https://ffmpeg.org/download.html" }).describe("FFmpeg \u2014 used when the input audio is not 48 kHz to chain up/down resamplers around the inference stream; can be left blank for 48 kHz input."),
35432
35623
  onnxAddonPath: external_exports.string().default("").meta({ input: "file", mode: "open", binary: "onnx-addon", download: "https://github.com/visionsofparadise/onnx-runtime-addon" }).describe("ONNX Runtime native addon"),
35433
- sampleRate: external_exports.number().int().positive().describe("Source audio sample rate in Hz. Required. When \u2260 48000, ffmpeg resampling is chained around the inference stream via _setup composition."),
35434
35624
  attenuation: external_exports.number().min(0).max(100).default(30).describe("Attenuation cap in dB. Maps to the ONNX `atten_lim_db` input; 0 = no cap")
35435
35625
  });
35436
35626
  var DeepFilterNet3Stream = class extends BufferedTransformStream {
@@ -35442,26 +35632,11 @@ var DeepFilterNet3Stream = class extends BufferedTransformStream {
35442
35632
  }
35443
35633
  _setup(context) {
35444
35634
  this.session = createOnnxSession(this.properties.onnxAddonPath, this.properties.modelPath, { executionProviders: ["cpu"] }, (message, data) => this.log(message, data));
35445
- const sourceRate = this.properties.sampleRate;
35446
- if (sourceRate === DFN3_SAMPLE_RATE) return;
35447
- this.upResample = new FfmpegStream(
35448
- ffmpeg({
35449
- ffmpegPath: this.properties.ffmpegPath,
35450
- args: ["-af", `aresample=${DFN3_SAMPLE_RATE}`],
35451
- outputSampleRate: DFN3_SAMPLE_RATE
35452
- }),
35453
- this.renderContext
35454
- );
35455
- this.downResample = new FfmpegStream(
35456
- ffmpeg({
35457
- ffmpegPath: this.properties.ffmpegPath,
35458
- args: ["-af", `aresample=${sourceRate}`],
35459
- outputSampleRate: sourceRate
35460
- }),
35461
- this.renderContext
35462
- );
35463
- this.upResample._setup(context);
35464
- this.downResample._setup(context);
35635
+ const composition = createResampleComposition({ context, streamContext: this.renderContext, ffmpegPath: this.properties.ffmpegPath, modelRate: DFN3_SAMPLE_RATE });
35636
+ if (composition) {
35637
+ this.upResample = composition.upResample;
35638
+ this.downResample = composition.downResample;
35639
+ }
35465
35640
  }
35466
35641
  _pipe(input) {
35467
35642
  if (!this.upResample || !this.downResample) return super._pipe(input);
@@ -35469,9 +35644,6 @@ var DeepFilterNet3Stream = class extends BufferedTransformStream {
35469
35644
  }
35470
35645
  async *_transform(buffered) {
35471
35646
  if (!this.session) throw new Error("deep-filter-net-3: stream not set up");
35472
- if (this.sampleRate !== void 0 && this.sampleRate !== DFN3_SAMPLE_RATE) {
35473
- throw new Error(`deep-filter-net-3: inference stream received ${this.sampleRate} Hz audio; expected ${DFN3_SAMPLE_RATE} Hz (composition in _setup should have resampled \u2014 check sampleRate property and pipeline setup)`);
35474
- }
35475
35647
  const session = this.session;
35476
35648
  const frames = buffered.frames;
35477
35649
  const channels = buffered.channels;
@@ -35482,9 +35654,9 @@ var DeepFilterNet3Stream = class extends BufferedTransformStream {
35482
35654
  this.dfnStates.push(createDfnState());
35483
35655
  }
35484
35656
  const outputChannels = [];
35485
- for (let ch = 0; ch < channels; ch++) {
35486
- const channel = chunk.samples[ch];
35487
- const dfnState = this.dfnStates[ch];
35657
+ for (let channelIndex = 0; channelIndex < channels; channelIndex++) {
35658
+ const channel = chunk.samples[channelIndex];
35659
+ const dfnState = this.dfnStates[channelIndex];
35488
35660
  if (!channel || !dfnState) {
35489
35661
  outputChannels.push(new Float32Array(frames));
35490
35662
  continue;
@@ -35504,7 +35676,7 @@ var DeepFilterNet3Node = class extends TransformNode {
35504
35676
  };
35505
35677
  DeepFilterNet3Node.nodeName = "DeepFilterNet3 (Denoiser)";
35506
35678
  DeepFilterNet3Node.packageName = PACKAGE_NAME;
35507
- DeepFilterNet3Node.description = "Remove background noise from speech using DeepFilterNet3 (48 kHz full-band CRN)";
35679
+ DeepFilterNet3Node.description = "Remove background noise from speech using DeepFilterNet3 (48 kHz full-band CRN). At other source rates, the internal resampling round trip may add or drop up to two source-rate frames.";
35508
35680
  DeepFilterNet3Node.schema = schema23;
35509
35681
  DeepFilterNet3Node.Stream = DeepFilterNet3Stream;
35510
35682
  function deepFilterNet3(options) {
@@ -35630,15 +35802,14 @@ var DtlnBlockStream = class {
35630
35802
  // src/transforms/dtln/utils/pump.ts
35631
35803
  var DTLN_SAMPLE_RATE = 16e3;
35632
35804
  var CHUNK_FRAMES5 = 16e3;
35633
- var RESAMPLE_DRAIN_CHUNK = 16384;
35634
35805
  var STEP_BATCH_SIZE = 16e3;
35635
35806
  var WARMUP_SAMPLES = WARMUP_SHIFTS * BLOCK_SHIFT;
35636
35807
  function stepAllChannels(args) {
35637
35808
  const { channels, streams, inputs, stepBatch, stepBatchLen, batchSize, warmupRemaining } = args;
35638
35809
  const stepOutputs = [];
35639
- for (let ch = 0; ch < channels; ch++) {
35640
- const stream = streams[ch];
35641
- const input = inputs[ch];
35810
+ for (let channel = 0; channel < channels; channel++) {
35811
+ const stream = streams[channel];
35812
+ const input = inputs[channel];
35642
35813
  if (!stream || !input) {
35643
35814
  stepOutputs.push(new Float32Array(BLOCK_SHIFT));
35644
35815
  continue;
@@ -35666,11 +35837,11 @@ function appendToStepBatch(args) {
35666
35837
  const space = batchSize - batchLen;
35667
35838
  const copy = Math.min(space, length - offset);
35668
35839
  const firstSample = samples[0];
35669
- for (let ch = 0; ch < channels; ch++) {
35670
- const src = samples[ch] ?? firstSample;
35671
- const dest = stepBatch[ch];
35672
- if (!src || !dest) continue;
35673
- dest.set(src.subarray(offset, offset + copy), batchLen);
35840
+ for (let channel = 0; channel < channels; channel++) {
35841
+ const sourceChannel = samples[channel] ?? firstSample;
35842
+ const dest = stepBatch[channel];
35843
+ if (!sourceChannel || !dest) continue;
35844
+ dest.set(sourceChannel.subarray(offset, offset + copy), batchLen);
35674
35845
  }
35675
35846
  batchLen += copy;
35676
35847
  offset += copy;
@@ -35678,90 +35849,39 @@ function appendToStepBatch(args) {
35678
35849
  return { stepBatchLen: batchLen, warmupRemaining: warmupLeft };
35679
35850
  }
35680
35851
  async function commitStepBatch(args) {
35681
- const { stepBatch, length, channels, pair, output, sourceRate, bitDepth, originalFrames, writerState } = args;
35852
+ const { stepBatch, length, channels, output, sampleRate, bitDepth, originalFrames, writerState } = args;
35682
35853
  if (length === 0) return;
35683
35854
  const slices = [];
35684
- for (let ch = 0; ch < channels; ch++) {
35685
- const src = stepBatch[ch] ?? new Float32Array(length);
35686
- slices.push(src.subarray(0, length));
35687
- }
35688
- if (pair) {
35689
- await pair.resampleOut.write(slices);
35690
- } else {
35691
- const remaining = Math.max(0, originalFrames - writerState.written);
35692
- if (remaining > 0) {
35693
- const take = Math.min(length, remaining);
35694
- const writeChannels = take === length ? slices : slices.map((channel) => channel.subarray(0, take));
35695
- await output.write(writeChannels, sourceRate, bitDepth);
35696
- writerState.written += take;
35697
- }
35698
- }
35699
- }
35700
- async function drainResampleOutToBuffer(args) {
35701
- const { resampleOut, output, channels, sourceRate, bitDepth, originalFrames, writerState } = args;
35702
- for (; ; ) {
35703
- const chunk = await resampleOut.read(RESAMPLE_DRAIN_CHUNK);
35704
- const got = chunk[0]?.length ?? 0;
35705
- if (got === 0) return;
35706
- await commitResampledFrames({ chunk, channels, output, sourceRate, bitDepth, originalFrames, writerState });
35707
- }
35855
+ for (let channel = 0; channel < channels; channel++) {
35856
+ const sourceChannel = stepBatch[channel] ?? new Float32Array(length);
35857
+ slices.push(sourceChannel.subarray(0, length));
35858
+ }
35859
+ const remaining = Math.max(0, originalFrames - writerState.written);
35860
+ if (remaining === 0) return;
35861
+ const take = Math.min(length, remaining);
35862
+ const writeChannels = take === length ? slices : slices.map((channel) => channel.subarray(0, take));
35863
+ await output.write(writeChannels, sampleRate, bitDepth);
35864
+ writerState.written += take;
35708
35865
  }
35709
35866
  async function pullNextChunkAt16k(args) {
35710
- const { buffer, pair, channels, frames } = args;
35711
- if (!pair) {
35712
- const chunk = await buffer.read(frames);
35713
- const got2 = chunk.samples[0]?.length ?? 0;
35714
- if (got2 === 0) return void 0;
35715
- const out2 = [];
35716
- for (let ch = 0; ch < channels; ch++) {
35717
- out2.push(chunk.samples[ch] ?? chunk.samples[0] ?? new Float32Array(got2));
35718
- }
35719
- return out2;
35720
- }
35721
- const out = await pair.resampleIn.read(frames);
35722
- const got = out[0]?.length ?? 0;
35867
+ const { buffer, channels, frames } = args;
35868
+ const chunk = await buffer.read(frames);
35869
+ const got = chunk.samples[0]?.length ?? 0;
35723
35870
  if (got === 0) return void 0;
35724
- return out;
35725
- }
35726
- async function pumpSourceToResampleIn(args) {
35727
- const { buffer, resampleIn, channels, chunkFrames } = args;
35728
- for (; ; ) {
35729
- const sourceChunk = await buffer.read(chunkFrames);
35730
- const sourceFrames = sourceChunk.samples[0]?.length ?? 0;
35731
- if (sourceFrames === 0) break;
35732
- const sourceChannels = [];
35733
- for (let ch = 0; ch < channels; ch++) {
35734
- sourceChannels.push(sourceChunk.samples[ch] ?? sourceChunk.samples[0] ?? new Float32Array(sourceFrames));
35735
- }
35736
- await resampleIn.write(sourceChannels);
35737
- if (sourceFrames < chunkFrames) break;
35738
- }
35739
- await resampleIn.end();
35740
- }
35741
- async function commitResampledFrames(args) {
35742
- const { chunk, channels, output, sourceRate, bitDepth, originalFrames, writerState } = args;
35743
- const firstChannel = chunk[0];
35744
- const got = firstChannel?.length ?? 0;
35745
- if (got === 0 || !firstChannel) return;
35746
- const remaining = originalFrames - writerState.written;
35747
- if (remaining <= 0) return;
35748
- const take = Math.min(got, remaining);
35749
- const writeChannels = [];
35750
- for (let ch = 0; ch < channels; ch++) {
35751
- const src = chunk[ch] ?? firstChannel;
35752
- writeChannels.push(take === got ? src : src.subarray(0, take));
35871
+ const out = [];
35872
+ for (let channel = 0; channel < channels; channel++) {
35873
+ out.push(chunk.samples[channel] ?? chunk.samples[0] ?? new Float32Array(got));
35753
35874
  }
35754
- await output.write(writeChannels, sourceRate, bitDepth);
35755
- writerState.written += take;
35875
+ return out;
35756
35876
  }
35757
- async function padTail(output, channels, originalFrames, written, sourceRate, bitDepth) {
35877
+ async function padTail(output, channels, originalFrames, written, sampleRate, bitDepth) {
35758
35878
  if (written >= originalFrames) return;
35759
35879
  const missing = originalFrames - written;
35760
35880
  const padChannels = [];
35761
35881
  for (let channel = 0; channel < Math.max(1, channels); channel++) {
35762
35882
  padChannels.push(new Float32Array(missing));
35763
35883
  }
35764
- await output.write(padChannels, sourceRate, bitDepth);
35884
+ await output.write(padChannels, sampleRate, bitDepth);
35765
35885
  }
35766
35886
 
35767
35887
  // src/transforms/dtln/index.ts
@@ -35774,9 +35894,10 @@ var schema24 = external_exports.object({
35774
35894
  fftwAddonPath: external_exports.string().default("").meta({ input: "file", mode: "open", binary: "fftw-addon", download: "https://github.com/visionsofparadise/fftw-addon" }).describe("FFTW native addon \u2014 CPU FFT acceleration")
35775
35895
  });
35776
35896
  var DtlnStream = class extends BufferedTransformStream {
35777
- constructor() {
35778
- super(...arguments);
35897
+ constructor(node, context) {
35898
+ super(node, context);
35779
35899
  this.blockSize = WHOLE_FILE;
35900
+ this.renderContext = context;
35780
35901
  }
35781
35902
  _setup(context) {
35782
35903
  const onnxProviders = filterOnnxProviders(context.executionProviders);
@@ -35786,30 +35907,22 @@ var DtlnStream = class extends BufferedTransformStream {
35786
35907
  const fft2 = initFftBackend(cpuProviders.length > 0 ? cpuProviders : ["cpu"], this.properties);
35787
35908
  this.fftBackend = fft2.backend;
35788
35909
  this.fftAddonOptions = fft2.addonOptions;
35910
+ const composition = createResampleComposition({ context, streamContext: this.renderContext, ffmpegPath: this.properties.ffmpegPath, modelRate: DTLN_SAMPLE_RATE });
35911
+ if (composition) {
35912
+ this.upResample = composition.upResample;
35913
+ this.downResample = composition.downResample;
35914
+ }
35915
+ }
35916
+ _pipe(input) {
35917
+ if (!this.upResample || !this.downResample) return super._pipe(input);
35918
+ return this.downResample._pipe(super._pipe(this.upResample._pipe(input)));
35789
35919
  }
35790
35920
  async *_transform(buffered) {
35791
35921
  const originalFrames = buffered.frames;
35792
35922
  const channels = buffered.channels;
35793
35923
  if (originalFrames === 0 || channels === 0) return;
35794
- const sourceRate = this.sampleRate ?? DTLN_SAMPLE_RATE;
35795
35924
  const bitDepth = this.bitDepth;
35796
- const needsResample = sourceRate !== DTLN_SAMPLE_RATE;
35797
35925
  await buffered.reset();
35798
- let pair;
35799
- if (needsResample) {
35800
- pair = {
35801
- resampleIn: new ResampleStream(this.properties.ffmpegPath, {
35802
- sourceSampleRate: sourceRate,
35803
- targetSampleRate: DTLN_SAMPLE_RATE,
35804
- channels
35805
- }),
35806
- resampleOut: new ResampleStream(this.properties.ffmpegPath, {
35807
- sourceSampleRate: DTLN_SAMPLE_RATE,
35808
- targetSampleRate: sourceRate,
35809
- channels
35810
- })
35811
- };
35812
- }
35813
35926
  const output = new BlockBuffer();
35814
35927
  try {
35815
35928
  await this.runMainPass({
@@ -35817,40 +35930,32 @@ var DtlnStream = class extends BufferedTransformStream {
35817
35930
  output,
35818
35931
  channels,
35819
35932
  originalFrames,
35820
- sourceRate,
35821
- bitDepth,
35822
- pair
35933
+ bitDepth
35823
35934
  });
35824
35935
  await output.reset();
35825
35936
  yield* output.iterate(CHUNK_FRAMES5);
35826
35937
  } finally {
35827
- if (pair) {
35828
- await Promise.all([pair.resampleIn.close(), pair.resampleOut.close()]);
35829
- }
35830
35938
  await output.close();
35831
35939
  }
35832
35940
  }
35833
35941
  async runMainPass(args) {
35834
- const { buffer, output, channels, originalFrames, sourceRate, bitDepth, pair } = args;
35942
+ const { buffer, output, channels, originalFrames, bitDepth } = args;
35835
35943
  const streams = [];
35836
- for (let ch = 0; ch < channels; ch++) {
35944
+ for (let channel = 0; channel < channels; channel++) {
35837
35945
  streams.push(new DtlnBlockStream({ session1: this.session1, session2: this.session2, fftBackend: this.fftBackend, fftAddonOptions: this.fftAddonOptions }));
35838
35946
  }
35839
35947
  const stepAccum = [];
35840
- for (let ch = 0; ch < channels; ch++) stepAccum.push(new Float32Array(BLOCK_SHIFT));
35948
+ for (let channel = 0; channel < channels; channel++) stepAccum.push(new Float32Array(BLOCK_SHIFT));
35841
35949
  let stepAccumLen = 0;
35842
35950
  const stepBatch = [];
35843
- for (let ch = 0; ch < channels; ch++) stepBatch.push(new Float32Array(STEP_BATCH_SIZE));
35951
+ for (let channel = 0; channel < channels; channel++) stepBatch.push(new Float32Array(STEP_BATCH_SIZE));
35844
35952
  let stepBatchLen = 0;
35845
35953
  let samplesFed = 0;
35846
35954
  let warmupRemaining = WARMUP_SAMPLES;
35847
35955
  const writerState = { written: 0 };
35848
- const pumpDone = pair !== void 0 ? pumpSourceToResampleIn({ buffer, resampleIn: pair.resampleIn, channels, chunkFrames: CHUNK_FRAMES5 }) : Promise.resolve();
35849
- const drainerDone = pair !== void 0 ? drainResampleOutToBuffer({ resampleOut: pair.resampleOut, output, channels, sourceRate, bitDepth, originalFrames, writerState }) : Promise.resolve();
35850
- const total16k = Math.round(originalFrames * DTLN_SAMPLE_RATE / sourceRate);
35851
- const progressGate = createProgressGate(total16k);
35956
+ const progressGate = createProgressGate(originalFrames);
35852
35957
  for (; ; ) {
35853
- const got16k = await pullNextChunkAt16k({ buffer, pair, channels, frames: CHUNK_FRAMES5 });
35958
+ const got16k = await pullNextChunkAt16k({ buffer, channels, frames: CHUNK_FRAMES5 });
35854
35959
  if (got16k === void 0) break;
35855
35960
  const firstChannel = got16k[0];
35856
35961
  const chunkFrames = firstChannel?.length ?? 0;
@@ -35859,11 +35964,11 @@ var DtlnStream = class extends BufferedTransformStream {
35859
35964
  while (consumed < chunkFrames) {
35860
35965
  const need = BLOCK_SHIFT - stepAccumLen;
35861
35966
  const take = Math.min(need, chunkFrames - consumed);
35862
- for (let ch = 0; ch < channels; ch++) {
35863
- const src = got16k[ch] ?? firstChannel;
35864
- const dest = stepAccum[ch];
35865
- if (!src || !dest) continue;
35866
- dest.set(src.subarray(consumed, consumed + take), stepAccumLen);
35967
+ for (let channel = 0; channel < channels; channel++) {
35968
+ const sourceChannel = got16k[channel] ?? firstChannel;
35969
+ const dest = stepAccum[channel];
35970
+ if (!sourceChannel || !dest) continue;
35971
+ dest.set(sourceChannel.subarray(consumed, consumed + take), stepAccumLen);
35867
35972
  }
35868
35973
  stepAccumLen += take;
35869
35974
  consumed += take;
@@ -35874,50 +35979,45 @@ var DtlnStream = class extends BufferedTransformStream {
35874
35979
  samplesFed += BLOCK_SHIFT;
35875
35980
  stepAccumLen = 0;
35876
35981
  if (stepBatchLen >= STEP_BATCH_SIZE) {
35877
- await commitStepBatch({ stepBatch, length: stepBatchLen, channels, pair, output, sourceRate, bitDepth, originalFrames, writerState });
35982
+ await commitStepBatch({ stepBatch, length: stepBatchLen, channels, output, sampleRate: DTLN_SAMPLE_RATE, bitDepth, originalFrames, writerState });
35878
35983
  stepBatchLen = 0;
35879
35984
  }
35880
35985
  }
35881
35986
  }
35882
- const doneFrames = Math.min(samplesFed, total16k);
35883
- if (progressGate(doneFrames, Date.now())) this.emitProgress("process", doneFrames, total16k);
35987
+ const doneFrames = Math.min(samplesFed, originalFrames);
35988
+ if (progressGate(doneFrames, Date.now())) this.emitProgress("process", doneFrames, originalFrames);
35884
35989
  }
35885
- await pumpDone;
35886
35990
  if (samplesFed > 0 && samplesFed < BLOCK_LEN) {
35887
35991
  const zeroInputs = [];
35888
- for (let ch = 0; ch < channels; ch++) zeroInputs.push(new Float32Array(BLOCK_SHIFT));
35992
+ for (let channel = 0; channel < channels; channel++) zeroInputs.push(new Float32Array(BLOCK_SHIFT));
35889
35993
  while (samplesFed < BLOCK_LEN) {
35890
35994
  const result = stepAllChannels({ channels, streams, inputs: zeroInputs, stepBatch, stepBatchLen, batchSize: STEP_BATCH_SIZE, warmupRemaining });
35891
35995
  stepBatchLen = result.stepBatchLen;
35892
35996
  warmupRemaining = result.warmupRemaining;
35893
35997
  samplesFed += BLOCK_SHIFT;
35894
35998
  if (stepBatchLen >= STEP_BATCH_SIZE) {
35895
- await commitStepBatch({ stepBatch, length: stepBatchLen, channels, pair, output, sourceRate, bitDepth, originalFrames, writerState });
35999
+ await commitStepBatch({ stepBatch, length: stepBatchLen, channels, output, sampleRate: DTLN_SAMPLE_RATE, bitDepth, originalFrames, writerState });
35896
36000
  stepBatchLen = 0;
35897
36001
  }
35898
36002
  }
35899
36003
  }
35900
36004
  const flushOutputs = [];
35901
- for (let ch = 0; ch < channels; ch++) flushOutputs.push(streams[ch]?.flush() ?? new Float32Array(0));
36005
+ for (let channel = 0; channel < channels; channel++) flushOutputs.push(streams[channel]?.flush() ?? new Float32Array(0));
35902
36006
  const flushLen = flushOutputs[0]?.length ?? 0;
35903
36007
  if (flushLen > 0) {
35904
36008
  const result = appendToStepBatch({ samples: flushOutputs, channels, stepBatch, stepBatchLen, batchSize: STEP_BATCH_SIZE, warmupRemaining });
35905
36009
  stepBatchLen = result.stepBatchLen;
35906
36010
  warmupRemaining = result.warmupRemaining;
35907
36011
  if (stepBatchLen >= STEP_BATCH_SIZE) {
35908
- await commitStepBatch({ stepBatch, length: stepBatchLen, channels, pair, output, sourceRate, bitDepth, originalFrames, writerState });
36012
+ await commitStepBatch({ stepBatch, length: stepBatchLen, channels, output, sampleRate: DTLN_SAMPLE_RATE, bitDepth, originalFrames, writerState });
35909
36013
  stepBatchLen = 0;
35910
36014
  }
35911
36015
  }
35912
36016
  if (stepBatchLen > 0) {
35913
- await commitStepBatch({ stepBatch, length: stepBatchLen, channels, pair, output, sourceRate, bitDepth, originalFrames, writerState });
36017
+ await commitStepBatch({ stepBatch, length: stepBatchLen, channels, output, sampleRate: DTLN_SAMPLE_RATE, bitDepth, originalFrames, writerState });
35914
36018
  stepBatchLen = 0;
35915
36019
  }
35916
- if (pair) {
35917
- await pair.resampleOut.end();
35918
- }
35919
- await drainerDone;
35920
- await padTail(output, channels, originalFrames, writerState.written, sourceRate, bitDepth);
36020
+ await padTail(output, channels, originalFrames, writerState.written, DTLN_SAMPLE_RATE, bitDepth);
35921
36021
  }
35922
36022
  };
35923
36023
  var DtlnNode = class extends TransformNode {
@@ -35994,12 +36094,12 @@ function computeIstftScaled(real, imag, outputLength) {
35994
36094
  // src/transforms/htdemucs/utils/stems.ts
35995
36095
  function buildModelInput(segLeft, segRight, stftLeft, stftRight, segmentLength, xBins, xFrames) {
35996
36096
  const xData = new Float32Array(4 * xBins * xFrames);
35997
- for (let ch = 0; ch < 2; ch++) {
35998
- const stftCh = ch === 0 ? stftLeft : stftRight;
36097
+ for (let channel = 0; channel < 2; channel++) {
36098
+ const stftCh = channel === 0 ? stftLeft : stftRight;
35999
36099
  for (let freq = 0; freq < xBins; freq++) {
36000
36100
  for (let frame = 0; frame < xFrames; frame++) {
36001
- const realIdx = 2 * ch * xBins * xFrames + freq * xFrames + frame;
36002
- const imagIdx = (2 * ch + 1) * xBins * xFrames + freq * xFrames + frame;
36101
+ const realIdx = 2 * channel * xBins * xFrames + freq * xFrames + frame;
36102
+ const imagIdx = (2 * channel + 1) * xBins * xFrames + freq * xFrames + frame;
36003
36103
  const srcFrame = frame + 2;
36004
36104
  xData[realIdx] = stftCh.real[srcFrame]?.[freq] ?? 0;
36005
36105
  xData[imagIdx] = stftCh.imag[srcFrame]?.[freq] ?? 0;
@@ -36034,8 +36134,8 @@ function mixStemsToStereo(stemAccum, sumWeight, stemGains, stats, nStable) {
36034
36134
  function extractStems(xtOut, xOut, workspace, stemOutputs, weight, segmentOffset, chunkLength, segmentLength) {
36035
36135
  const { freqRealBuffers, freqImagBuffers, nbFrames, stftLen, stftPad, pad: pad2, xBins, xFrames } = workspace;
36036
36136
  for (let source = 0; source < 4; source++) {
36037
- for (let ch = 0; ch < 2; ch++) {
36038
- const xtIndex = source * 2 * segmentLength + ch * segmentLength;
36137
+ for (let channel = 0; channel < 2; channel++) {
36138
+ const xtIndex = source * 2 * segmentLength + channel * segmentLength;
36039
36139
  for (let frame = 0; frame < nbFrames; frame++) {
36040
36140
  freqRealBuffers[frame]?.fill(0);
36041
36141
  freqImagBuffers[frame]?.fill(0);
@@ -36044,8 +36144,8 @@ function extractStems(xtOut, xOut, workspace, stemOutputs, weight, segmentOffset
36044
36144
  const baseOffset = source * 4 * xBins * xFrames;
36045
36145
  for (let freq = 0; freq < xBins; freq++) {
36046
36146
  for (let frame = 0; frame < xFrames; frame++) {
36047
- const realIdx = baseOffset + 2 * ch * xBins * xFrames + freq * xFrames + frame;
36048
- const imagIdx = baseOffset + (2 * ch + 1) * xBins * xFrames + freq * xFrames + frame;
36147
+ const realIdx = baseOffset + 2 * channel * xBins * xFrames + freq * xFrames + frame;
36148
+ const imagIdx = baseOffset + (2 * channel + 1) * xBins * xFrames + freq * xFrames + frame;
36049
36149
  const destFrame = frame + 2;
36050
36150
  const realArr = freqRealBuffers[destFrame];
36051
36151
  const imagArr = freqImagBuffers[destFrame];
@@ -36063,10 +36163,10 @@ function extractStems(xtOut, xOut, workspace, stemOutputs, weight, segmentOffset
36063
36163
  const freqVal = freqWaveform[freqOffset + index] ?? 0;
36064
36164
  const combined = timeVal + freqVal;
36065
36165
  const wt = weight[index] ?? 1;
36066
- const outIdx = source * 2 + ch;
36067
- const arr = stemOutputs[outIdx];
36068
- if (arr) {
36069
- arr[segmentOffset + index] = (arr[segmentOffset + index] ?? 0) + combined * wt;
36166
+ const outIdx = source * 2 + channel;
36167
+ const stemOutput = stemOutputs[outIdx];
36168
+ if (stemOutput) {
36169
+ stemOutput[segmentOffset + index] = (stemOutput[segmentOffset + index] ?? 0) + combined * wt;
36070
36170
  }
36071
36171
  }
36072
36172
  }
@@ -36088,41 +36188,33 @@ var SEGMENT_SAMPLES = 343980;
36088
36188
  var OVERLAP = 0.25;
36089
36189
  var TRANSITION_POWER = 1;
36090
36190
  var CHUNK_FRAMES6 = 44100;
36091
- var RESAMPLE_DRAIN_CHUNK2 = 16384;
36092
36191
  var STEM_OUTPUTS = 4 * 2;
36093
36192
  var HtdemucsStream = class extends BufferedTransformStream {
36094
- constructor() {
36095
- super(...arguments);
36193
+ constructor(node, context) {
36194
+ super(node, context);
36096
36195
  this.blockSize = WHOLE_FILE;
36196
+ this.renderContext = context;
36097
36197
  }
36098
- _setup(_context) {
36198
+ _setup(context) {
36099
36199
  this.session = createOnnxSession(this.properties.onnxAddonPath, this.properties.modelPath, { executionProviders: ["cpu"] }, (message, data) => this.log(message, data));
36200
+ const composition = createResampleComposition({ context, streamContext: this.renderContext, ffmpegPath: this.properties.ffmpegPath, modelRate: HTDEMUCS_SAMPLE_RATE });
36201
+ if (composition) {
36202
+ this.upResample = composition.upResample;
36203
+ this.downResample = composition.downResample;
36204
+ }
36205
+ }
36206
+ _pipe(input) {
36207
+ if (!this.upResample || !this.downResample) return super._pipe(input);
36208
+ return this.downResample._pipe(super._pipe(this.upResample._pipe(input)));
36100
36209
  }
36101
36210
  async *_transform(buffered) {
36102
36211
  const originalFrames = buffered.frames;
36103
36212
  const channels = buffered.channels;
36104
36213
  if (originalFrames === 0 || channels === 0) return;
36105
- const sourceRate = this.sampleRate ?? HTDEMUCS_SAMPLE_RATE;
36106
36214
  const bitDepth = this.bitDepth;
36107
- const needsResample = sourceRate !== HTDEMUCS_SAMPLE_RATE;
36108
36215
  const stats = await computeStreamingStats(buffered, channels);
36109
36216
  this.log("streaming stats computed", { mean: stats.mean, std: stats.std });
36110
36217
  await buffered.reset();
36111
- let pair;
36112
- if (needsResample) {
36113
- pair = {
36114
- resampleIn: new ResampleStream(this.properties.ffmpegPath, {
36115
- sourceSampleRate: sourceRate,
36116
- targetSampleRate: HTDEMUCS_SAMPLE_RATE,
36117
- channels: 2
36118
- }),
36119
- resampleOut: new ResampleStream(this.properties.ffmpegPath, {
36120
- sourceSampleRate: HTDEMUCS_SAMPLE_RATE,
36121
- targetSampleRate: sourceRate,
36122
- channels: 2
36123
- })
36124
- };
36125
- }
36126
36218
  const output = new BlockBuffer();
36127
36219
  try {
36128
36220
  await this.runMainPass({
@@ -36130,26 +36222,19 @@ var HtdemucsStream = class extends BufferedTransformStream {
36130
36222
  output,
36131
36223
  channels,
36132
36224
  originalFrames,
36133
- sourceRate,
36134
36225
  bitDepth,
36135
- stats,
36136
- pair
36226
+ stats
36137
36227
  });
36138
36228
  await output.reset();
36139
36229
  yield* output.iterate(CHUNK_FRAMES6);
36140
36230
  } finally {
36141
- if (pair) {
36142
- await Promise.all([pair.resampleIn.close(), pair.resampleOut.close()]);
36143
- }
36144
36231
  await output.close();
36145
36232
  }
36146
36233
  }
36147
36234
  async runMainPass(args) {
36148
- const { buffer, output, channels, originalFrames, sourceRate, bitDepth, stats, pair } = args;
36235
+ const { buffer, output, channels, originalFrames, bitDepth, stats } = args;
36149
36236
  const stride = Math.round((1 - OVERLAP) * SEGMENT_SAMPLES);
36150
36237
  const writerState = { written: 0 };
36151
- const pumpDone = pair !== void 0 ? pumpSourceToResampleIn2({ buffer, resampleIn: pair.resampleIn, channels, chunkFrames: CHUNK_FRAMES6 }) : Promise.resolve();
36152
- const drainerDone = pair !== void 0 ? drainResampleOutToBuffer2({ resampleOut: pair.resampleOut, output, channels, sourceRate, bitDepth, originalFrames, writerState }) : Promise.resolve();
36153
36238
  const weight = buildTriangularWeight(SEGMENT_SAMPLES, TRANSITION_POWER);
36154
36239
  const pad2 = Math.floor(HOP_SIZE2 / 2) * 3;
36155
36240
  const le = Math.ceil(SEGMENT_SAMPLES / HOP_SIZE2);
@@ -36187,14 +36272,13 @@ var HtdemucsStream = class extends BufferedTransformStream {
36187
36272
  const { stems } = this.properties;
36188
36273
  const stemGains = [stems.drums, stems.bass, stems.other, stems.vocals];
36189
36274
  const inv = 1 / (stats.std || 1);
36190
- const modelRateFrames = Math.round(originalFrames * HTDEMUCS_SAMPLE_RATE / sourceRate);
36191
- const progressGate = createProgressGate(modelRateFrames);
36275
+ const progressGate = createProgressGate(originalFrames);
36192
36276
  let stableEmitted = 0;
36193
36277
  for (; ; ) {
36194
36278
  if (!inputExhausted) {
36195
36279
  while (segFilled < SEGMENT_SAMPLES) {
36196
36280
  const need = SEGMENT_SAMPLES - segFilled;
36197
- const got = await pullNextChunkAt441({ buffer, pair, channels, frames: Math.min(need, CHUNK_FRAMES6) });
36281
+ const got = await pullNextChunkAt441({ buffer, channels, frames: Math.min(need, CHUNK_FRAMES6) });
36198
36282
  if (got === void 0 || got[0].length === 0) {
36199
36283
  inputExhausted = true;
36200
36284
  break;
@@ -36236,17 +36320,15 @@ var HtdemucsStream = class extends BufferedTransformStream {
36236
36320
  sumWeight,
36237
36321
  stats,
36238
36322
  stemGains,
36239
- pair,
36240
36323
  output,
36241
36324
  channels,
36242
- sourceRate,
36243
36325
  bitDepth,
36244
36326
  originalFrames,
36245
36327
  writerState
36246
36328
  });
36247
36329
  stableEmitted += nStable;
36248
- const doneFrames = Math.min(stableEmitted, modelRateFrames);
36249
- if (progressGate(doneFrames, Date.now())) this.emitProgress("process", doneFrames, modelRateFrames);
36330
+ const doneFrames = Math.min(stableEmitted, originalFrames);
36331
+ if (progressGate(doneFrames, Date.now())) this.emitProgress("process", doneFrames, originalFrames);
36250
36332
  if (!isFinalIter) {
36251
36333
  segLeft.copyWithin(0, nStable, SEGMENT_SAMPLES);
36252
36334
  segRight.copyWithin(0, nStable, SEGMENT_SAMPLES);
@@ -36257,35 +36339,26 @@ var HtdemucsStream = class extends BufferedTransformStream {
36257
36339
  break;
36258
36340
  }
36259
36341
  }
36260
- await pumpDone;
36261
- if (pair) {
36262
- await pair.resampleOut.end();
36263
- }
36264
- await drainerDone;
36265
- await padTail2(output, channels, originalFrames, writerState.written, sourceRate, bitDepth);
36342
+ await padTail2(output, channels, originalFrames, writerState.written, HTDEMUCS_SAMPLE_RATE, bitDepth);
36266
36343
  }
36267
36344
  async emitStable(args) {
36268
- const { nStable, stemAccum, sumWeight, stats, stemGains, pair, output, channels, sourceRate, bitDepth, originalFrames, writerState } = args;
36345
+ const { nStable, stemAccum, sumWeight, stats, stemGains, output, channels, bitDepth, originalFrames, writerState } = args;
36269
36346
  if (nStable <= 0) return;
36270
36347
  const { outLeft, outRight } = mixStemsToStereo(stemAccum, sumWeight, stemGains, stats, nStable);
36271
36348
  bandpass([outLeft, outRight], HTDEMUCS_SAMPLE_RATE, this.properties.highPass, this.properties.lowPass);
36272
- if (pair) {
36273
- await pair.resampleOut.write([outLeft, outRight]);
36274
- } else {
36275
- const writeChannels = buildWriteChannels(outLeft, outRight, channels);
36276
- const remaining = Math.max(0, originalFrames - writerState.written);
36277
- if (remaining > 0) {
36278
- const take = Math.min(nStable, remaining);
36279
- const sliced = take === nStable ? writeChannels : writeChannels.map((channel) => channel.subarray(0, take));
36280
- await output.write(sliced, sourceRate, bitDepth);
36281
- writerState.written += take;
36282
- }
36349
+ const writeChannels = buildWriteChannels(outLeft, outRight, channels);
36350
+ const remaining = Math.max(0, originalFrames - writerState.written);
36351
+ if (remaining > 0) {
36352
+ const take = Math.min(nStable, remaining);
36353
+ const sliced = take === nStable ? writeChannels : writeChannels.map((channel) => channel.subarray(0, take));
36354
+ await output.write(sliced, HTDEMUCS_SAMPLE_RATE, bitDepth);
36355
+ writerState.written += take;
36283
36356
  }
36284
36357
  for (let stem = 0; stem < STEM_OUTPUTS; stem++) {
36285
- const arr = stemAccum[stem];
36286
- if (!arr) continue;
36287
- arr.copyWithin(0, nStable, SEGMENT_SAMPLES);
36288
- arr.fill(0, SEGMENT_SAMPLES - nStable, SEGMENT_SAMPLES);
36358
+ const stemAccumulator = stemAccum[stem];
36359
+ if (!stemAccumulator) continue;
36360
+ stemAccumulator.copyWithin(0, nStable, SEGMENT_SAMPLES);
36361
+ stemAccumulator.fill(0, SEGMENT_SAMPLES - nStable, SEGMENT_SAMPLES);
36289
36362
  }
36290
36363
  sumWeight.copyWithin(0, nStable, SEGMENT_SAMPLES);
36291
36364
  sumWeight.fill(0, SEGMENT_SAMPLES - nStable, SEGMENT_SAMPLES);
@@ -36341,59 +36414,14 @@ async function computeStreamingStats(buffer, channels) {
36341
36414
  return { mean, std };
36342
36415
  }
36343
36416
  async function pullNextChunkAt441(args) {
36344
- const { buffer, pair, channels, frames } = args;
36345
- if (!pair) {
36346
- const chunk = await buffer.read(frames);
36347
- const got2 = chunk.samples[0]?.length ?? 0;
36348
- if (got2 === 0) return void 0;
36349
- const left2 = chunk.samples[0] ?? new Float32Array(got2);
36350
- const right2 = channels >= 2 ? chunk.samples[1] ?? left2 : left2;
36351
- return [left2, right2];
36352
- }
36353
- const out = await pair.resampleIn.read(frames);
36354
- const got = out[0]?.length ?? 0;
36417
+ const { buffer, channels, frames } = args;
36418
+ const chunk = await buffer.read(frames);
36419
+ const got = chunk.samples[0]?.length ?? 0;
36355
36420
  if (got === 0) return void 0;
36356
- const left = out[0] ?? new Float32Array(got);
36357
- const right = out[1] ?? left;
36421
+ const left = chunk.samples[0] ?? new Float32Array(got);
36422
+ const right = channels >= 2 ? chunk.samples[1] ?? left : left;
36358
36423
  return [left, right];
36359
36424
  }
36360
- async function pumpSourceToResampleIn2(args) {
36361
- const { buffer, resampleIn, channels, chunkFrames } = args;
36362
- for (; ; ) {
36363
- const sourceChunk = await buffer.read(chunkFrames);
36364
- const sourceFrames = sourceChunk.samples[0]?.length ?? 0;
36365
- if (sourceFrames === 0) break;
36366
- const sourceLeft = sourceChunk.samples[0] ?? new Float32Array(sourceFrames);
36367
- const sourceRight = channels >= 2 ? sourceChunk.samples[1] ?? sourceLeft : sourceLeft;
36368
- await resampleIn.write([sourceLeft, sourceRight]);
36369
- if (sourceFrames < chunkFrames) break;
36370
- }
36371
- await resampleIn.end();
36372
- }
36373
- async function drainResampleOutToBuffer2(args) {
36374
- const { resampleOut, output, channels, sourceRate, bitDepth, originalFrames, writerState } = args;
36375
- for (; ; ) {
36376
- const chunk = await resampleOut.read(RESAMPLE_DRAIN_CHUNK2);
36377
- const got = chunk[0]?.length ?? 0;
36378
- if (got === 0) return;
36379
- await commitResampledFrames2({ chunk, channels, output, sourceRate, bitDepth, originalFrames, writerState });
36380
- }
36381
- }
36382
- async function commitResampledFrames2(args) {
36383
- const { chunk, channels, output, sourceRate, bitDepth, originalFrames, writerState } = args;
36384
- const firstChannel = chunk[0];
36385
- const got = firstChannel?.length ?? 0;
36386
- if (got === 0 || !firstChannel) return;
36387
- const remaining = originalFrames - writerState.written;
36388
- if (remaining <= 0) return;
36389
- const take = Math.min(got, remaining);
36390
- const right = chunk[1] ?? firstChannel;
36391
- const writeLeft = take === got ? firstChannel : firstChannel.subarray(0, take);
36392
- const writeRight = take === got ? right : right.subarray(0, take);
36393
- const writeChannels = buildWriteChannels(writeLeft, writeRight, channels);
36394
- await output.write(writeChannels, sourceRate, bitDepth);
36395
- writerState.written += take;
36396
- }
36397
36425
  function buildWriteChannels(left, right, channels) {
36398
36426
  const out = [];
36399
36427
  for (let channel = 0; channel < channels; channel++) {
@@ -36403,14 +36431,14 @@ function buildWriteChannels(left, right, channels) {
36403
36431
  }
36404
36432
  return out;
36405
36433
  }
36406
- async function padTail2(output, channels, originalFrames, written, sourceRate, bitDepth) {
36434
+ async function padTail2(output, channels, originalFrames, written, sampleRate, bitDepth) {
36407
36435
  if (written >= originalFrames) return;
36408
36436
  const missing = originalFrames - written;
36409
36437
  const padChannels = [];
36410
36438
  for (let channel = 0; channel < Math.max(1, channels); channel++) {
36411
36439
  padChannels.push(new Float32Array(missing));
36412
36440
  }
36413
- await output.write(padChannels, sourceRate, bitDepth);
36441
+ await output.write(padChannels, sampleRate, bitDepth);
36414
36442
  }
36415
36443
  var HtdemucsNode = class extends TransformNode {
36416
36444
  };
@@ -36570,39 +36598,31 @@ var SEGMENT_SAMPLES2 = N_FFT2 + (DIM_T3 - 1) * HOP_SIZE4;
36570
36598
  var OVERLAP2 = 0.25;
36571
36599
  var TRANSITION_POWER2 = 1;
36572
36600
  var CHUNK_FRAMES7 = 44100;
36573
- var RESAMPLE_DRAIN_CHUNK3 = 16384;
36574
36601
  var KimVocal2Stream = class extends BufferedTransformStream {
36575
36602
  constructor(node, context) {
36576
36603
  super(node, context);
36577
36604
  this.blockSize = WHOLE_FILE;
36578
36605
  this.fftInstance = new MixedRadixFft(N_FFT2);
36606
+ this.renderContext = context;
36579
36607
  }
36580
36608
  _setup(context) {
36581
36609
  this.session = createOnnxSession(this.properties.onnxAddonPath, this.properties.modelPath, { executionProviders: filterOnnxProviders(context.executionProviders) }, (message, data) => this.log(message, data));
36610
+ const composition = createResampleComposition({ context, streamContext: this.renderContext, ffmpegPath: this.properties.ffmpegPath, modelRate: SAMPLE_RATE });
36611
+ if (composition) {
36612
+ this.upResample = composition.upResample;
36613
+ this.downResample = composition.downResample;
36614
+ }
36615
+ }
36616
+ _pipe(input) {
36617
+ if (!this.upResample || !this.downResample) return super._pipe(input);
36618
+ return this.downResample._pipe(super._pipe(this.upResample._pipe(input)));
36582
36619
  }
36583
36620
  async *_transform(buffered) {
36584
36621
  const originalFrames = buffered.frames;
36585
36622
  const channels = buffered.channels;
36586
36623
  if (originalFrames === 0 || channels === 0) return;
36587
- const sourceRate = this.sampleRate ?? SAMPLE_RATE;
36588
36624
  const bitDepth = this.bitDepth;
36589
- const needsResample = sourceRate !== SAMPLE_RATE;
36590
36625
  await buffered.reset();
36591
- let pair;
36592
- if (needsResample) {
36593
- pair = {
36594
- resampleIn: new ResampleStream(this.properties.ffmpegPath, {
36595
- sourceSampleRate: sourceRate,
36596
- targetSampleRate: SAMPLE_RATE,
36597
- channels: 2
36598
- }),
36599
- resampleOut: new ResampleStream(this.properties.ffmpegPath, {
36600
- sourceSampleRate: SAMPLE_RATE,
36601
- targetSampleRate: sourceRate,
36602
- channels: 2
36603
- })
36604
- };
36605
- }
36606
36626
  const output = new BlockBuffer();
36607
36627
  try {
36608
36628
  await this.runMainPass({
@@ -36610,26 +36630,19 @@ var KimVocal2Stream = class extends BufferedTransformStream {
36610
36630
  output,
36611
36631
  channels,
36612
36632
  originalFrames,
36613
- sourceRate,
36614
- bitDepth,
36615
- pair
36633
+ bitDepth
36616
36634
  });
36617
36635
  await output.reset();
36618
36636
  yield* output.iterate(CHUNK_FRAMES7);
36619
36637
  } finally {
36620
- if (pair) {
36621
- await Promise.all([pair.resampleIn.close(), pair.resampleOut.close()]);
36622
- }
36623
36638
  await output.close();
36624
36639
  }
36625
36640
  }
36626
36641
  async runMainPass(args) {
36627
- const { buffer, output, channels, originalFrames, sourceRate, bitDepth, pair } = args;
36642
+ const { buffer, output, channels, originalFrames, bitDepth } = args;
36628
36643
  const stride = Math.round((1 - OVERLAP2) * SEGMENT_SAMPLES2);
36629
36644
  const isMono = channels < 2;
36630
36645
  const writerState = { written: 0 };
36631
- const pumpDone = pair !== void 0 ? pumpSourceToResampleIn3({ buffer, resampleIn: pair.resampleIn, channels, chunkFrames: CHUNK_FRAMES7 }) : Promise.resolve();
36632
- const drainerDone = pair !== void 0 ? drainResampleOutToBuffer3({ resampleOut: pair.resampleOut, output, channels, sourceRate, bitDepth, originalFrames, writerState }) : Promise.resolve();
36633
36646
  const weight = buildTransitionWindow(SEGMENT_SAMPLES2, TRANSITION_POWER2);
36634
36647
  const workspace = createSegmentWorkspace(SEGMENT_SAMPLES2);
36635
36648
  const segLeft = new Float32Array(SEGMENT_SAMPLES2);
@@ -36639,14 +36652,13 @@ var KimVocal2Stream = class extends BufferedTransformStream {
36639
36652
  const outAccumLeft = new Float32Array(SEGMENT_SAMPLES2);
36640
36653
  const outAccumRight = new Float32Array(SEGMENT_SAMPLES2);
36641
36654
  const sumWeight = new Float32Array(SEGMENT_SAMPLES2);
36642
- const modelRateFrames = Math.round(originalFrames * SAMPLE_RATE / sourceRate);
36643
- const progressGate = createProgressGate(modelRateFrames);
36655
+ const progressGate = createProgressGate(originalFrames);
36644
36656
  let stableEmitted = 0;
36645
36657
  for (; ; ) {
36646
36658
  if (!inputExhausted) {
36647
36659
  while (segFilled < SEGMENT_SAMPLES2) {
36648
36660
  const need = SEGMENT_SAMPLES2 - segFilled;
36649
- const got = await pullNextChunkAt4412({ buffer, pair, channels, frames: Math.min(need, CHUNK_FRAMES7) });
36661
+ const got = await pullNextChunkAt4412({ buffer, channels, frames: Math.min(need, CHUNK_FRAMES7) });
36650
36662
  if (got === void 0 || got[0].length === 0) {
36651
36663
  inputExhausted = true;
36652
36664
  break;
@@ -36679,17 +36691,15 @@ var KimVocal2Stream = class extends BufferedTransformStream {
36679
36691
  outAccumLeft,
36680
36692
  outAccumRight,
36681
36693
  sumWeight,
36682
- pair,
36683
36694
  output,
36684
36695
  channels,
36685
- sourceRate,
36686
36696
  bitDepth,
36687
36697
  originalFrames,
36688
36698
  writerState
36689
36699
  });
36690
36700
  stableEmitted += nStable;
36691
- const doneFrames = Math.min(stableEmitted, modelRateFrames);
36692
- if (progressGate(doneFrames, Date.now())) this.emitProgress("process", doneFrames, modelRateFrames);
36701
+ const doneFrames = Math.min(stableEmitted, originalFrames);
36702
+ if (progressGate(doneFrames, Date.now())) this.emitProgress("process", doneFrames, originalFrames);
36693
36703
  if (!isFinalIter) {
36694
36704
  segLeft.copyWithin(0, nStable, SEGMENT_SAMPLES2);
36695
36705
  segRight.copyWithin(0, nStable, SEGMENT_SAMPLES2);
@@ -36700,15 +36710,10 @@ var KimVocal2Stream = class extends BufferedTransformStream {
36700
36710
  break;
36701
36711
  }
36702
36712
  }
36703
- await pumpDone;
36704
- if (pair) {
36705
- await pair.resampleOut.end();
36706
- }
36707
- await drainerDone;
36708
- await padTail3(output, channels, originalFrames, writerState.written, sourceRate, bitDepth);
36713
+ await padTail3(output, channels, originalFrames, writerState.written, SAMPLE_RATE, bitDepth);
36709
36714
  }
36710
36715
  async emitStable(args) {
36711
- const { nStable, outAccumLeft, outAccumRight, sumWeight, pair, output, channels, sourceRate, bitDepth, originalFrames, writerState } = args;
36716
+ const { nStable, outAccumLeft, outAccumRight, sumWeight, output, channels, bitDepth, originalFrames, writerState } = args;
36712
36717
  if (nStable <= 0) return;
36713
36718
  const outLeft = new Float32Array(nStable);
36714
36719
  const outRight = new Float32Array(nStable);
@@ -36718,17 +36723,13 @@ var KimVocal2Stream = class extends BufferedTransformStream {
36718
36723
  outRight[index] = sw > 0 ? (outAccumRight[index] ?? 0) / sw : outAccumRight[index] ?? 0;
36719
36724
  }
36720
36725
  bandpass([outLeft, outRight], SAMPLE_RATE, this.properties.highPass, this.properties.lowPass);
36721
- if (pair) {
36722
- await pair.resampleOut.write([outLeft, outRight]);
36723
- } else {
36724
- const writeChannels = buildWriteChannels2(outLeft, outRight, channels);
36725
- const remaining = Math.max(0, originalFrames - writerState.written);
36726
- if (remaining > 0) {
36727
- const take = Math.min(nStable, remaining);
36728
- const sliced = take === nStable ? writeChannels : writeChannels.map((channel) => channel.subarray(0, take));
36729
- await output.write(sliced, sourceRate, bitDepth);
36730
- writerState.written += take;
36731
- }
36726
+ const writeChannels = buildWriteChannels2(outLeft, outRight, channels);
36727
+ const remaining = Math.max(0, originalFrames - writerState.written);
36728
+ if (remaining > 0) {
36729
+ const take = Math.min(nStable, remaining);
36730
+ const sliced = take === nStable ? writeChannels : writeChannels.map((channel) => channel.subarray(0, take));
36731
+ await output.write(sliced, SAMPLE_RATE, bitDepth);
36732
+ writerState.written += take;
36732
36733
  }
36733
36734
  outAccumLeft.copyWithin(0, nStable, SEGMENT_SAMPLES2);
36734
36735
  outAccumLeft.fill(0, SEGMENT_SAMPLES2 - nStable, SEGMENT_SAMPLES2);
@@ -36739,59 +36740,14 @@ var KimVocal2Stream = class extends BufferedTransformStream {
36739
36740
  }
36740
36741
  };
36741
36742
  async function pullNextChunkAt4412(args) {
36742
- const { buffer, pair, channels, frames } = args;
36743
- if (!pair) {
36744
- const chunk = await buffer.read(frames);
36745
- const got2 = chunk.samples[0]?.length ?? 0;
36746
- if (got2 === 0) return void 0;
36747
- const left2 = chunk.samples[0] ?? new Float32Array(got2);
36748
- const right2 = channels >= 2 ? chunk.samples[1] ?? left2 : left2;
36749
- return [left2, right2];
36750
- }
36751
- const out = await pair.resampleIn.read(frames);
36752
- const got = out[0]?.length ?? 0;
36743
+ const { buffer, channels, frames } = args;
36744
+ const chunk = await buffer.read(frames);
36745
+ const got = chunk.samples[0]?.length ?? 0;
36753
36746
  if (got === 0) return void 0;
36754
- const left = out[0] ?? new Float32Array(got);
36755
- const right = out[1] ?? left;
36747
+ const left = chunk.samples[0] ?? new Float32Array(got);
36748
+ const right = channels >= 2 ? chunk.samples[1] ?? left : left;
36756
36749
  return [left, right];
36757
36750
  }
36758
- async function pumpSourceToResampleIn3(args) {
36759
- const { buffer, resampleIn, channels, chunkFrames } = args;
36760
- for (; ; ) {
36761
- const sourceChunk = await buffer.read(chunkFrames);
36762
- const sourceFrames = sourceChunk.samples[0]?.length ?? 0;
36763
- if (sourceFrames === 0) break;
36764
- const sourceLeft = sourceChunk.samples[0] ?? new Float32Array(sourceFrames);
36765
- const sourceRight = channels >= 2 ? sourceChunk.samples[1] ?? sourceLeft : sourceLeft;
36766
- await resampleIn.write([sourceLeft, sourceRight]);
36767
- if (sourceFrames < chunkFrames) break;
36768
- }
36769
- await resampleIn.end();
36770
- }
36771
- async function drainResampleOutToBuffer3(args) {
36772
- const { resampleOut, output, channels, sourceRate, bitDepth, originalFrames, writerState } = args;
36773
- for (; ; ) {
36774
- const chunk = await resampleOut.read(RESAMPLE_DRAIN_CHUNK3);
36775
- const got = chunk[0]?.length ?? 0;
36776
- if (got === 0) return;
36777
- await commitResampledFrames3({ chunk, channels, output, sourceRate, bitDepth, originalFrames, writerState });
36778
- }
36779
- }
36780
- async function commitResampledFrames3(args) {
36781
- const { chunk, channels, output, sourceRate, bitDepth, originalFrames, writerState } = args;
36782
- const firstChannel = chunk[0];
36783
- const got = firstChannel?.length ?? 0;
36784
- if (got === 0 || !firstChannel) return;
36785
- const remaining = originalFrames - writerState.written;
36786
- if (remaining <= 0) return;
36787
- const take = Math.min(got, remaining);
36788
- const right = chunk[1] ?? firstChannel;
36789
- const writeLeft = take === got ? firstChannel : firstChannel.subarray(0, take);
36790
- const writeRight = take === got ? right : right.subarray(0, take);
36791
- const writeChannels = buildWriteChannels2(writeLeft, writeRight, channels);
36792
- await output.write(writeChannels, sourceRate, bitDepth);
36793
- writerState.written += take;
36794
- }
36795
36751
  function buildWriteChannels2(left, right, channels) {
36796
36752
  const out = [];
36797
36753
  for (let channel = 0; channel < channels; channel++) {
@@ -36801,14 +36757,14 @@ function buildWriteChannels2(left, right, channels) {
36801
36757
  }
36802
36758
  return out;
36803
36759
  }
36804
- async function padTail3(output, channels, originalFrames, written, sourceRate, bitDepth) {
36760
+ async function padTail3(output, channels, originalFrames, written, sampleRate, bitDepth) {
36805
36761
  if (written >= originalFrames) return;
36806
36762
  const missing = originalFrames - written;
36807
36763
  const padChannels = [];
36808
36764
  for (let channel = 0; channel < Math.max(1, channels); channel++) {
36809
36765
  padChannels.push(new Float32Array(missing));
36810
36766
  }
36811
- await output.write(padChannels, sourceRate, bitDepth);
36767
+ await output.write(padChannels, sampleRate, bitDepth);
36812
36768
  }
36813
36769
  var KimVocal2Node = class extends TransformNode {
36814
36770
  };
@@ -36821,8 +36777,72 @@ function kimVocal2(options) {
36821
36777
  return new KimVocal2Node(options);
36822
36778
  }
36823
36779
  var CHUNK_FRAMES8 = 48e3;
36780
+ var TELEMETRY_PREFIX = "VST_HOST_EVENT ";
36781
+ var DIAGNOSTIC_TAIL_BYTES = 64 * 1024;
36824
36782
  var READY_LINE = "READY\n";
36825
36783
  var READY_TIMEOUT_MS = 3e5;
36784
+ var isFiniteNonnegativeNumber = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0;
36785
+ var isUnknownRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
36786
+ function parseVstHostEvent(line) {
36787
+ if (!line.startsWith(TELEMETRY_PREFIX)) return void 0;
36788
+ let parsed;
36789
+ try {
36790
+ parsed = JSON.parse(line.slice(TELEMETRY_PREFIX.length));
36791
+ } catch {
36792
+ return void 0;
36793
+ }
36794
+ if (!isUnknownRecord(parsed)) return void 0;
36795
+ const record3 = parsed;
36796
+ if (record3.type !== "liveness" || record3.phase !== "process") return void 0;
36797
+ if (record3.state !== "active" && record3.state !== "idle") return void 0;
36798
+ if (!isFiniteNonnegativeNumber(record3.elapsedMs)) return void 0;
36799
+ if (!isFiniteNonnegativeNumber(record3.processCpuDeltaMs)) return void 0;
36800
+ if (!isFiniteNonnegativeNumber(record3.processCpuMs)) return void 0;
36801
+ return {
36802
+ type: record3.type,
36803
+ phase: record3.phase,
36804
+ elapsedMs: record3.elapsedMs,
36805
+ processCpuDeltaMs: record3.processCpuDeltaMs,
36806
+ processCpuMs: record3.processCpuMs,
36807
+ state: record3.state
36808
+ };
36809
+ }
36810
+ function observeVstHostStderr(stderr, onLiveness) {
36811
+ const decoder = new StringDecoder("utf8");
36812
+ let pendingLine = "";
36813
+ let diagnosticTail = Buffer.alloc(0);
36814
+ const appendDiagnostic = (text) => {
36815
+ if (text.length === 0) return;
36816
+ const bytes = Buffer.from(text);
36817
+ const combined = diagnosticTail.length === 0 ? bytes : Buffer.concat([diagnosticTail, bytes]);
36818
+ diagnosticTail = combined.length <= DIAGNOSTIC_TAIL_BYTES ? combined : combined.subarray(combined.length - DIAGNOSTIC_TAIL_BYTES);
36819
+ };
36820
+ const consumeCompleteLines = () => {
36821
+ for (; ; ) {
36822
+ const newlineIndex = pendingLine.indexOf("\n");
36823
+ if (newlineIndex === -1) return;
36824
+ const line = pendingLine.slice(0, newlineIndex);
36825
+ pendingLine = pendingLine.slice(newlineIndex + 1);
36826
+ const event = parseVstHostEvent(line);
36827
+ if (event !== void 0) {
36828
+ onLiveness?.(event);
36829
+ } else {
36830
+ appendDiagnostic(`${line}
36831
+ `);
36832
+ }
36833
+ }
36834
+ };
36835
+ stderr.on("data", (chunk) => {
36836
+ pendingLine += decoder.write(chunk);
36837
+ consumeCompleteLines();
36838
+ });
36839
+ stderr.once("end", () => {
36840
+ pendingLine += decoder.end();
36841
+ appendDiagnostic(pendingLine);
36842
+ pendingLine = "";
36843
+ });
36844
+ return () => diagnosticTail.toString("utf8");
36845
+ }
36826
36846
  var VstHostExitedBeforeReadyError = class extends Error {
36827
36847
  constructor(code, stderr) {
36828
36848
  super(`vst-host exited before READY (code ${code ?? "null"}): ${stderr}`);
@@ -36831,7 +36851,7 @@ var VstHostExitedBeforeReadyError = class extends Error {
36831
36851
  this.stderr = stderr;
36832
36852
  }
36833
36853
  };
36834
- function spawnVstHost(binaryPath, args) {
36854
+ function spawnVstHost(binaryPath, args, options = {}) {
36835
36855
  const proc = spawn(binaryPath, [...args], {
36836
36856
  stdio: ["pipe", "pipe", "pipe"]
36837
36857
  });
@@ -36841,10 +36861,7 @@ function spawnVstHost(binaryPath, args) {
36841
36861
  const stdin = proc.stdin;
36842
36862
  const stdout = proc.stdout;
36843
36863
  const stderr = proc.stderr;
36844
- const stderrChunks = [];
36845
- stderr.on("data", (chunk) => {
36846
- stderrChunks.push(chunk);
36847
- });
36864
+ const getStderrTail = observeVstHostStderr(stderr, options.onLiveness);
36848
36865
  const ready = new Promise((resolve, reject) => {
36849
36866
  const seen = [];
36850
36867
  const cleanup = () => {
@@ -36875,7 +36892,7 @@ function spawnVstHost(binaryPath, args) {
36875
36892
  fail(new Error(`vst-host failed to start: ${error50.message}`));
36876
36893
  };
36877
36894
  const onClose = (code) => {
36878
- const stderrOutput = Buffer.concat(stderrChunks).toString();
36895
+ const stderrOutput = getStderrTail();
36879
36896
  fail(new VstHostExitedBeforeReadyError(code, stderrOutput));
36880
36897
  };
36881
36898
  const timer = setTimeout(() => {
@@ -36887,7 +36904,7 @@ function spawnVstHost(binaryPath, args) {
36887
36904
  });
36888
36905
  stdin.on("error", () => {
36889
36906
  });
36890
- return { proc, stdin, stdout, stderr, ready, stderrChunks };
36907
+ return { proc, stdin, stdout, stderr, ready, getStderrTail };
36891
36908
  }
36892
36909
  var CLEAN_WRAPPER_EXIT_CODES = /* @__PURE__ */ new Set([0, 1, 2]);
36893
36910
  function isRetryableInitCrash(error50) {
@@ -36898,7 +36915,7 @@ async function spawnVstHostReady(binaryPath, args, options = {}) {
36898
36915
  const maxAttempts = options.maxAttempts ?? 5;
36899
36916
  const backoffMs = options.backoffMs ?? 750;
36900
36917
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
36901
- const handle = spawnVstHost(binaryPath, args);
36918
+ const handle = spawnVstHost(binaryPath, args, { onLiveness: options.onLiveness });
36902
36919
  try {
36903
36920
  await handle.ready;
36904
36921
  return handle;
@@ -36922,9 +36939,11 @@ async function writeStagesJson(stages) {
36922
36939
  }
36923
36940
  };
36924
36941
  }
36925
- async function processStreamingThroughVstHost(handle, buffer, channelCount, sampleRate, bitDepth) {
36942
+ async function processStreamingThroughVstHost(handle, buffer, options) {
36943
+ const { channelCount, sampleRate, bitDepth, onInputProgress, onOutputProgress } = options;
36926
36944
  const inputFrames = buffer.frames;
36927
36945
  const expectedOutputBytes = inputFrames * channelCount * 4;
36946
+ let inputFramesDone = 0;
36928
36947
  const stdoutEnd = new Promise((resolve) => {
36929
36948
  handle.stdout.once("end", () => resolve());
36930
36949
  });
@@ -36937,15 +36956,22 @@ async function processStreamingThroughVstHost(handle, buffer, channelCount, samp
36937
36956
  const chunkFrames = chunk.samples[0]?.length ?? 0;
36938
36957
  if (chunkFrames === 0) break;
36939
36958
  const channelArrays = [];
36940
- for (let ch = 0; ch < channelCount; ch++) {
36941
- channelArrays.push(chunk.samples[ch] ?? new Float32Array(chunkFrames));
36959
+ for (let channel = 0; channel < channelCount; channel++) {
36960
+ channelArrays.push(chunk.samples[channel] ?? new Float32Array(chunkFrames));
36942
36961
  }
36943
36962
  const interleaved = interleave(channelArrays, chunkFrames, channelCount);
36944
- const buf = Buffer.from(interleaved.buffer, interleaved.byteOffset, interleaved.byteLength);
36945
- const canWrite = handle.stdin.write(buf);
36963
+ const interleavedBuffer = Buffer.from(interleaved.buffer, interleaved.byteOffset, interleaved.byteLength);
36964
+ const canWrite = handle.stdin.write(interleavedBuffer);
36946
36965
  if (!canWrite) {
36947
36966
  await waitForDrain(handle.proc, handle.stdin);
36948
36967
  }
36968
+ inputFramesDone = Math.min(inputFrames, inputFramesDone + chunkFrames);
36969
+ onInputProgress?.({
36970
+ framesDone: inputFramesDone,
36971
+ framesTotal: inputFrames,
36972
+ bytesDone: inputFramesDone * channelCount * 4,
36973
+ bytesTotal: expectedOutputBytes
36974
+ });
36949
36975
  if (chunkFrames < CHUNK_FRAMES8) break;
36950
36976
  }
36951
36977
  handle.stdin.end();
@@ -36954,6 +36980,7 @@ async function processStreamingThroughVstHost(handle, buffer, channelCount, samp
36954
36980
  let stdoutTail = Buffer.alloc(0);
36955
36981
  let stdoutError;
36956
36982
  const bytesPerFrame = channelCount * 4;
36983
+ let outputFramesDone = 0;
36957
36984
  let writeChain = Promise.resolve();
36958
36985
  const onData = (chunk) => {
36959
36986
  if (stdoutError !== void 0) return;
@@ -36968,7 +36995,16 @@ async function processStreamingThroughVstHost(handle, buffer, channelCount, samp
36968
36995
  const aligned = combined.subarray(0, alignedBytes);
36969
36996
  stdoutTail = combined.length === alignedBytes ? Buffer.alloc(0) : combined.subarray(alignedBytes);
36970
36997
  const channels = deinterleaveBuffer(aligned, channelCount);
36971
- writeChain = writeChain.then(() => buffer.write(channels, sampleRate, bitDepth)).catch((error50) => {
36998
+ writeChain = writeChain.then(async () => {
36999
+ await buffer.write(channels, sampleRate, bitDepth);
37000
+ outputFramesDone = Math.min(inputFrames, outputFramesDone + alignedFrames);
37001
+ onOutputProgress?.({
37002
+ framesDone: outputFramesDone,
37003
+ framesTotal: inputFrames,
37004
+ bytesDone: outputFramesDone * bytesPerFrame,
37005
+ bytesTotal: expectedOutputBytes
37006
+ });
37007
+ }).catch((error50) => {
36972
37008
  stdoutError ?? (stdoutError = error50 instanceof Error ? error50 : new Error(String(error50)));
36973
37009
  });
36974
37010
  };
@@ -36978,7 +37014,7 @@ async function processStreamingThroughVstHost(handle, buffer, channelCount, samp
36978
37014
  const exit = await exited;
36979
37015
  if (stdoutError !== void 0) throw stdoutError;
36980
37016
  if (exit.code !== 0) {
36981
- const stderrOutput = Buffer.concat(handle.stderrChunks).toString();
37017
+ const stderrOutput = handle.getStderrTail();
36982
37018
  throw new Error(`vst-host exited with code ${exit.code ?? "null"}${exit.signal ? ` (signal ${exit.signal})` : ""}: ${stderrOutput}`);
36983
37019
  }
36984
37020
  if (outputBytesReceived !== expectedOutputBytes) {
@@ -37019,6 +37055,8 @@ var Vst3Stream = class extends BufferedTransformStream {
37019
37055
  const channels = buffered.channels;
37020
37056
  const sampleRate = this.sampleRate ?? 44100;
37021
37057
  const bd = buffered.bitDepth;
37058
+ const inputGate = createProgressGate(buffered.frames);
37059
+ const outputGate = createProgressGate(buffered.frames);
37022
37060
  const args = [
37023
37061
  ...this.properties.extraArgs ?? [],
37024
37062
  "--stages-json",
@@ -37029,11 +37067,48 @@ var Vst3Stream = class extends BufferedTransformStream {
37029
37067
  String(channels)
37030
37068
  ];
37031
37069
  const handle = await spawnVstHostReady(this.properties.vstHostPath, args, {
37070
+ onLiveness: (event) => {
37071
+ this.log(
37072
+ "vst-host liveness",
37073
+ {
37074
+ phase: event.phase,
37075
+ elapsedMs: event.elapsedMs,
37076
+ processCpuDeltaMs: event.processCpuDeltaMs,
37077
+ processCpuMs: event.processCpuMs,
37078
+ state: event.state
37079
+ },
37080
+ event.state === "idle" ? "warn" : "info"
37081
+ );
37082
+ },
37032
37083
  onRetry: (failedAttempt, error50) => {
37033
37084
  this.log("vst-host init crash, retrying", { attempt: failedAttempt, error: error50.message }, "warn");
37034
37085
  }
37035
37086
  });
37036
- await processStreamingThroughVstHost(handle, buffered, channels, sampleRate, bd);
37087
+ await processStreamingThroughVstHost(handle, buffered, {
37088
+ channelCount: channels,
37089
+ sampleRate,
37090
+ bitDepth: bd,
37091
+ onInputProgress: (progress) => {
37092
+ if (inputGate(progress.framesDone, Date.now())) {
37093
+ this.log("vst-host input", {
37094
+ framesDone: progress.framesDone,
37095
+ framesTotal: progress.framesTotal,
37096
+ bytesDone: progress.bytesDone,
37097
+ bytesTotal: progress.bytesTotal
37098
+ });
37099
+ }
37100
+ },
37101
+ onOutputProgress: (progress) => {
37102
+ if (outputGate(progress.framesDone, Date.now())) {
37103
+ this.log("vst-host output", {
37104
+ framesDone: progress.framesDone,
37105
+ framesTotal: progress.framesTotal,
37106
+ bytesDone: progress.bytesDone,
37107
+ bytesTotal: progress.bytesTotal
37108
+ });
37109
+ }
37110
+ }
37111
+ });
37037
37112
  await buffered.reset();
37038
37113
  yield* buffered.iterate(44100);
37039
37114
  }