@buffered-audio/nodes 0.22.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
  }
@@ -29843,6 +29812,9 @@ var BufferedAudioNode = class {
29843
29812
  get id() {
29844
29813
  return this.properties.id;
29845
29814
  }
29815
+ get packageVersion() {
29816
+ return this.properties.packageVersion;
29817
+ }
29846
29818
  get isBypassed() {
29847
29819
  return this.properties.bypass === true;
29848
29820
  }
@@ -29853,75 +29825,6 @@ var BufferedAudioNode = class {
29853
29825
  BufferedAudioNode.apiVersion = 1;
29854
29826
  BufferedAudioNode.description = "";
29855
29827
  BufferedAudioNode.schema = external_exports2.object({});
29856
- var BufferedSourceStream = class extends BufferedStream {
29857
- constructor() {
29858
- super(...arguments);
29859
- this.framesRead = 0;
29860
- this.hasStarted = false;
29861
- }
29862
- setup(context) {
29863
- return Promise.resolve(this._setup(context));
29864
- }
29865
- _setup(context) {
29866
- let done = false;
29867
- this.framesRead = 0;
29868
- this.processingMs = 0;
29869
- this.hasStarted = false;
29870
- const { signal, durationFrames: sourceTotalFrames, highWaterMark } = context;
29871
- const readGate = createProgressGate(sourceTotalFrames);
29872
- return new ReadableStream(
29873
- {
29874
- pull: async (controller) => {
29875
- if (done) return;
29876
- if (signal?.aborted) {
29877
- done = true;
29878
- await this.destroy();
29879
- controller.close();
29880
- return;
29881
- }
29882
- try {
29883
- if (!this.hasStarted) {
29884
- this.hasStarted = true;
29885
- this.emitStarted();
29886
- }
29887
- const start = performance.now();
29888
- const chunk = await this._read();
29889
- this.processingMs += performance.now() - start;
29890
- if (!chunk) {
29891
- done = true;
29892
- this.emitProgress("read", this.framesRead, sourceTotalFrames);
29893
- this.emitFinished({ framesDone: this.framesRead, processingMs: this.processingMs });
29894
- await this.destroy();
29895
- controller.close();
29896
- return;
29897
- }
29898
- this.framesRead += chunk.samples[0]?.length ?? 0;
29899
- controller.enqueue(chunk);
29900
- if (readGate(this.framesRead, Date.now())) this.emitProgress("read", this.framesRead, sourceTotalFrames);
29901
- } catch (error482) {
29902
- done = true;
29903
- await this.destroy();
29904
- controller.error(error482);
29905
- }
29906
- },
29907
- cancel: async () => {
29908
- done = true;
29909
- await this.destroy();
29910
- }
29911
- },
29912
- { highWaterMark }
29913
- );
29914
- }
29915
- };
29916
- var SourceNode = class extends BufferedAudioNode {
29917
- to(child) {
29918
- const head = "head" in child ? child.head : child;
29919
- this.properties = { ...this.properties, children: [...this.properties.children ?? [], head] };
29920
- }
29921
- createRenderJob(options) {
29922
- return new RenderJob(this, options);
29923
- }
29924
- };
29925
29828
  var BufferedTargetStream = class extends BufferedStream {
29926
29829
  constructor() {
29927
29830
  super(...arguments);
@@ -29929,7 +29832,7 @@ var BufferedTargetStream = class extends BufferedStream {
29929
29832
  this.framesWritten = 0;
29930
29833
  }
29931
29834
  setup(readable, context) {
29932
- this.sourceTotalFrames = context.durationFrames;
29835
+ this.sourceTotalFrames = context.sourceTotalFrames;
29933
29836
  return Promise.resolve(this._setup(readable, context));
29934
29837
  }
29935
29838
  _setup(input, _context) {
@@ -29968,23 +29871,106 @@ var BufferedTargetStream = class extends BufferedStream {
29968
29871
  };
29969
29872
  var TargetNode = class extends BufferedAudioNode {
29970
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
+ }
29971
29903
  function teeReadable(readable, items) {
29972
29904
  if (items.length === 0) return [];
29973
29905
  const first = items[0];
29974
29906
  if (items.length === 1) return [[readable, first]];
29975
- const pairs = [];
29976
- let current = readable;
29977
- for (let offset = 0; offset < items.length - 1; offset++) {
29978
- const [left, right] = current.tee();
29979
- pairs.push([left, items[offset]]);
29980
- current = right;
29981
- }
29982
- pairs.push([current, items[items.length - 1]]);
29983
- return pairs;
29984
- }
29985
- function isSourceStream(stream) {
29986
- 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]]);
29987
29972
  }
29973
+ var RENDER_LIVENESS_INTERVAL_MS = 3e4;
29988
29974
  var RenderJob = class {
29989
29975
  constructor(source, options) {
29990
29976
  this.options = options;
@@ -29995,10 +29981,11 @@ var RenderJob = class {
29995
29981
  let streamIdCounter = 0;
29996
29982
  this.renderContext = { events: this.events, nextStreamId: () => streamIdCounter++ };
29997
29983
  this.root = this.build(source, /* @__PURE__ */ new Set());
29998
- if (!isSourceStream(this.root.stream)) {
29984
+ const sourceStream = this.root.stream;
29985
+ if (!(sourceStream instanceof BufferedSourceStream)) {
29999
29986
  throw new Error("Source node did not produce a source stream");
30000
29987
  }
30001
- this.sourceStream = this.root.stream;
29988
+ this.sourceStream = sourceStream;
30002
29989
  }
30003
29990
  get streams() {
30004
29991
  return this.streamsMap;
@@ -30049,53 +30036,65 @@ var RenderJob = class {
30049
30036
  async render() {
30050
30037
  if (this.started) throw new Error("RenderJob is single-use; render() was already called");
30051
30038
  this.started = true;
30052
- const meta32 = await this.sourceStream.getMetadata();
30053
- const defaultProviders = ["gpu", "cpu-native", "cpu"];
30054
- const memoryLimit = this.options?.memoryLimit ?? 256 * 1024 * 1024;
30055
- const stages = Math.max(1, this.countStreams());
30056
- const chunkSize = this.options?.chunkSize ?? 128 * 1024;
30057
- const bytesPerChunk = meta32.channels * chunkSize * 4;
30058
- const computedHighWaterMark = Math.max(1, Math.floor(memoryLimit / (stages * bytesPerChunk)));
30059
- const context = {
30060
- executionProviders: this.options?.executionProviders ?? defaultProviders,
30061
- memoryLimit,
30062
- durationFrames: meta32.durationFrames,
30063
- highWaterMark: this.options?.highWaterMark ?? computedHighWaterMark,
30064
- signal: this.signal()
30065
- };
30066
- 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);
30067
30043
  try {
30068
- const readable = await this.sourceStream.setup(context);
30069
- const promises = await this.wireChildren(this.root.children, readable, context);
30070
- await Promise.all(promises);
30071
- } finally {
30072
- for (const streams of this.streamsMap.values()) {
30073
- for (const stream of streams) {
30074
- 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
+ }
30075
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
+ };
30076
30079
  }
30077
- const totalMs = performance.now() - start;
30078
- const audioDurationMs = meta32.durationFrames !== void 0 ? meta32.durationFrames / meta32.sampleRate * 1e3 : 0;
30079
- this.timingData = {
30080
- totalMs,
30081
- audioDurationMs,
30082
- realTimeMultiplier: audioDurationMs > 0 ? audioDurationMs / totalMs : 0
30083
- };
30080
+ } finally {
30081
+ clearInterval(livenessInterval);
30084
30082
  }
30085
30083
  }
30086
- async wireChildren(children, readable, context) {
30084
+ async setupChildren(children, readable, context) {
30087
30085
  const pairs = teeReadable(readable, children);
30088
- 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 })));
30089
30087
  return nested.flat();
30090
30088
  }
30091
- async wire(plan, input, context) {
30089
+ async setup(plan, input, context) {
30092
30090
  const { stream } = plan;
30093
30091
  if (stream instanceof BufferedTargetStream) {
30094
30092
  return [stream.setup(input, context)];
30095
30093
  }
30096
30094
  if (stream instanceof BufferedTransformStream || stream instanceof UnbufferedTransformStream) {
30095
+ const nodeName = plan.node.constructor.nodeName;
30097
30096
  const output = await stream.setup(input, context);
30098
- return this.wireChildren(plan.children, output, context);
30097
+ return this.setupChildren(plan.children, assertFirstBlockSampleRate(output, context.sampleRate, nodeName), context);
30099
30098
  }
30100
30099
  throw new Error(`Unexpected stream type for node "${plan.node.constructor.nodeName}"`);
30101
30100
  }
@@ -30109,15 +30108,118 @@ var RenderJob = class {
30109
30108
  return AbortSignal.any([this.options.signal, this.abortController.signal]);
30110
30109
  }
30111
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
+ };
30112
30180
  var TransformNode = class extends BufferedAudioNode {
30113
30181
  to(child) {
30114
30182
  const head = "head" in child ? child.head : child;
30115
30183
  this.properties = { ...this.properties, children: [...this.properties.children ?? [], head] };
30116
30184
  }
30117
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
+ );
30118
30219
  var graphNodeSchema = external_exports2.object({
30119
30220
  id: external_exports2.string().min(1),
30120
30221
  packageName: external_exports2.string().min(1),
30222
+ packageVersion: external_exports2.string().min(1),
30121
30223
  nodeName: external_exports2.string().min(1),
30122
30224
  parameters: external_exports2.record(external_exports2.string(), external_exports2.unknown()).optional(),
30123
30225
  options: external_exports2.object({
@@ -30132,15 +30234,8 @@ external_exports2.object({
30132
30234
  id: external_exports2.uuid(),
30133
30235
  name: external_exports2.string().default("Untitled"),
30134
30236
  apiVersion: external_exports2.number().int().min(1),
30135
- packages: external_exports2.record(external_exports2.string(), external_exports2.string().min(1)),
30136
30237
  nodes: external_exports2.array(graphNodeSchema),
30137
30238
  edges: external_exports2.array(graphEdgeSchema)
30138
- }).superRefine((definition, context) => {
30139
- for (const node of definition.nodes) {
30140
- if (!Object.prototype.hasOwnProperty.call(definition.packages, node.packageName)) {
30141
- context.addIssue(`Node "${node.id}" references package "${node.packageName}" which has no entry in the graph packages map`);
30142
- }
30143
- }
30144
30239
  });
30145
30240
 
30146
30241
  // package.json
@@ -30460,13 +30555,13 @@ var ReadWavStream = class extends BufferedSourceStream {
30460
30555
  const fileChannels = format.channels;
30461
30556
  const selectedChannels = this.properties.channels;
30462
30557
  const allChannels = [];
30463
- for (let ch = 0; ch < fileChannels; ch++) {
30558
+ for (let fileChannel = 0; fileChannel < fileChannels; fileChannel++) {
30464
30559
  allChannels.push(new Float32Array(frames));
30465
30560
  }
30466
30561
  for (let frame = 0; frame < frames; frame++) {
30467
- for (let ch = 0; ch < fileChannels; ch++) {
30468
- const byteOffset = frame * format.blockAlign + ch * (format.bitsPerSample / 8);
30469
- 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];
30470
30565
  if (channel) {
30471
30566
  channel[frame] = readSample(chunk, byteOffset, format.bitsPerSample, format.audioFormat);
30472
30567
  }
@@ -30827,7 +30922,7 @@ var SpectrogramStream = class extends BufferedTargetStream {
30827
30922
  this.outputBins = numBands;
30828
30923
  }
30829
30924
  this.sampleBuffers = [];
30830
- for (let ch = 0; ch < this.channels; ch++) {
30925
+ for (let channel = 0; channel < this.channels; channel++) {
30831
30926
  this.sampleBuffers.push(new Float32Array(this.sampleBufferCapacity));
30832
30927
  }
30833
30928
  if (!this.fileHandle) return;
@@ -30848,17 +30943,17 @@ var SpectrogramStream = class extends BufferedTargetStream {
30848
30943
  const frames = chunk.samples[0]?.length ?? 0;
30849
30944
  if (this.sampleBufferOffset + frames > this.sampleBufferCapacity) {
30850
30945
  const newCapacity = Math.max(this.sampleBufferCapacity * 2, this.sampleBufferOffset + frames);
30851
- for (let ch = 0; ch < this.channels; ch++) {
30946
+ for (let channel = 0; channel < this.channels; channel++) {
30852
30947
  const newBuf = new Float32Array(newCapacity);
30853
- newBuf.set(this.sampleBuffers[ch].subarray(0, this.sampleBufferOffset));
30854
- this.sampleBuffers[ch] = newBuf;
30948
+ newBuf.set(this.sampleBuffers[channel].subarray(0, this.sampleBufferOffset));
30949
+ this.sampleBuffers[channel] = newBuf;
30855
30950
  }
30856
30951
  this.sampleBufferCapacity = newCapacity;
30857
30952
  }
30858
- for (let ch = 0; ch < this.channels; ch++) {
30859
- const src = chunk.samples[ch];
30860
- if (!src) continue;
30861
- 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);
30862
30957
  }
30863
30958
  this.sampleBufferOffset += frames;
30864
30959
  await this.processAccumulatedSamples(false);
@@ -30882,9 +30977,9 @@ var SpectrogramStream = class extends BufferedTargetStream {
30882
30977
  const halfSize = this.linearBins;
30883
30978
  const magScale = 2 / fftSize;
30884
30979
  if (flush && this.sampleBufferOffset > 0 && this.sampleBufferOffset < fftSize) {
30885
- for (let ch = 0; ch < this.channels; ch++) {
30886
- const buf = this.sampleBuffers[ch];
30887
- 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);
30888
30983
  }
30889
30984
  this.sampleBufferOffset = fftSize;
30890
30985
  }
@@ -30898,9 +30993,9 @@ var SpectrogramStream = class extends BufferedTargetStream {
30898
30993
  this.writeBufferOffset = 0;
30899
30994
  this.writeBufferFileOffset = this.fileOffset;
30900
30995
  }
30901
- for (let ch = 0; ch < this.channels; ch++) {
30996
+ for (let channel = 0; channel < this.channels; channel++) {
30902
30997
  const frames = computeSpectrogramFrames(
30903
- this.sampleBuffers[ch],
30998
+ this.sampleBuffers[channel],
30904
30999
  batchFrames,
30905
31000
  fftSize,
30906
31001
  hopSize,
@@ -30914,30 +31009,30 @@ var SpectrogramStream = class extends BufferedTargetStream {
30914
31009
  this.magnitudes
30915
31010
  );
30916
31011
  for (const frame of frames) {
30917
- await this.writeFrame(ch, frame);
31012
+ await this.writeFrame(channel, frame);
30918
31013
  }
30919
31014
  }
30920
31015
  await this.flushWriteBuffer();
30921
31016
  const keepFrom = batchFrames * hopSize;
30922
31017
  const keepCount = this.sampleBufferOffset - keepFrom;
30923
31018
  if (keepCount > 0) {
30924
- for (let ch = 0; ch < this.channels; ch++) {
30925
- const buf = this.sampleBuffers[ch];
30926
- 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);
30927
31022
  }
30928
31023
  }
30929
31024
  this.sampleBufferOffset = keepCount > 0 ? keepCount : 0;
30930
31025
  }
30931
- async writeFrame(ch, frame) {
31026
+ async writeFrame(channel, frame) {
30932
31027
  const frameByteSize = this.outputBins * this.channels * 4;
30933
31028
  if (this.writeBuffer && this.writeBufferOffset + frameByteSize > this.writeBuffer.length) {
30934
31029
  await this.flushWriteBuffer();
30935
31030
  }
30936
- const buf = this.writeBuffer;
30937
- if (!buf) return;
31031
+ const writeBuffer = this.writeBuffer;
31032
+ if (!writeBuffer) return;
30938
31033
  const offset = this.writeBufferOffset;
30939
31034
  for (let bin = 0; bin < this.outputBins; bin++) {
30940
- buf.writeFloatLE(frame[bin], offset + (ch * this.outputBins + bin) * 4);
31035
+ writeBuffer.writeFloatLE(frame[bin], offset + (channel * this.outputBins + bin) * 4);
30941
31036
  }
30942
31037
  this.writeBufferOffset += frameByteSize;
30943
31038
  this.fileOffset += frameByteSize;
@@ -30963,18 +31058,18 @@ function spectrogram(outputPath, options) {
30963
31058
 
30964
31059
  // src/targets/waveform/utils/minmax.ts
30965
31060
  function updateMinMax(samples, frame, channels, min, max) {
30966
- for (let ch = 0; ch < channels; ch++) {
30967
- const sample = samples[ch]?.[frame] ?? 0;
30968
- const currentMin = min[ch];
30969
- const currentMax = max[ch];
30970
- if (currentMin !== void 0 && sample < currentMin) min[ch] = sample;
30971
- 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;
30972
31067
  }
30973
31068
  }
30974
31069
  function writeMinMaxPoint(min, max, channels, target, offset) {
30975
- for (let ch = 0; ch < channels; ch++) {
30976
- target.writeFloatLE(min[ch] ?? 0, offset + ch * 8);
30977
- 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);
30978
31073
  }
30979
31074
  }
30980
31075
 
@@ -31221,7 +31316,7 @@ function bitDepthToPcmFormat(bitDepth) {
31221
31316
  }
31222
31317
  var encodingSchema = external_exports.object({
31223
31318
  format: external_exports.enum(["wav", "flac", "mp3", "aac"]),
31224
- 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."),
31225
31320
  vbr: external_exports.number().min(0).max(9).optional(),
31226
31321
  sampleRate: external_exports.number().int().positive().optional().describe("Output sample rate (Hz). When set, ffmpeg resamples on encode.")
31227
31322
  });
@@ -31339,8 +31434,8 @@ var WriteStream = class extends BufferedTargetStream {
31339
31434
  const buffer = Buffer.alloc(frames * channels * bytesPerSample);
31340
31435
  let offset = 0;
31341
31436
  for (let frame = 0; frame < frames; frame++) {
31342
- for (let ch = 0; ch < channels; ch++) {
31343
- const sample = chunk.samples[ch]?.[frame] ?? 0;
31437
+ for (let channel = 0; channel < channels; channel++) {
31438
+ const sample = chunk.samples[channel]?.[frame] ?? 0;
31344
31439
  offset = writeSample(buffer, offset, sample, this.properties.bitDepth);
31345
31440
  }
31346
31441
  }
@@ -31358,11 +31453,11 @@ var WriteStream = class extends BufferedTargetStream {
31358
31453
  if (encoding.vbr !== void 0) {
31359
31454
  args.push("-q:a", String(encoding.vbr));
31360
31455
  } else {
31361
- args.push("-b:a", encoding.bitrate ?? "192k");
31456
+ args.push("-b:a", `${encoding.bitrate ?? 192}k`);
31362
31457
  }
31363
31458
  break;
31364
31459
  case "aac":
31365
- args.push("-codec:a", "aac", "-b:a", encoding.bitrate ?? "192k");
31460
+ args.push("-codec:a", "aac", "-b:a", `${encoding.bitrate ?? 192}k`);
31366
31461
  break;
31367
31462
  }
31368
31463
  if (encoding.sampleRate !== void 0) {
@@ -31443,8 +31538,8 @@ var CutStream = class extends UnbufferedTransformStream {
31443
31538
  }
31444
31539
  const channels = chunk.samples.length;
31445
31540
  const output = [];
31446
- for (let ch = 0; ch < channels; ch++) {
31447
- const channel = chunk.samples[ch];
31541
+ for (let channelIndex = 0; channelIndex < channels; channelIndex++) {
31542
+ const channel = chunk.samples[channelIndex];
31448
31543
  if (!channel) {
31449
31544
  output.push(new Float32Array(totalKept));
31450
31545
  continue;
@@ -31496,18 +31591,18 @@ var DitherStream = class extends UnbufferedTransformStream {
31496
31591
  while (this.lastError.length < chunk.samples.length) {
31497
31592
  this.lastError.push(0);
31498
31593
  }
31499
- const samples = chunk.samples.map((channel, ch) => {
31594
+ const samples = chunk.samples.map((channel, channelIndex) => {
31500
31595
  const output = new Float32Array(channel.length);
31501
31596
  for (let index = 0; index < channel.length; index++) {
31502
31597
  const sample = channel[index] ?? 0;
31503
31598
  const tpdfNoise = (Math.random() - Math.random()) * lsb;
31504
31599
  let dithered = sample + tpdfNoise;
31505
31600
  if (noiseShaping) {
31506
- dithered += this.lastError[ch] ?? 0;
31601
+ dithered += this.lastError[channelIndex] ?? 0;
31507
31602
  }
31508
31603
  const quantized = quantizeSample(dithered, levels);
31509
31604
  if (noiseShaping) {
31510
- this.lastError[ch] = dithered - quantized;
31605
+ this.lastError[channelIndex] = dithered - quantized;
31511
31606
  }
31512
31607
  output[index] = quantized;
31513
31608
  }
@@ -31715,9 +31810,9 @@ var PhaseStream = class extends UnbufferedTransformStream {
31715
31810
  while (this.allpassState.length < chunk.samples.length) {
31716
31811
  this.allpassState.push(0);
31717
31812
  }
31718
- const samples = chunk.samples.map((channel, ch) => {
31719
- const { output, state } = applyAllpass(channel, coefficient, this.allpassState[ch] ?? 0);
31720
- 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;
31721
31816
  return output;
31722
31817
  });
31723
31818
  return { samples, offset: chunk.offset, sampleRate: chunk.sampleRate, bitDepth: chunk.bitDepth };
@@ -31832,9 +31927,9 @@ var SpliceStream = class extends UnbufferedTransformStream {
31832
31927
  const { samples, sampleRate } = await readWavSamples(this.properties.insertPath);
31833
31928
  const targetChannels = this.properties.channels;
31834
31929
  if (targetChannels) {
31835
- for (const ch of targetChannels) {
31836
- if (ch < 0) {
31837
- 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`);
31838
31933
  }
31839
31934
  }
31840
31935
  }
@@ -31867,9 +31962,9 @@ var SpliceStream = class extends UnbufferedTransformStream {
31867
31962
  applyInsert(channelSamples, insertChannel, overlap);
31868
31963
  }
31869
31964
  } else {
31870
- for (let ch = 0; ch < samples.length; ch++) {
31871
- const channelSamples = samples[ch];
31872
- 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];
31873
31968
  if (!channelSamples || !insertChannel) continue;
31874
31969
  applyInsert(channelSamples, insertChannel, overlap);
31875
31970
  }
@@ -31994,8 +32089,8 @@ function downmixToMono(samples) {
31994
32089
  const mono = new Float32Array(frames);
31995
32090
  if (channels === 0) return mono;
31996
32091
  const scale = 1 / channels;
31997
- for (let ch = 0; ch < channels; ch++) {
31998
- const channel = samples[ch] ?? new Float32Array(0);
32092
+ for (let channelIndex = 0; channelIndex < channels; channelIndex++) {
32093
+ const channel = samples[channelIndex] ?? new Float32Array(0);
31999
32094
  for (let index = 0; index < frames; index++) {
32000
32095
  mono[index] = (mono[index] ?? 0) + (channel[index] ?? 0) * scale;
32001
32096
  }
@@ -32039,7 +32134,7 @@ var DuplicateChannelsStream = class extends UnbufferedTransformStream {
32039
32134
  const source = chunk.samples[0] ?? new Float32Array(0);
32040
32135
  const outputCount = this.properties.channels;
32041
32136
  const samples = [];
32042
- for (let ch = 0; ch < outputCount; ch++) {
32137
+ for (let channel = 0; channel < outputCount; channel++) {
32043
32138
  samples.push(Float32Array.from(source));
32044
32139
  }
32045
32140
  yield { samples, offset: chunk.offset, sampleRate: chunk.sampleRate, bitDepth: chunk.bitDepth };
@@ -32149,15 +32244,11 @@ var STDERR_CAP_BYTES = 64 * 1024;
32149
32244
  function spawnFfmpegChild(options) {
32150
32245
  const child = spawn(options.ffmpegPath, [...options.args], { stdio: ["pipe", "pipe", "pipe"] });
32151
32246
  child.stderr.on("data", options.onStderr);
32152
- child.stdout.on("data", options.onStdout);
32153
32247
  child.stdin.on("error", options.onStdinError);
32154
32248
  const exitPromise = new Promise((resolve) => {
32155
32249
  child.once("exit", (code, signal) => resolve({ code, signal }));
32156
32250
  });
32157
- const stdoutEndPromise = new Promise((resolve) => {
32158
- child.stdout.once("end", () => resolve());
32159
- });
32160
- return { child, exitPromise, stdoutEndPromise };
32251
+ return { child, exitPromise };
32161
32252
  }
32162
32253
  function buildInputArgs(sampleRate, channels) {
32163
32254
  return ["-f", "f32le", "-ar", String(sampleRate), "-ac", String(channels), "-i", "pipe:0"];
@@ -32192,14 +32283,14 @@ function parseStdoutFrames(stash, bytes, channels, offset, sampleRate) {
32192
32283
  floatView = new Float32Array(aligned.buffer, aligned.byteOffset, totalFloats);
32193
32284
  }
32194
32285
  const samples = [];
32195
- for (let ch = 0; ch < channels; ch++) {
32286
+ for (let channel = 0; channel < channels; channel++) {
32196
32287
  samples.push(new Float32Array(frameCount));
32197
32288
  }
32198
32289
  for (let frame = 0; frame < frameCount; frame++) {
32199
- for (let ch = 0; ch < channels; ch++) {
32200
- const channelArray = samples[ch];
32290
+ for (let channel = 0; channel < channels; channel++) {
32291
+ const channelArray = samples[channel];
32201
32292
  if (channelArray) {
32202
- channelArray[frame] = floatView[frame * channels + ch] ?? 0;
32293
+ channelArray[frame] = floatView[frame * channels + channel] ?? 0;
32203
32294
  }
32204
32295
  }
32205
32296
  }
@@ -32222,15 +32313,16 @@ var TEARDOWN_KILL_GRACE_MS2 = 2e3;
32222
32313
  var FfmpegStream = class extends UnbufferedTransformStream {
32223
32314
  constructor() {
32224
32315
  super(...arguments);
32225
- this.pending = [];
32226
32316
  this.stdoutStash = Buffer.alloc(0);
32227
32317
  this.outputOffset = 0;
32228
32318
  this.stderr = "";
32319
+ this.stdoutEnded = false;
32229
32320
  this.inputSampleRate = 0;
32230
32321
  this.inputChannels = 0;
32231
32322
  }
32232
32323
  _setup(context) {
32233
32324
  this.streamContext = context;
32325
+ if (this.properties.outputSampleRate !== void 0) context.sampleRate = this.properties.outputSampleRate;
32234
32326
  }
32235
32327
  _buildArgs(context) {
32236
32328
  const { args } = this.properties;
@@ -32243,29 +32335,72 @@ var FfmpegStream = class extends UnbufferedTransformStream {
32243
32335
  this.inputChannels = channels;
32244
32336
  const outRate = this.properties.outputSampleRate ?? sampleRate;
32245
32337
  const args = [...buildInputArgs(sampleRate, channels), ...this._buildArgs(this.streamContext), ...buildOutputArgs(outRate, channels)];
32246
- const { child, exitPromise, stdoutEndPromise } = spawnFfmpegChild({
32338
+ const { child, exitPromise } = spawnFfmpegChild({
32247
32339
  ffmpegPath: this.properties.ffmpegPath,
32248
32340
  args,
32249
32341
  onStderr: (chunk) => {
32250
32342
  this.stderr = appendStderr(this.stderr, chunk);
32251
32343
  },
32252
- onStdout: (bytes) => this.handleStdoutBytes(bytes),
32253
32344
  onStdinError: (error50) => {
32254
32345
  if (error50.code === "EPIPE") return;
32255
32346
  this.stdinError ?? (this.stdinError = new Error(`ffmpeg stdin error: ${error50.message}`));
32256
32347
  }
32257
32348
  });
32349
+ child.stdout.on("readable", () => this.wakeStdout());
32350
+ child.stdout.on("end", () => {
32351
+ this.stdoutEnded = true;
32352
+ this.wakeStdout();
32353
+ });
32258
32354
  this.child = child;
32259
32355
  this.exitPromise = exitPromise;
32260
- this.stdoutEndPromise = stdoutEndPromise;
32261
32356
  }
32262
- 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;
32263
32366
  const outRate = this.properties.outputSampleRate ?? this.inputSampleRate;
32264
- const { block, stash, frameCount } = parseStdoutFrames(this.stdoutStash, bytes, this.inputChannels, this.outputOffset, outRate);
32265
- this.stdoutStash = stash;
32266
- if (!block) return;
32267
- this.pending.push(block);
32268
- 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
+ }
32269
32404
  }
32270
32405
  async *_transform(block) {
32271
32406
  if (this.stdinError) throw this.stdinError;
@@ -32278,11 +32413,8 @@ var FfmpegStream = class extends UnbufferedTransformStream {
32278
32413
  const child = this.child;
32279
32414
  if (!child) throw new Error("FfmpegStream.child not initialized");
32280
32415
  const interleaved = interleave(block.samples, frames, channels);
32281
- const buf = Buffer.from(interleaved.buffer, interleaved.byteOffset, interleaved.byteLength);
32282
- if (this.pendingDrain) {
32283
- await this.pendingDrain;
32284
- }
32285
- const ok = child.stdin.write(buf);
32416
+ const interleavedBuffer = Buffer.from(interleaved.buffer, interleaved.byteOffset, interleaved.byteLength);
32417
+ const ok = child.stdin.write(interleavedBuffer);
32286
32418
  if (!ok) {
32287
32419
  this.pendingDrain = new Promise((resolve) => {
32288
32420
  child.stdin.once("drain", () => {
@@ -32291,27 +32423,31 @@ var FfmpegStream = class extends UnbufferedTransformStream {
32291
32423
  });
32292
32424
  });
32293
32425
  }
32294
- yield* this.pending.splice(0);
32426
+ yield* this.serveWhileParked();
32427
+ yield* this.readAvailableStdout();
32295
32428
  }
32296
32429
  async *_flush() {
32297
32430
  const child = this.child;
32298
32431
  if (!child) return;
32299
- if (this.pendingDrain) {
32300
- await this.pendingDrain;
32301
- }
32432
+ yield* this.serveWhileParked();
32302
32433
  child.stdin.end();
32303
32434
  if (this.stdinError) throw this.stdinError;
32304
- const stdoutEnd = this.stdoutEndPromise ?? Promise.resolve();
32305
- const exit = this.exitPromise ?? Promise.resolve({ code: 0, signal: null });
32306
- 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 }));
32307
32447
  if (exitResult.code !== null && exitResult.code !== 0) {
32308
32448
  const detail = this.stderr ? `: ${this.stderr.slice(0, 1024)}` : "";
32309
32449
  throw new Error(`ffmpeg exited ${exitResult.code}${detail}`);
32310
32450
  }
32311
- if (this.stdoutStash.length >= this.inputChannels * 4) {
32312
- this.handleStdoutBytes(Buffer.alloc(0));
32313
- }
32314
- yield* this.pending.splice(0);
32315
32451
  }
32316
32452
  async _destroy() {
32317
32453
  const child = this.child;
@@ -32407,8 +32543,9 @@ function windowSamplesFromMs(smoothingMs, sampleRate) {
32407
32543
  return Math.max(1, Math.round(smoothingMs * sampleRate / 1e3));
32408
32544
  }
32409
32545
  async function applyBackwardPassOverChunkBuffer(args) {
32410
- const { sourceBuffer, destBuffer, iir, chunkSize, minHeldBuffer } = args;
32546
+ const { sourceBuffer, destBuffer, iir, chunkSize, minHeldBuffer, progress } = args;
32411
32547
  const totalFrames = sourceBuffer.frames;
32548
+ const totalWork = totalFrames * 2;
32412
32549
  if (totalFrames === 0) return;
32413
32550
  if (chunkSize <= 0) {
32414
32551
  throw new Error(`applyBackwardPassOverChunkBuffer: chunkSize must be > 0 (got ${chunkSize})`);
@@ -32424,6 +32561,7 @@ async function applyBackwardPassOverChunkBuffer(args) {
32424
32561
  try {
32425
32562
  const backwardState = { value: 0 };
32426
32563
  let seeded = false;
32564
+ let filteredFrames = 0;
32427
32565
  const sourceReader = await sourceBuffer.openReverseReader();
32428
32566
  try {
32429
32567
  for (; ; ) {
@@ -32436,12 +32574,15 @@ async function applyBackwardPassOverChunkBuffer(args) {
32436
32574
  }
32437
32575
  const filtered = iir.applyForwardPass(reversed, backwardState);
32438
32576
  await filteredReversed.write([filtered], sr, bd);
32577
+ filteredFrames += reversed.length;
32578
+ progress?.(filteredFrames, totalWork);
32439
32579
  }
32440
32580
  } finally {
32441
32581
  await sourceReader.close();
32442
32582
  }
32443
32583
  if (minHeldBuffer !== void 0) await minHeldBuffer.reset();
32444
32584
  const filteredReader = await filteredReversed.openReverseReader();
32585
+ let restoredFrames = 0;
32445
32586
  try {
32446
32587
  for (; ; ) {
32447
32588
  const chunk = await filteredReader.read(chunkSize);
@@ -32463,6 +32604,8 @@ async function applyBackwardPassOverChunkBuffer(args) {
32463
32604
  }
32464
32605
  }
32465
32606
  await destBuffer.write([forwardOrder], sr, bd);
32607
+ restoredFrames += stripeFrames;
32608
+ progress?.(totalFrames + restoredFrames, totalWork);
32466
32609
  }
32467
32610
  } finally {
32468
32611
  await filteredReader.close();
@@ -32621,7 +32764,8 @@ async function iterateForTargets(args) {
32621
32764
  tolerance = DEFAULT_TOLERANCE,
32622
32765
  peakTolerance,
32623
32766
  seedB,
32624
- onAttempt
32767
+ onAttempt,
32768
+ progress
32625
32769
  } = args;
32626
32770
  const channelCount = buffer.channels;
32627
32771
  const frames = buffer.frames;
@@ -32683,7 +32827,10 @@ async function iterateForTargets(args) {
32683
32827
  let winnerLufsErr = Infinity;
32684
32828
  let winnerPeakErr = Infinity;
32685
32829
  let previousStepMagnitude = Infinity;
32830
+ const attemptWork = frames * 4;
32831
+ const totalWork = maxAttempts * attemptWork;
32686
32832
  for (let attemptIdx = 0; attemptIdx < maxAttempts; attemptIdx++) {
32833
+ const attemptBase = attemptIdx * attemptWork;
32687
32834
  const tAttempt0 = Date.now();
32688
32835
  const anchors = {
32689
32836
  floorDb: anchorBase.floorDb,
@@ -32698,16 +32845,24 @@ async function iterateForTargets(args) {
32698
32845
  iir,
32699
32846
  halfWidth,
32700
32847
  forwardEnvelopeBuffer,
32701
- minHeldEnvelopeBuffer
32848
+ minHeldEnvelopeBuffer,
32849
+ progress: (done) => progress?.(attemptBase + done, totalWork)
32702
32850
  });
32703
32851
  await applyBackwardPassOverChunkBuffer({
32704
32852
  sourceBuffer: forwardEnvelopeBuffer,
32705
32853
  destBuffer: activeRef,
32706
32854
  iir,
32707
32855
  chunkSize: CHUNK_FRAMES3,
32708
- 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)
32709
32865
  });
32710
- const measured = await measureAttemptOutput({ source: buffer, sampleRate, channelCount, gSmoothed: activeRef });
32711
32866
  const lufsErr = measured.outputLufs - targetLufs;
32712
32867
  const peakErr = measured.outputTruePeakDb - effectiveTargetTp;
32713
32868
  const attempt = {
@@ -32788,7 +32943,7 @@ async function iterateForTargets(args) {
32788
32943
  }
32789
32944
  }
32790
32945
  async function streamCurveAndForwardIir(args) {
32791
- const { detectionEnvelope, anchors, iir, halfWidth, forwardEnvelopeBuffer, minHeldEnvelopeBuffer } = args;
32946
+ const { detectionEnvelope, anchors, iir, halfWidth, forwardEnvelopeBuffer, minHeldEnvelopeBuffer, progress } = args;
32792
32947
  const totalFrames = detectionEnvelope.frames;
32793
32948
  if (totalFrames === 0) return;
32794
32949
  await detectionEnvelope.reset();
@@ -32823,13 +32978,14 @@ async function streamCurveAndForwardIir(args) {
32823
32978
  await forwardEnvelopeBuffer.write([forwardChunk], detectionSampleRate, detectionBitDepth);
32824
32979
  await minHeldEnvelopeBuffer.write([minHeldChunk], detectionSampleRate, detectionBitDepth);
32825
32980
  }
32981
+ progress?.(consumedFrames, totalFrames);
32826
32982
  if (chunkLength < CHUNK_FRAMES3) break;
32827
32983
  }
32828
32984
  await forwardEnvelopeBuffer.flushWrites();
32829
32985
  await minHeldEnvelopeBuffer.flushWrites();
32830
32986
  }
32831
32987
  async function measureAttemptOutput(args) {
32832
- const { source, sampleRate, channelCount, gSmoothed } = args;
32988
+ const { source, sampleRate, channelCount, gSmoothed, progress } = args;
32833
32989
  const accumulator = new LoudnessAccumulator(sampleRate, channelCount);
32834
32990
  const truePeakAccumulator = new TruePeakAccumulator(sampleRate, channelCount);
32835
32991
  const applyOutputScratch = [];
@@ -32838,6 +32994,8 @@ async function measureAttemptOutput(args) {
32838
32994
  }
32839
32995
  await source.reset();
32840
32996
  await gSmoothed.reset();
32997
+ const totalFrames = source.frames;
32998
+ let framesDone = 0;
32841
32999
  for (; ; ) {
32842
33000
  const sourceChunk = await source.read(CHUNK_FRAMES3);
32843
33001
  const chunkFrames = sourceChunk.samples[0]?.length ?? 0;
@@ -32859,6 +33017,8 @@ async function measureAttemptOutput(args) {
32859
33017
  });
32860
33018
  accumulator.push(transformed, chunkFrames);
32861
33019
  truePeakAccumulator.push(transformed, chunkFrames);
33020
+ framesDone += chunkFrames;
33021
+ progress?.(framesDone, totalFrames);
32862
33022
  if (chunkFrames < CHUNK_FRAMES3) break;
32863
33023
  }
32864
33024
  const result = accumulator.finalize();
@@ -33314,7 +33474,8 @@ var LoudnessTargetStream = class extends BufferedTransformStream {
33314
33474
  tolerance
33315
33475
  });
33316
33476
  const tIterate0 = Date.now();
33317
- const attemptGate = createProgressGate(maxAttempts);
33477
+ const totalWork = maxAttempts * buffer.frames * 4;
33478
+ const progressGate = createProgressGate(totalWork);
33318
33479
  const result = await iterateForTargets({
33319
33480
  buffer,
33320
33481
  sampleRate,
@@ -33331,6 +33492,9 @@ var LoudnessTargetStream = class extends BufferedTransformStream {
33331
33492
  peakTolerance,
33332
33493
  seedB,
33333
33494
  detectionEnvelope,
33495
+ progress: (done, total) => {
33496
+ if (progressGate(done, Date.now())) this.emitProgress("process", done, total);
33497
+ },
33334
33498
  onAttempt: (attempt, attemptIndex) => {
33335
33499
  this.log("attempt", {
33336
33500
  attempt: attemptIndex + 1,
@@ -33341,7 +33505,6 @@ var LoudnessTargetStream = class extends BufferedTransformStream {
33341
33505
  outputLra: attempt.outputLra,
33342
33506
  elapsedMs: attempt.elapsedMs
33343
33507
  });
33344
- if (attemptGate(attemptIndex + 1, Date.now())) this.emitProgress("process", attemptIndex + 1, maxAttempts);
33345
33508
  }
33346
33509
  });
33347
33510
  this.learnTimingMs.iteration = Date.now() - tIterate0;
@@ -34086,10 +34249,10 @@ async function streamLatticeTrajectory(buffer, frameSize, hopSize, backend, addo
34086
34249
  for (let index = 0; index < got; index++) {
34087
34250
  let sample = 0;
34088
34251
  const ringPos = consumed % frameSize;
34089
- for (let ch = 0; ch < channelCount; ch++) {
34090
- const value = chunk.samples[ch]?.[index] ?? 0;
34252
+ for (let channel = 0; channel < channelCount; channel++) {
34253
+ const value = chunk.samples[channel]?.[index] ?? 0;
34091
34254
  sample = Math.fround(sample + value);
34092
- const channelRing = channelRings[ch];
34255
+ const channelRing = channelRings[channel];
34093
34256
  if (channelRing) channelRing[ringPos] = value;
34094
34257
  }
34095
34258
  sumRing[ringPos] = sample;
@@ -34097,9 +34260,9 @@ async function streamLatticeTrajectory(buffer, frameSize, hopSize, backend, addo
34097
34260
  while (nextFrame < frameCount && consumed >= nextFrame * hopSize + frameSize) {
34098
34261
  const start = nextFrame * hopSize;
34099
34262
  for (let pos = 0; pos < frameSize; pos++) window2[pos] = sumRing[(start + pos) % frameSize] ?? 0;
34100
- for (let ch = 0; ch < channelCount; ch++) {
34101
- const channelRing = channelRings[ch];
34102
- const channelWindow = channelWindows[ch];
34263
+ for (let channel = 0; channel < channelCount; channel++) {
34264
+ const channelRing = channelRings[channel];
34265
+ const channelWindow = channelWindows[channel];
34103
34266
  if (!channelRing || !channelWindow) continue;
34104
34267
  for (let pos = 0; pos < frameSize; pos++) channelWindow[pos] = channelRing[(start + pos) % frameSize] ?? 0;
34105
34268
  }
@@ -34186,10 +34349,10 @@ var _LatticeApplyState = class _LatticeApplyState {
34186
34349
  const fraction = framePos - frame0;
34187
34350
  const row0 = rows[frame0] ?? this.trajectory.identity;
34188
34351
  const row1 = rows[frame1] ?? this.trajectory.identity;
34189
- for (let ch = 0; ch < channelCount; ch++) {
34190
- const inputValue = channels[ch]?.[index] ?? 0;
34191
- const outChannel = out[ch];
34192
- 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);
34193
34356
  let signalValue = inputValue;
34194
34357
  for (let section = 0; section < order; section++) {
34195
34358
  const interpolated = (row0[section] ?? 0) + fraction * ((row1[section] ?? 0) - (row0[section] ?? 0));
@@ -34282,7 +34445,7 @@ var CrestReduceStream = class extends BufferedTransformStream {
34282
34445
  }
34283
34446
  applyState ?? (applyState = new LatticeApplyState(smoothedTrajectory, order, hopSize, blockChannelCount));
34284
34447
  const transformed = applyState.apply(block.samples, frames);
34285
- const samples = block.samples.map((inputChannel, ch) => transformed[ch] ?? inputChannel);
34448
+ const samples = block.samples.map((inputChannel, channelIndex) => transformed[channelIndex] ?? inputChannel);
34286
34449
  yield { samples, offset: block.offset, sampleRate: block.sampleRate, bitDepth: block.bitDepth };
34287
34450
  appliedFrames += frames;
34288
34451
  const doneFrames = Math.min(appliedFrames, totalFrames);
@@ -34749,13 +34912,13 @@ var WindowReader = class {
34749
34912
  this.channels = channels;
34750
34913
  this.windowSamples = windowSamples;
34751
34914
  this.scratch = [];
34752
- 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));
34753
34916
  }
34754
34917
  getScratch() {
34755
34918
  return this.scratch;
34756
34919
  }
34757
34920
  async preload(buffer, edgePadSamples) {
34758
- 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);
34759
34922
  this.virtualCursor = 0;
34760
34923
  this.bufferDrained = false;
34761
34924
  const headPad = Math.min(edgePadSamples, this.windowSamples);
@@ -34766,8 +34929,8 @@ var WindowReader = class {
34766
34929
  async advance(buffer, step) {
34767
34930
  if (step <= 0) return;
34768
34931
  const keep = this.windowSamples - step;
34769
- for (let ch = 0; ch < this.channels; ch++) {
34770
- const view = this.scratch[ch];
34932
+ for (let channelIndex = 0; channelIndex < this.channels; channelIndex++) {
34933
+ const view = this.scratch[channelIndex];
34771
34934
  if (keep > 0) view.copyWithin(0, step, this.windowSamples);
34772
34935
  view.fill(0, keep, this.windowSamples);
34773
34936
  }
@@ -34785,10 +34948,10 @@ var WindowReader = class {
34785
34948
  this.bufferDrained = true;
34786
34949
  return;
34787
34950
  }
34788
- for (let ch = 0; ch < this.channels; ch++) {
34789
- const src = chunk.samples[ch];
34790
- const dest = this.scratch[ch];
34791
- 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);
34792
34955
  }
34793
34956
  outOffset += chunkFrames;
34794
34957
  remaining -= chunkFrames;
@@ -34950,8 +35113,8 @@ async function readSequentialPadded(chunkBuffer, channelIndex, frames, out, hopS
34950
35113
  const chunk = await chunkBuffer.read(toRead);
34951
35114
  const chunkFrames = chunk.samples[0]?.length ?? 0;
34952
35115
  if (chunkFrames === 0) return;
34953
- const src = chunk.samples[channelIndex];
34954
- 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);
34955
35118
  written += chunkFrames;
34956
35119
  toRead -= chunkFrames;
34957
35120
  }
@@ -35033,7 +35196,7 @@ var DeBleedStream = class extends BufferedTransformStream {
35033
35196
  const targetPaddeds = Array.from({ length: channels }, () => new Float32Array(warmupFrames * hopSize + (fftSize - hopSize)));
35034
35197
  const refPaddeds = Array.from({ length: refCount }, () => new Float32Array(warmupFrames * hopSize + (fftSize - hopSize)));
35035
35198
  await buffer.reset();
35036
- for (let ch = 0; ch < channels; ch++) targetPaddeds[ch].fill(0);
35199
+ for (let channelIndex = 0; channelIndex < channels; channelIndex++) targetPaddeds[channelIndex].fill(0);
35037
35200
  const targetSamples = warmupFrames * hopSize + (fftSize - hopSize);
35038
35201
  let written = 0;
35039
35202
  let toRead = Math.min(targetSamples, buffer.frames);
@@ -35041,9 +35204,9 @@ var DeBleedStream = class extends BufferedTransformStream {
35041
35204
  const chunk = await buffer.read(toRead);
35042
35205
  const chunkFrames = chunk.samples[0]?.length ?? 0;
35043
35206
  if (chunkFrames === 0) break;
35044
- for (let ch = 0; ch < channels; ch++) {
35045
- const src = chunk.samples[ch];
35046
- 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);
35047
35210
  }
35048
35211
  written += chunkFrames;
35049
35212
  toRead -= chunkFrames;
@@ -35054,13 +35217,13 @@ var DeBleedStream = class extends BufferedTransformStream {
35054
35217
  }
35055
35218
  const targetStftOutputs = Array.from({ length: channels }, () => allocateStftOutput(warmupFrames, numBins));
35056
35219
  const refStftOutputs = Array.from({ length: refCount }, () => allocateStftOutput(warmupFrames, numBins));
35057
- 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));
35058
35221
  const refStfts = refPaddeds.map((padded, refIndex) => stft(padded, fftSize, hopSize, refStftOutputs[refIndex], this.fftBackend, this.fftAddonOptions));
35059
35222
  const maxRefPows = refStfts.map((refStft) => findMaxRefPower(refStft.real, refStft.imag, refStft.frames, numBins));
35060
35223
  const weightEpsilons = maxRefPows.map((maxPow) => 1e-10 * (maxPow + 1e-20));
35061
35224
  const seedsByChannel = [];
35062
- for (let ch = 0; ch < channels; ch++) {
35063
- const targetStft = targetStfts[ch];
35225
+ for (let channelIndex = 0; channelIndex < channels; channelIndex++) {
35226
+ const targetStft = targetStfts[channelIndex];
35064
35227
  const accumulators = refStfts.map(() => createTransferAccumulator(numBins));
35065
35228
  for (let refIndex = 0; refIndex < refCount; refIndex++) {
35066
35229
  const refStft = refStfts[refIndex];
@@ -35160,8 +35323,8 @@ var DeBleedStream = class extends BufferedTransformStream {
35160
35323
  const _tstft = _profStart();
35161
35324
  const targetScratch = targetReader.getScratch();
35162
35325
  const targetStfts = [];
35163
- for (let ch = 0; ch < channels; ch++) {
35164
- 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);
35165
35328
  targetStfts.push(stftOut);
35166
35329
  }
35167
35330
  const refStftsForChunk = [];
@@ -35174,12 +35337,12 @@ var DeBleedStream = class extends BufferedTransformStream {
35174
35337
  const cleanedByChannel = [];
35175
35338
  const sHatRe = new Float32Array(numBins);
35176
35339
  const sHatIm = new Float32Array(numBins);
35177
- for (let ch = 0; ch < channels; ch++) {
35178
- const kalmanStates = kalmanStatesByCh[ch];
35179
- const interfererPsd = interfererPsdByCh[ch];
35180
- const msadChannelStates = msadChannelStatesByCh[ch];
35181
- const ispStates = ispStatesByCh[ch];
35182
- 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];
35183
35346
  for (let frame = 0; frame < winFrames; frame++) {
35184
35347
  const frameOffset = frame * numBins;
35185
35348
  const frameReal = targetStft.real.subarray(frameOffset, frameOffset + numBins);
@@ -35277,13 +35440,13 @@ var DeBleedStream = class extends BufferedTransformStream {
35277
35440
  if (!clip) continue;
35278
35441
  const { clipStart, sliceFromOffset, sliceLength } = clip;
35279
35442
  const writeSamplesByChannel = [];
35280
- for (let ch = 0; ch < channels; ch++) {
35281
- 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));
35282
35445
  }
35283
35446
  if (clipStart > outputBuffer.frames) {
35284
35447
  const padFrames = clipStart - outputBuffer.frames;
35285
35448
  const zeroSamples = [];
35286
- 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));
35287
35450
  const _twritePad = _profStart();
35288
35451
  await outputBuffer.write(zeroSamples, sampleRate, bitDepth);
35289
35452
  _profAdd("write", _twritePad);
@@ -35297,7 +35460,7 @@ var DeBleedStream = class extends BufferedTransformStream {
35297
35460
  if (outputBuffer.frames < totalFrames) {
35298
35461
  const padFrames = totalFrames - outputBuffer.frames;
35299
35462
  const zeroSamples = [];
35300
- 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));
35301
35464
  await outputBuffer.write(zeroSamples, sampleRate, bitDepth);
35302
35465
  }
35303
35466
  await outputBuffer.reset();
@@ -35379,6 +35542,31 @@ function createOnnxSession(addonPath, modelPath, options, log) {
35379
35542
  };
35380
35543
  }
35381
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
+
35382
35570
  // src/transforms/deep-filter-net-3/utils/dfn.ts
35383
35571
  var DFN3_SAMPLE_RATE = 48e3;
35384
35572
  var DFN3_HOP_SIZE = 480;
@@ -35431,9 +35619,8 @@ function processDfnBlock(dfnState, signal, session, attenLimDb) {
35431
35619
  var DFN3_BUFFER_SIZE = 100 * DFN3_HOP_SIZE;
35432
35620
  var schema23 = external_exports.object({
35433
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)"),
35434
- 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."),
35435
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"),
35436
- 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."),
35437
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")
35438
35625
  });
35439
35626
  var DeepFilterNet3Stream = class extends BufferedTransformStream {
@@ -35445,26 +35632,11 @@ var DeepFilterNet3Stream = class extends BufferedTransformStream {
35445
35632
  }
35446
35633
  _setup(context) {
35447
35634
  this.session = createOnnxSession(this.properties.onnxAddonPath, this.properties.modelPath, { executionProviders: ["cpu"] }, (message, data) => this.log(message, data));
35448
- const sourceRate = this.properties.sampleRate;
35449
- if (sourceRate === DFN3_SAMPLE_RATE) return;
35450
- this.upResample = new FfmpegStream(
35451
- ffmpeg({
35452
- ffmpegPath: this.properties.ffmpegPath,
35453
- args: ["-af", `aresample=${DFN3_SAMPLE_RATE}`],
35454
- outputSampleRate: DFN3_SAMPLE_RATE
35455
- }),
35456
- this.renderContext
35457
- );
35458
- this.downResample = new FfmpegStream(
35459
- ffmpeg({
35460
- ffmpegPath: this.properties.ffmpegPath,
35461
- args: ["-af", `aresample=${sourceRate}`],
35462
- outputSampleRate: sourceRate
35463
- }),
35464
- this.renderContext
35465
- );
35466
- this.upResample._setup(context);
35467
- 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
+ }
35468
35640
  }
35469
35641
  _pipe(input) {
35470
35642
  if (!this.upResample || !this.downResample) return super._pipe(input);
@@ -35472,9 +35644,6 @@ var DeepFilterNet3Stream = class extends BufferedTransformStream {
35472
35644
  }
35473
35645
  async *_transform(buffered) {
35474
35646
  if (!this.session) throw new Error("deep-filter-net-3: stream not set up");
35475
- if (this.sampleRate !== void 0 && this.sampleRate !== DFN3_SAMPLE_RATE) {
35476
- 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)`);
35477
- }
35478
35647
  const session = this.session;
35479
35648
  const frames = buffered.frames;
35480
35649
  const channels = buffered.channels;
@@ -35485,9 +35654,9 @@ var DeepFilterNet3Stream = class extends BufferedTransformStream {
35485
35654
  this.dfnStates.push(createDfnState());
35486
35655
  }
35487
35656
  const outputChannels = [];
35488
- for (let ch = 0; ch < channels; ch++) {
35489
- const channel = chunk.samples[ch];
35490
- 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];
35491
35660
  if (!channel || !dfnState) {
35492
35661
  outputChannels.push(new Float32Array(frames));
35493
35662
  continue;
@@ -35507,7 +35676,7 @@ var DeepFilterNet3Node = class extends TransformNode {
35507
35676
  };
35508
35677
  DeepFilterNet3Node.nodeName = "DeepFilterNet3 (Denoiser)";
35509
35678
  DeepFilterNet3Node.packageName = PACKAGE_NAME;
35510
- 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.";
35511
35680
  DeepFilterNet3Node.schema = schema23;
35512
35681
  DeepFilterNet3Node.Stream = DeepFilterNet3Stream;
35513
35682
  function deepFilterNet3(options) {
@@ -35633,15 +35802,14 @@ var DtlnBlockStream = class {
35633
35802
  // src/transforms/dtln/utils/pump.ts
35634
35803
  var DTLN_SAMPLE_RATE = 16e3;
35635
35804
  var CHUNK_FRAMES5 = 16e3;
35636
- var RESAMPLE_DRAIN_CHUNK = 16384;
35637
35805
  var STEP_BATCH_SIZE = 16e3;
35638
35806
  var WARMUP_SAMPLES = WARMUP_SHIFTS * BLOCK_SHIFT;
35639
35807
  function stepAllChannels(args) {
35640
35808
  const { channels, streams, inputs, stepBatch, stepBatchLen, batchSize, warmupRemaining } = args;
35641
35809
  const stepOutputs = [];
35642
- for (let ch = 0; ch < channels; ch++) {
35643
- const stream = streams[ch];
35644
- const input = inputs[ch];
35810
+ for (let channel = 0; channel < channels; channel++) {
35811
+ const stream = streams[channel];
35812
+ const input = inputs[channel];
35645
35813
  if (!stream || !input) {
35646
35814
  stepOutputs.push(new Float32Array(BLOCK_SHIFT));
35647
35815
  continue;
@@ -35669,11 +35837,11 @@ function appendToStepBatch(args) {
35669
35837
  const space = batchSize - batchLen;
35670
35838
  const copy = Math.min(space, length - offset);
35671
35839
  const firstSample = samples[0];
35672
- for (let ch = 0; ch < channels; ch++) {
35673
- const src = samples[ch] ?? firstSample;
35674
- const dest = stepBatch[ch];
35675
- if (!src || !dest) continue;
35676
- 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);
35677
35845
  }
35678
35846
  batchLen += copy;
35679
35847
  offset += copy;
@@ -35681,90 +35849,39 @@ function appendToStepBatch(args) {
35681
35849
  return { stepBatchLen: batchLen, warmupRemaining: warmupLeft };
35682
35850
  }
35683
35851
  async function commitStepBatch(args) {
35684
- const { stepBatch, length, channels, pair, output, sourceRate, bitDepth, originalFrames, writerState } = args;
35852
+ const { stepBatch, length, channels, output, sampleRate, bitDepth, originalFrames, writerState } = args;
35685
35853
  if (length === 0) return;
35686
35854
  const slices = [];
35687
- for (let ch = 0; ch < channels; ch++) {
35688
- const src = stepBatch[ch] ?? new Float32Array(length);
35689
- slices.push(src.subarray(0, length));
35690
- }
35691
- if (pair) {
35692
- await pair.resampleOut.write(slices);
35693
- } else {
35694
- const remaining = Math.max(0, originalFrames - writerState.written);
35695
- if (remaining > 0) {
35696
- const take = Math.min(length, remaining);
35697
- const writeChannels = take === length ? slices : slices.map((channel) => channel.subarray(0, take));
35698
- await output.write(writeChannels, sourceRate, bitDepth);
35699
- writerState.written += take;
35700
- }
35701
- }
35702
- }
35703
- async function drainResampleOutToBuffer(args) {
35704
- const { resampleOut, output, channels, sourceRate, bitDepth, originalFrames, writerState } = args;
35705
- for (; ; ) {
35706
- const chunk = await resampleOut.read(RESAMPLE_DRAIN_CHUNK);
35707
- const got = chunk[0]?.length ?? 0;
35708
- if (got === 0) return;
35709
- await commitResampledFrames({ chunk, channels, output, sourceRate, bitDepth, originalFrames, writerState });
35710
- }
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;
35711
35865
  }
35712
35866
  async function pullNextChunkAt16k(args) {
35713
- const { buffer, pair, channels, frames } = args;
35714
- if (!pair) {
35715
- const chunk = await buffer.read(frames);
35716
- const got2 = chunk.samples[0]?.length ?? 0;
35717
- if (got2 === 0) return void 0;
35718
- const out2 = [];
35719
- for (let ch = 0; ch < channels; ch++) {
35720
- out2.push(chunk.samples[ch] ?? chunk.samples[0] ?? new Float32Array(got2));
35721
- }
35722
- return out2;
35723
- }
35724
- const out = await pair.resampleIn.read(frames);
35725
- 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;
35726
35870
  if (got === 0) return void 0;
35727
- return out;
35728
- }
35729
- async function pumpSourceToResampleIn(args) {
35730
- const { buffer, resampleIn, channels, chunkFrames } = args;
35731
- for (; ; ) {
35732
- const sourceChunk = await buffer.read(chunkFrames);
35733
- const sourceFrames = sourceChunk.samples[0]?.length ?? 0;
35734
- if (sourceFrames === 0) break;
35735
- const sourceChannels = [];
35736
- for (let ch = 0; ch < channels; ch++) {
35737
- sourceChannels.push(sourceChunk.samples[ch] ?? sourceChunk.samples[0] ?? new Float32Array(sourceFrames));
35738
- }
35739
- await resampleIn.write(sourceChannels);
35740
- if (sourceFrames < chunkFrames) break;
35741
- }
35742
- await resampleIn.end();
35743
- }
35744
- async function commitResampledFrames(args) {
35745
- const { chunk, channels, output, sourceRate, bitDepth, originalFrames, writerState } = args;
35746
- const firstChannel = chunk[0];
35747
- const got = firstChannel?.length ?? 0;
35748
- if (got === 0 || !firstChannel) return;
35749
- const remaining = originalFrames - writerState.written;
35750
- if (remaining <= 0) return;
35751
- const take = Math.min(got, remaining);
35752
- const writeChannels = [];
35753
- for (let ch = 0; ch < channels; ch++) {
35754
- const src = chunk[ch] ?? firstChannel;
35755
- 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));
35756
35874
  }
35757
- await output.write(writeChannels, sourceRate, bitDepth);
35758
- writerState.written += take;
35875
+ return out;
35759
35876
  }
35760
- async function padTail(output, channels, originalFrames, written, sourceRate, bitDepth) {
35877
+ async function padTail(output, channels, originalFrames, written, sampleRate, bitDepth) {
35761
35878
  if (written >= originalFrames) return;
35762
35879
  const missing = originalFrames - written;
35763
35880
  const padChannels = [];
35764
35881
  for (let channel = 0; channel < Math.max(1, channels); channel++) {
35765
35882
  padChannels.push(new Float32Array(missing));
35766
35883
  }
35767
- await output.write(padChannels, sourceRate, bitDepth);
35884
+ await output.write(padChannels, sampleRate, bitDepth);
35768
35885
  }
35769
35886
 
35770
35887
  // src/transforms/dtln/index.ts
@@ -35777,9 +35894,10 @@ var schema24 = external_exports.object({
35777
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")
35778
35895
  });
35779
35896
  var DtlnStream = class extends BufferedTransformStream {
35780
- constructor() {
35781
- super(...arguments);
35897
+ constructor(node, context) {
35898
+ super(node, context);
35782
35899
  this.blockSize = WHOLE_FILE;
35900
+ this.renderContext = context;
35783
35901
  }
35784
35902
  _setup(context) {
35785
35903
  const onnxProviders = filterOnnxProviders(context.executionProviders);
@@ -35789,30 +35907,22 @@ var DtlnStream = class extends BufferedTransformStream {
35789
35907
  const fft2 = initFftBackend(cpuProviders.length > 0 ? cpuProviders : ["cpu"], this.properties);
35790
35908
  this.fftBackend = fft2.backend;
35791
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)));
35792
35919
  }
35793
35920
  async *_transform(buffered) {
35794
35921
  const originalFrames = buffered.frames;
35795
35922
  const channels = buffered.channels;
35796
35923
  if (originalFrames === 0 || channels === 0) return;
35797
- const sourceRate = this.sampleRate ?? DTLN_SAMPLE_RATE;
35798
35924
  const bitDepth = this.bitDepth;
35799
- const needsResample = sourceRate !== DTLN_SAMPLE_RATE;
35800
35925
  await buffered.reset();
35801
- let pair;
35802
- if (needsResample) {
35803
- pair = {
35804
- resampleIn: new ResampleStream(this.properties.ffmpegPath, {
35805
- sourceSampleRate: sourceRate,
35806
- targetSampleRate: DTLN_SAMPLE_RATE,
35807
- channels
35808
- }),
35809
- resampleOut: new ResampleStream(this.properties.ffmpegPath, {
35810
- sourceSampleRate: DTLN_SAMPLE_RATE,
35811
- targetSampleRate: sourceRate,
35812
- channels
35813
- })
35814
- };
35815
- }
35816
35926
  const output = new BlockBuffer();
35817
35927
  try {
35818
35928
  await this.runMainPass({
@@ -35820,40 +35930,32 @@ var DtlnStream = class extends BufferedTransformStream {
35820
35930
  output,
35821
35931
  channels,
35822
35932
  originalFrames,
35823
- sourceRate,
35824
- bitDepth,
35825
- pair
35933
+ bitDepth
35826
35934
  });
35827
35935
  await output.reset();
35828
35936
  yield* output.iterate(CHUNK_FRAMES5);
35829
35937
  } finally {
35830
- if (pair) {
35831
- await Promise.all([pair.resampleIn.close(), pair.resampleOut.close()]);
35832
- }
35833
35938
  await output.close();
35834
35939
  }
35835
35940
  }
35836
35941
  async runMainPass(args) {
35837
- const { buffer, output, channels, originalFrames, sourceRate, bitDepth, pair } = args;
35942
+ const { buffer, output, channels, originalFrames, bitDepth } = args;
35838
35943
  const streams = [];
35839
- for (let ch = 0; ch < channels; ch++) {
35944
+ for (let channel = 0; channel < channels; channel++) {
35840
35945
  streams.push(new DtlnBlockStream({ session1: this.session1, session2: this.session2, fftBackend: this.fftBackend, fftAddonOptions: this.fftAddonOptions }));
35841
35946
  }
35842
35947
  const stepAccum = [];
35843
- 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));
35844
35949
  let stepAccumLen = 0;
35845
35950
  const stepBatch = [];
35846
- 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));
35847
35952
  let stepBatchLen = 0;
35848
35953
  let samplesFed = 0;
35849
35954
  let warmupRemaining = WARMUP_SAMPLES;
35850
35955
  const writerState = { written: 0 };
35851
- const pumpDone = pair !== void 0 ? pumpSourceToResampleIn({ buffer, resampleIn: pair.resampleIn, channels, chunkFrames: CHUNK_FRAMES5 }) : Promise.resolve();
35852
- const drainerDone = pair !== void 0 ? drainResampleOutToBuffer({ resampleOut: pair.resampleOut, output, channels, sourceRate, bitDepth, originalFrames, writerState }) : Promise.resolve();
35853
- const total16k = Math.round(originalFrames * DTLN_SAMPLE_RATE / sourceRate);
35854
- const progressGate = createProgressGate(total16k);
35956
+ const progressGate = createProgressGate(originalFrames);
35855
35957
  for (; ; ) {
35856
- const got16k = await pullNextChunkAt16k({ buffer, pair, channels, frames: CHUNK_FRAMES5 });
35958
+ const got16k = await pullNextChunkAt16k({ buffer, channels, frames: CHUNK_FRAMES5 });
35857
35959
  if (got16k === void 0) break;
35858
35960
  const firstChannel = got16k[0];
35859
35961
  const chunkFrames = firstChannel?.length ?? 0;
@@ -35862,11 +35964,11 @@ var DtlnStream = class extends BufferedTransformStream {
35862
35964
  while (consumed < chunkFrames) {
35863
35965
  const need = BLOCK_SHIFT - stepAccumLen;
35864
35966
  const take = Math.min(need, chunkFrames - consumed);
35865
- for (let ch = 0; ch < channels; ch++) {
35866
- const src = got16k[ch] ?? firstChannel;
35867
- const dest = stepAccum[ch];
35868
- if (!src || !dest) continue;
35869
- 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);
35870
35972
  }
35871
35973
  stepAccumLen += take;
35872
35974
  consumed += take;
@@ -35877,50 +35979,45 @@ var DtlnStream = class extends BufferedTransformStream {
35877
35979
  samplesFed += BLOCK_SHIFT;
35878
35980
  stepAccumLen = 0;
35879
35981
  if (stepBatchLen >= STEP_BATCH_SIZE) {
35880
- 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 });
35881
35983
  stepBatchLen = 0;
35882
35984
  }
35883
35985
  }
35884
35986
  }
35885
- const doneFrames = Math.min(samplesFed, total16k);
35886
- 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);
35887
35989
  }
35888
- await pumpDone;
35889
35990
  if (samplesFed > 0 && samplesFed < BLOCK_LEN) {
35890
35991
  const zeroInputs = [];
35891
- 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));
35892
35993
  while (samplesFed < BLOCK_LEN) {
35893
35994
  const result = stepAllChannels({ channels, streams, inputs: zeroInputs, stepBatch, stepBatchLen, batchSize: STEP_BATCH_SIZE, warmupRemaining });
35894
35995
  stepBatchLen = result.stepBatchLen;
35895
35996
  warmupRemaining = result.warmupRemaining;
35896
35997
  samplesFed += BLOCK_SHIFT;
35897
35998
  if (stepBatchLen >= STEP_BATCH_SIZE) {
35898
- 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 });
35899
36000
  stepBatchLen = 0;
35900
36001
  }
35901
36002
  }
35902
36003
  }
35903
36004
  const flushOutputs = [];
35904
- 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));
35905
36006
  const flushLen = flushOutputs[0]?.length ?? 0;
35906
36007
  if (flushLen > 0) {
35907
36008
  const result = appendToStepBatch({ samples: flushOutputs, channels, stepBatch, stepBatchLen, batchSize: STEP_BATCH_SIZE, warmupRemaining });
35908
36009
  stepBatchLen = result.stepBatchLen;
35909
36010
  warmupRemaining = result.warmupRemaining;
35910
36011
  if (stepBatchLen >= STEP_BATCH_SIZE) {
35911
- 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 });
35912
36013
  stepBatchLen = 0;
35913
36014
  }
35914
36015
  }
35915
36016
  if (stepBatchLen > 0) {
35916
- 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 });
35917
36018
  stepBatchLen = 0;
35918
36019
  }
35919
- if (pair) {
35920
- await pair.resampleOut.end();
35921
- }
35922
- await drainerDone;
35923
- await padTail(output, channels, originalFrames, writerState.written, sourceRate, bitDepth);
36020
+ await padTail(output, channels, originalFrames, writerState.written, DTLN_SAMPLE_RATE, bitDepth);
35924
36021
  }
35925
36022
  };
35926
36023
  var DtlnNode = class extends TransformNode {
@@ -35997,12 +36094,12 @@ function computeIstftScaled(real, imag, outputLength) {
35997
36094
  // src/transforms/htdemucs/utils/stems.ts
35998
36095
  function buildModelInput(segLeft, segRight, stftLeft, stftRight, segmentLength, xBins, xFrames) {
35999
36096
  const xData = new Float32Array(4 * xBins * xFrames);
36000
- for (let ch = 0; ch < 2; ch++) {
36001
- const stftCh = ch === 0 ? stftLeft : stftRight;
36097
+ for (let channel = 0; channel < 2; channel++) {
36098
+ const stftCh = channel === 0 ? stftLeft : stftRight;
36002
36099
  for (let freq = 0; freq < xBins; freq++) {
36003
36100
  for (let frame = 0; frame < xFrames; frame++) {
36004
- const realIdx = 2 * ch * xBins * xFrames + freq * xFrames + frame;
36005
- 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;
36006
36103
  const srcFrame = frame + 2;
36007
36104
  xData[realIdx] = stftCh.real[srcFrame]?.[freq] ?? 0;
36008
36105
  xData[imagIdx] = stftCh.imag[srcFrame]?.[freq] ?? 0;
@@ -36037,8 +36134,8 @@ function mixStemsToStereo(stemAccum, sumWeight, stemGains, stats, nStable) {
36037
36134
  function extractStems(xtOut, xOut, workspace, stemOutputs, weight, segmentOffset, chunkLength, segmentLength) {
36038
36135
  const { freqRealBuffers, freqImagBuffers, nbFrames, stftLen, stftPad, pad: pad2, xBins, xFrames } = workspace;
36039
36136
  for (let source = 0; source < 4; source++) {
36040
- for (let ch = 0; ch < 2; ch++) {
36041
- const xtIndex = source * 2 * segmentLength + ch * segmentLength;
36137
+ for (let channel = 0; channel < 2; channel++) {
36138
+ const xtIndex = source * 2 * segmentLength + channel * segmentLength;
36042
36139
  for (let frame = 0; frame < nbFrames; frame++) {
36043
36140
  freqRealBuffers[frame]?.fill(0);
36044
36141
  freqImagBuffers[frame]?.fill(0);
@@ -36047,8 +36144,8 @@ function extractStems(xtOut, xOut, workspace, stemOutputs, weight, segmentOffset
36047
36144
  const baseOffset = source * 4 * xBins * xFrames;
36048
36145
  for (let freq = 0; freq < xBins; freq++) {
36049
36146
  for (let frame = 0; frame < xFrames; frame++) {
36050
- const realIdx = baseOffset + 2 * ch * xBins * xFrames + freq * xFrames + frame;
36051
- 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;
36052
36149
  const destFrame = frame + 2;
36053
36150
  const realArr = freqRealBuffers[destFrame];
36054
36151
  const imagArr = freqImagBuffers[destFrame];
@@ -36066,10 +36163,10 @@ function extractStems(xtOut, xOut, workspace, stemOutputs, weight, segmentOffset
36066
36163
  const freqVal = freqWaveform[freqOffset + index] ?? 0;
36067
36164
  const combined = timeVal + freqVal;
36068
36165
  const wt = weight[index] ?? 1;
36069
- const outIdx = source * 2 + ch;
36070
- const arr = stemOutputs[outIdx];
36071
- if (arr) {
36072
- 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;
36073
36170
  }
36074
36171
  }
36075
36172
  }
@@ -36091,41 +36188,33 @@ var SEGMENT_SAMPLES = 343980;
36091
36188
  var OVERLAP = 0.25;
36092
36189
  var TRANSITION_POWER = 1;
36093
36190
  var CHUNK_FRAMES6 = 44100;
36094
- var RESAMPLE_DRAIN_CHUNK2 = 16384;
36095
36191
  var STEM_OUTPUTS = 4 * 2;
36096
36192
  var HtdemucsStream = class extends BufferedTransformStream {
36097
- constructor() {
36098
- super(...arguments);
36193
+ constructor(node, context) {
36194
+ super(node, context);
36099
36195
  this.blockSize = WHOLE_FILE;
36196
+ this.renderContext = context;
36100
36197
  }
36101
- _setup(_context) {
36198
+ _setup(context) {
36102
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)));
36103
36209
  }
36104
36210
  async *_transform(buffered) {
36105
36211
  const originalFrames = buffered.frames;
36106
36212
  const channels = buffered.channels;
36107
36213
  if (originalFrames === 0 || channels === 0) return;
36108
- const sourceRate = this.sampleRate ?? HTDEMUCS_SAMPLE_RATE;
36109
36214
  const bitDepth = this.bitDepth;
36110
- const needsResample = sourceRate !== HTDEMUCS_SAMPLE_RATE;
36111
36215
  const stats = await computeStreamingStats(buffered, channels);
36112
36216
  this.log("streaming stats computed", { mean: stats.mean, std: stats.std });
36113
36217
  await buffered.reset();
36114
- let pair;
36115
- if (needsResample) {
36116
- pair = {
36117
- resampleIn: new ResampleStream(this.properties.ffmpegPath, {
36118
- sourceSampleRate: sourceRate,
36119
- targetSampleRate: HTDEMUCS_SAMPLE_RATE,
36120
- channels: 2
36121
- }),
36122
- resampleOut: new ResampleStream(this.properties.ffmpegPath, {
36123
- sourceSampleRate: HTDEMUCS_SAMPLE_RATE,
36124
- targetSampleRate: sourceRate,
36125
- channels: 2
36126
- })
36127
- };
36128
- }
36129
36218
  const output = new BlockBuffer();
36130
36219
  try {
36131
36220
  await this.runMainPass({
@@ -36133,26 +36222,19 @@ var HtdemucsStream = class extends BufferedTransformStream {
36133
36222
  output,
36134
36223
  channels,
36135
36224
  originalFrames,
36136
- sourceRate,
36137
36225
  bitDepth,
36138
- stats,
36139
- pair
36226
+ stats
36140
36227
  });
36141
36228
  await output.reset();
36142
36229
  yield* output.iterate(CHUNK_FRAMES6);
36143
36230
  } finally {
36144
- if (pair) {
36145
- await Promise.all([pair.resampleIn.close(), pair.resampleOut.close()]);
36146
- }
36147
36231
  await output.close();
36148
36232
  }
36149
36233
  }
36150
36234
  async runMainPass(args) {
36151
- const { buffer, output, channels, originalFrames, sourceRate, bitDepth, stats, pair } = args;
36235
+ const { buffer, output, channels, originalFrames, bitDepth, stats } = args;
36152
36236
  const stride = Math.round((1 - OVERLAP) * SEGMENT_SAMPLES);
36153
36237
  const writerState = { written: 0 };
36154
- const pumpDone = pair !== void 0 ? pumpSourceToResampleIn2({ buffer, resampleIn: pair.resampleIn, channels, chunkFrames: CHUNK_FRAMES6 }) : Promise.resolve();
36155
- const drainerDone = pair !== void 0 ? drainResampleOutToBuffer2({ resampleOut: pair.resampleOut, output, channels, sourceRate, bitDepth, originalFrames, writerState }) : Promise.resolve();
36156
36238
  const weight = buildTriangularWeight(SEGMENT_SAMPLES, TRANSITION_POWER);
36157
36239
  const pad2 = Math.floor(HOP_SIZE2 / 2) * 3;
36158
36240
  const le = Math.ceil(SEGMENT_SAMPLES / HOP_SIZE2);
@@ -36190,14 +36272,13 @@ var HtdemucsStream = class extends BufferedTransformStream {
36190
36272
  const { stems } = this.properties;
36191
36273
  const stemGains = [stems.drums, stems.bass, stems.other, stems.vocals];
36192
36274
  const inv = 1 / (stats.std || 1);
36193
- const modelRateFrames = Math.round(originalFrames * HTDEMUCS_SAMPLE_RATE / sourceRate);
36194
- const progressGate = createProgressGate(modelRateFrames);
36275
+ const progressGate = createProgressGate(originalFrames);
36195
36276
  let stableEmitted = 0;
36196
36277
  for (; ; ) {
36197
36278
  if (!inputExhausted) {
36198
36279
  while (segFilled < SEGMENT_SAMPLES) {
36199
36280
  const need = SEGMENT_SAMPLES - segFilled;
36200
- 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) });
36201
36282
  if (got === void 0 || got[0].length === 0) {
36202
36283
  inputExhausted = true;
36203
36284
  break;
@@ -36239,17 +36320,15 @@ var HtdemucsStream = class extends BufferedTransformStream {
36239
36320
  sumWeight,
36240
36321
  stats,
36241
36322
  stemGains,
36242
- pair,
36243
36323
  output,
36244
36324
  channels,
36245
- sourceRate,
36246
36325
  bitDepth,
36247
36326
  originalFrames,
36248
36327
  writerState
36249
36328
  });
36250
36329
  stableEmitted += nStable;
36251
- const doneFrames = Math.min(stableEmitted, modelRateFrames);
36252
- 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);
36253
36332
  if (!isFinalIter) {
36254
36333
  segLeft.copyWithin(0, nStable, SEGMENT_SAMPLES);
36255
36334
  segRight.copyWithin(0, nStable, SEGMENT_SAMPLES);
@@ -36260,35 +36339,26 @@ var HtdemucsStream = class extends BufferedTransformStream {
36260
36339
  break;
36261
36340
  }
36262
36341
  }
36263
- await pumpDone;
36264
- if (pair) {
36265
- await pair.resampleOut.end();
36266
- }
36267
- await drainerDone;
36268
- await padTail2(output, channels, originalFrames, writerState.written, sourceRate, bitDepth);
36342
+ await padTail2(output, channels, originalFrames, writerState.written, HTDEMUCS_SAMPLE_RATE, bitDepth);
36269
36343
  }
36270
36344
  async emitStable(args) {
36271
- 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;
36272
36346
  if (nStable <= 0) return;
36273
36347
  const { outLeft, outRight } = mixStemsToStereo(stemAccum, sumWeight, stemGains, stats, nStable);
36274
36348
  bandpass([outLeft, outRight], HTDEMUCS_SAMPLE_RATE, this.properties.highPass, this.properties.lowPass);
36275
- if (pair) {
36276
- await pair.resampleOut.write([outLeft, outRight]);
36277
- } else {
36278
- const writeChannels = buildWriteChannels(outLeft, outRight, channels);
36279
- const remaining = Math.max(0, originalFrames - writerState.written);
36280
- if (remaining > 0) {
36281
- const take = Math.min(nStable, remaining);
36282
- const sliced = take === nStable ? writeChannels : writeChannels.map((channel) => channel.subarray(0, take));
36283
- await output.write(sliced, sourceRate, bitDepth);
36284
- writerState.written += take;
36285
- }
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;
36286
36356
  }
36287
36357
  for (let stem = 0; stem < STEM_OUTPUTS; stem++) {
36288
- const arr = stemAccum[stem];
36289
- if (!arr) continue;
36290
- arr.copyWithin(0, nStable, SEGMENT_SAMPLES);
36291
- 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);
36292
36362
  }
36293
36363
  sumWeight.copyWithin(0, nStable, SEGMENT_SAMPLES);
36294
36364
  sumWeight.fill(0, SEGMENT_SAMPLES - nStable, SEGMENT_SAMPLES);
@@ -36344,59 +36414,14 @@ async function computeStreamingStats(buffer, channels) {
36344
36414
  return { mean, std };
36345
36415
  }
36346
36416
  async function pullNextChunkAt441(args) {
36347
- const { buffer, pair, channels, frames } = args;
36348
- if (!pair) {
36349
- const chunk = await buffer.read(frames);
36350
- const got2 = chunk.samples[0]?.length ?? 0;
36351
- if (got2 === 0) return void 0;
36352
- const left2 = chunk.samples[0] ?? new Float32Array(got2);
36353
- const right2 = channels >= 2 ? chunk.samples[1] ?? left2 : left2;
36354
- return [left2, right2];
36355
- }
36356
- const out = await pair.resampleIn.read(frames);
36357
- 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;
36358
36420
  if (got === 0) return void 0;
36359
- const left = out[0] ?? new Float32Array(got);
36360
- const right = out[1] ?? left;
36421
+ const left = chunk.samples[0] ?? new Float32Array(got);
36422
+ const right = channels >= 2 ? chunk.samples[1] ?? left : left;
36361
36423
  return [left, right];
36362
36424
  }
36363
- async function pumpSourceToResampleIn2(args) {
36364
- const { buffer, resampleIn, channels, chunkFrames } = args;
36365
- for (; ; ) {
36366
- const sourceChunk = await buffer.read(chunkFrames);
36367
- const sourceFrames = sourceChunk.samples[0]?.length ?? 0;
36368
- if (sourceFrames === 0) break;
36369
- const sourceLeft = sourceChunk.samples[0] ?? new Float32Array(sourceFrames);
36370
- const sourceRight = channels >= 2 ? sourceChunk.samples[1] ?? sourceLeft : sourceLeft;
36371
- await resampleIn.write([sourceLeft, sourceRight]);
36372
- if (sourceFrames < chunkFrames) break;
36373
- }
36374
- await resampleIn.end();
36375
- }
36376
- async function drainResampleOutToBuffer2(args) {
36377
- const { resampleOut, output, channels, sourceRate, bitDepth, originalFrames, writerState } = args;
36378
- for (; ; ) {
36379
- const chunk = await resampleOut.read(RESAMPLE_DRAIN_CHUNK2);
36380
- const got = chunk[0]?.length ?? 0;
36381
- if (got === 0) return;
36382
- await commitResampledFrames2({ chunk, channels, output, sourceRate, bitDepth, originalFrames, writerState });
36383
- }
36384
- }
36385
- async function commitResampledFrames2(args) {
36386
- const { chunk, channels, output, sourceRate, bitDepth, originalFrames, writerState } = args;
36387
- const firstChannel = chunk[0];
36388
- const got = firstChannel?.length ?? 0;
36389
- if (got === 0 || !firstChannel) return;
36390
- const remaining = originalFrames - writerState.written;
36391
- if (remaining <= 0) return;
36392
- const take = Math.min(got, remaining);
36393
- const right = chunk[1] ?? firstChannel;
36394
- const writeLeft = take === got ? firstChannel : firstChannel.subarray(0, take);
36395
- const writeRight = take === got ? right : right.subarray(0, take);
36396
- const writeChannels = buildWriteChannels(writeLeft, writeRight, channels);
36397
- await output.write(writeChannels, sourceRate, bitDepth);
36398
- writerState.written += take;
36399
- }
36400
36425
  function buildWriteChannels(left, right, channels) {
36401
36426
  const out = [];
36402
36427
  for (let channel = 0; channel < channels; channel++) {
@@ -36406,14 +36431,14 @@ function buildWriteChannels(left, right, channels) {
36406
36431
  }
36407
36432
  return out;
36408
36433
  }
36409
- async function padTail2(output, channels, originalFrames, written, sourceRate, bitDepth) {
36434
+ async function padTail2(output, channels, originalFrames, written, sampleRate, bitDepth) {
36410
36435
  if (written >= originalFrames) return;
36411
36436
  const missing = originalFrames - written;
36412
36437
  const padChannels = [];
36413
36438
  for (let channel = 0; channel < Math.max(1, channels); channel++) {
36414
36439
  padChannels.push(new Float32Array(missing));
36415
36440
  }
36416
- await output.write(padChannels, sourceRate, bitDepth);
36441
+ await output.write(padChannels, sampleRate, bitDepth);
36417
36442
  }
36418
36443
  var HtdemucsNode = class extends TransformNode {
36419
36444
  };
@@ -36573,39 +36598,31 @@ var SEGMENT_SAMPLES2 = N_FFT2 + (DIM_T3 - 1) * HOP_SIZE4;
36573
36598
  var OVERLAP2 = 0.25;
36574
36599
  var TRANSITION_POWER2 = 1;
36575
36600
  var CHUNK_FRAMES7 = 44100;
36576
- var RESAMPLE_DRAIN_CHUNK3 = 16384;
36577
36601
  var KimVocal2Stream = class extends BufferedTransformStream {
36578
36602
  constructor(node, context) {
36579
36603
  super(node, context);
36580
36604
  this.blockSize = WHOLE_FILE;
36581
36605
  this.fftInstance = new MixedRadixFft(N_FFT2);
36606
+ this.renderContext = context;
36582
36607
  }
36583
36608
  _setup(context) {
36584
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)));
36585
36619
  }
36586
36620
  async *_transform(buffered) {
36587
36621
  const originalFrames = buffered.frames;
36588
36622
  const channels = buffered.channels;
36589
36623
  if (originalFrames === 0 || channels === 0) return;
36590
- const sourceRate = this.sampleRate ?? SAMPLE_RATE;
36591
36624
  const bitDepth = this.bitDepth;
36592
- const needsResample = sourceRate !== SAMPLE_RATE;
36593
36625
  await buffered.reset();
36594
- let pair;
36595
- if (needsResample) {
36596
- pair = {
36597
- resampleIn: new ResampleStream(this.properties.ffmpegPath, {
36598
- sourceSampleRate: sourceRate,
36599
- targetSampleRate: SAMPLE_RATE,
36600
- channels: 2
36601
- }),
36602
- resampleOut: new ResampleStream(this.properties.ffmpegPath, {
36603
- sourceSampleRate: SAMPLE_RATE,
36604
- targetSampleRate: sourceRate,
36605
- channels: 2
36606
- })
36607
- };
36608
- }
36609
36626
  const output = new BlockBuffer();
36610
36627
  try {
36611
36628
  await this.runMainPass({
@@ -36613,26 +36630,19 @@ var KimVocal2Stream = class extends BufferedTransformStream {
36613
36630
  output,
36614
36631
  channels,
36615
36632
  originalFrames,
36616
- sourceRate,
36617
- bitDepth,
36618
- pair
36633
+ bitDepth
36619
36634
  });
36620
36635
  await output.reset();
36621
36636
  yield* output.iterate(CHUNK_FRAMES7);
36622
36637
  } finally {
36623
- if (pair) {
36624
- await Promise.all([pair.resampleIn.close(), pair.resampleOut.close()]);
36625
- }
36626
36638
  await output.close();
36627
36639
  }
36628
36640
  }
36629
36641
  async runMainPass(args) {
36630
- const { buffer, output, channels, originalFrames, sourceRate, bitDepth, pair } = args;
36642
+ const { buffer, output, channels, originalFrames, bitDepth } = args;
36631
36643
  const stride = Math.round((1 - OVERLAP2) * SEGMENT_SAMPLES2);
36632
36644
  const isMono = channels < 2;
36633
36645
  const writerState = { written: 0 };
36634
- const pumpDone = pair !== void 0 ? pumpSourceToResampleIn3({ buffer, resampleIn: pair.resampleIn, channels, chunkFrames: CHUNK_FRAMES7 }) : Promise.resolve();
36635
- const drainerDone = pair !== void 0 ? drainResampleOutToBuffer3({ resampleOut: pair.resampleOut, output, channels, sourceRate, bitDepth, originalFrames, writerState }) : Promise.resolve();
36636
36646
  const weight = buildTransitionWindow(SEGMENT_SAMPLES2, TRANSITION_POWER2);
36637
36647
  const workspace = createSegmentWorkspace(SEGMENT_SAMPLES2);
36638
36648
  const segLeft = new Float32Array(SEGMENT_SAMPLES2);
@@ -36642,14 +36652,13 @@ var KimVocal2Stream = class extends BufferedTransformStream {
36642
36652
  const outAccumLeft = new Float32Array(SEGMENT_SAMPLES2);
36643
36653
  const outAccumRight = new Float32Array(SEGMENT_SAMPLES2);
36644
36654
  const sumWeight = new Float32Array(SEGMENT_SAMPLES2);
36645
- const modelRateFrames = Math.round(originalFrames * SAMPLE_RATE / sourceRate);
36646
- const progressGate = createProgressGate(modelRateFrames);
36655
+ const progressGate = createProgressGate(originalFrames);
36647
36656
  let stableEmitted = 0;
36648
36657
  for (; ; ) {
36649
36658
  if (!inputExhausted) {
36650
36659
  while (segFilled < SEGMENT_SAMPLES2) {
36651
36660
  const need = SEGMENT_SAMPLES2 - segFilled;
36652
- 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) });
36653
36662
  if (got === void 0 || got[0].length === 0) {
36654
36663
  inputExhausted = true;
36655
36664
  break;
@@ -36682,17 +36691,15 @@ var KimVocal2Stream = class extends BufferedTransformStream {
36682
36691
  outAccumLeft,
36683
36692
  outAccumRight,
36684
36693
  sumWeight,
36685
- pair,
36686
36694
  output,
36687
36695
  channels,
36688
- sourceRate,
36689
36696
  bitDepth,
36690
36697
  originalFrames,
36691
36698
  writerState
36692
36699
  });
36693
36700
  stableEmitted += nStable;
36694
- const doneFrames = Math.min(stableEmitted, modelRateFrames);
36695
- 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);
36696
36703
  if (!isFinalIter) {
36697
36704
  segLeft.copyWithin(0, nStable, SEGMENT_SAMPLES2);
36698
36705
  segRight.copyWithin(0, nStable, SEGMENT_SAMPLES2);
@@ -36703,15 +36710,10 @@ var KimVocal2Stream = class extends BufferedTransformStream {
36703
36710
  break;
36704
36711
  }
36705
36712
  }
36706
- await pumpDone;
36707
- if (pair) {
36708
- await pair.resampleOut.end();
36709
- }
36710
- await drainerDone;
36711
- await padTail3(output, channels, originalFrames, writerState.written, sourceRate, bitDepth);
36713
+ await padTail3(output, channels, originalFrames, writerState.written, SAMPLE_RATE, bitDepth);
36712
36714
  }
36713
36715
  async emitStable(args) {
36714
- 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;
36715
36717
  if (nStable <= 0) return;
36716
36718
  const outLeft = new Float32Array(nStable);
36717
36719
  const outRight = new Float32Array(nStable);
@@ -36721,17 +36723,13 @@ var KimVocal2Stream = class extends BufferedTransformStream {
36721
36723
  outRight[index] = sw > 0 ? (outAccumRight[index] ?? 0) / sw : outAccumRight[index] ?? 0;
36722
36724
  }
36723
36725
  bandpass([outLeft, outRight], SAMPLE_RATE, this.properties.highPass, this.properties.lowPass);
36724
- if (pair) {
36725
- await pair.resampleOut.write([outLeft, outRight]);
36726
- } else {
36727
- const writeChannels = buildWriteChannels2(outLeft, outRight, channels);
36728
- const remaining = Math.max(0, originalFrames - writerState.written);
36729
- if (remaining > 0) {
36730
- const take = Math.min(nStable, remaining);
36731
- const sliced = take === nStable ? writeChannels : writeChannels.map((channel) => channel.subarray(0, take));
36732
- await output.write(sliced, sourceRate, bitDepth);
36733
- writerState.written += take;
36734
- }
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;
36735
36733
  }
36736
36734
  outAccumLeft.copyWithin(0, nStable, SEGMENT_SAMPLES2);
36737
36735
  outAccumLeft.fill(0, SEGMENT_SAMPLES2 - nStable, SEGMENT_SAMPLES2);
@@ -36742,59 +36740,14 @@ var KimVocal2Stream = class extends BufferedTransformStream {
36742
36740
  }
36743
36741
  };
36744
36742
  async function pullNextChunkAt4412(args) {
36745
- const { buffer, pair, channels, frames } = args;
36746
- if (!pair) {
36747
- const chunk = await buffer.read(frames);
36748
- const got2 = chunk.samples[0]?.length ?? 0;
36749
- if (got2 === 0) return void 0;
36750
- const left2 = chunk.samples[0] ?? new Float32Array(got2);
36751
- const right2 = channels >= 2 ? chunk.samples[1] ?? left2 : left2;
36752
- return [left2, right2];
36753
- }
36754
- const out = await pair.resampleIn.read(frames);
36755
- 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;
36756
36746
  if (got === 0) return void 0;
36757
- const left = out[0] ?? new Float32Array(got);
36758
- const right = out[1] ?? left;
36747
+ const left = chunk.samples[0] ?? new Float32Array(got);
36748
+ const right = channels >= 2 ? chunk.samples[1] ?? left : left;
36759
36749
  return [left, right];
36760
36750
  }
36761
- async function pumpSourceToResampleIn3(args) {
36762
- const { buffer, resampleIn, channels, chunkFrames } = args;
36763
- for (; ; ) {
36764
- const sourceChunk = await buffer.read(chunkFrames);
36765
- const sourceFrames = sourceChunk.samples[0]?.length ?? 0;
36766
- if (sourceFrames === 0) break;
36767
- const sourceLeft = sourceChunk.samples[0] ?? new Float32Array(sourceFrames);
36768
- const sourceRight = channels >= 2 ? sourceChunk.samples[1] ?? sourceLeft : sourceLeft;
36769
- await resampleIn.write([sourceLeft, sourceRight]);
36770
- if (sourceFrames < chunkFrames) break;
36771
- }
36772
- await resampleIn.end();
36773
- }
36774
- async function drainResampleOutToBuffer3(args) {
36775
- const { resampleOut, output, channels, sourceRate, bitDepth, originalFrames, writerState } = args;
36776
- for (; ; ) {
36777
- const chunk = await resampleOut.read(RESAMPLE_DRAIN_CHUNK3);
36778
- const got = chunk[0]?.length ?? 0;
36779
- if (got === 0) return;
36780
- await commitResampledFrames3({ chunk, channels, output, sourceRate, bitDepth, originalFrames, writerState });
36781
- }
36782
- }
36783
- async function commitResampledFrames3(args) {
36784
- const { chunk, channels, output, sourceRate, bitDepth, originalFrames, writerState } = args;
36785
- const firstChannel = chunk[0];
36786
- const got = firstChannel?.length ?? 0;
36787
- if (got === 0 || !firstChannel) return;
36788
- const remaining = originalFrames - writerState.written;
36789
- if (remaining <= 0) return;
36790
- const take = Math.min(got, remaining);
36791
- const right = chunk[1] ?? firstChannel;
36792
- const writeLeft = take === got ? firstChannel : firstChannel.subarray(0, take);
36793
- const writeRight = take === got ? right : right.subarray(0, take);
36794
- const writeChannels = buildWriteChannels2(writeLeft, writeRight, channels);
36795
- await output.write(writeChannels, sourceRate, bitDepth);
36796
- writerState.written += take;
36797
- }
36798
36751
  function buildWriteChannels2(left, right, channels) {
36799
36752
  const out = [];
36800
36753
  for (let channel = 0; channel < channels; channel++) {
@@ -36804,14 +36757,14 @@ function buildWriteChannels2(left, right, channels) {
36804
36757
  }
36805
36758
  return out;
36806
36759
  }
36807
- async function padTail3(output, channels, originalFrames, written, sourceRate, bitDepth) {
36760
+ async function padTail3(output, channels, originalFrames, written, sampleRate, bitDepth) {
36808
36761
  if (written >= originalFrames) return;
36809
36762
  const missing = originalFrames - written;
36810
36763
  const padChannels = [];
36811
36764
  for (let channel = 0; channel < Math.max(1, channels); channel++) {
36812
36765
  padChannels.push(new Float32Array(missing));
36813
36766
  }
36814
- await output.write(padChannels, sourceRate, bitDepth);
36767
+ await output.write(padChannels, sampleRate, bitDepth);
36815
36768
  }
36816
36769
  var KimVocal2Node = class extends TransformNode {
36817
36770
  };
@@ -36824,8 +36777,72 @@ function kimVocal2(options) {
36824
36777
  return new KimVocal2Node(options);
36825
36778
  }
36826
36779
  var CHUNK_FRAMES8 = 48e3;
36780
+ var TELEMETRY_PREFIX = "VST_HOST_EVENT ";
36781
+ var DIAGNOSTIC_TAIL_BYTES = 64 * 1024;
36827
36782
  var READY_LINE = "READY\n";
36828
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
+ }
36829
36846
  var VstHostExitedBeforeReadyError = class extends Error {
36830
36847
  constructor(code, stderr) {
36831
36848
  super(`vst-host exited before READY (code ${code ?? "null"}): ${stderr}`);
@@ -36834,7 +36851,7 @@ var VstHostExitedBeforeReadyError = class extends Error {
36834
36851
  this.stderr = stderr;
36835
36852
  }
36836
36853
  };
36837
- function spawnVstHost(binaryPath, args) {
36854
+ function spawnVstHost(binaryPath, args, options = {}) {
36838
36855
  const proc = spawn(binaryPath, [...args], {
36839
36856
  stdio: ["pipe", "pipe", "pipe"]
36840
36857
  });
@@ -36844,10 +36861,7 @@ function spawnVstHost(binaryPath, args) {
36844
36861
  const stdin = proc.stdin;
36845
36862
  const stdout = proc.stdout;
36846
36863
  const stderr = proc.stderr;
36847
- const stderrChunks = [];
36848
- stderr.on("data", (chunk) => {
36849
- stderrChunks.push(chunk);
36850
- });
36864
+ const getStderrTail = observeVstHostStderr(stderr, options.onLiveness);
36851
36865
  const ready = new Promise((resolve, reject) => {
36852
36866
  const seen = [];
36853
36867
  const cleanup = () => {
@@ -36878,7 +36892,7 @@ function spawnVstHost(binaryPath, args) {
36878
36892
  fail(new Error(`vst-host failed to start: ${error50.message}`));
36879
36893
  };
36880
36894
  const onClose = (code) => {
36881
- const stderrOutput = Buffer.concat(stderrChunks).toString();
36895
+ const stderrOutput = getStderrTail();
36882
36896
  fail(new VstHostExitedBeforeReadyError(code, stderrOutput));
36883
36897
  };
36884
36898
  const timer = setTimeout(() => {
@@ -36890,7 +36904,7 @@ function spawnVstHost(binaryPath, args) {
36890
36904
  });
36891
36905
  stdin.on("error", () => {
36892
36906
  });
36893
- return { proc, stdin, stdout, stderr, ready, stderrChunks };
36907
+ return { proc, stdin, stdout, stderr, ready, getStderrTail };
36894
36908
  }
36895
36909
  var CLEAN_WRAPPER_EXIT_CODES = /* @__PURE__ */ new Set([0, 1, 2]);
36896
36910
  function isRetryableInitCrash(error50) {
@@ -36901,7 +36915,7 @@ async function spawnVstHostReady(binaryPath, args, options = {}) {
36901
36915
  const maxAttempts = options.maxAttempts ?? 5;
36902
36916
  const backoffMs = options.backoffMs ?? 750;
36903
36917
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
36904
- const handle = spawnVstHost(binaryPath, args);
36918
+ const handle = spawnVstHost(binaryPath, args, { onLiveness: options.onLiveness });
36905
36919
  try {
36906
36920
  await handle.ready;
36907
36921
  return handle;
@@ -36925,9 +36939,11 @@ async function writeStagesJson(stages) {
36925
36939
  }
36926
36940
  };
36927
36941
  }
36928
- async function processStreamingThroughVstHost(handle, buffer, channelCount, sampleRate, bitDepth) {
36942
+ async function processStreamingThroughVstHost(handle, buffer, options) {
36943
+ const { channelCount, sampleRate, bitDepth, onInputProgress, onOutputProgress } = options;
36929
36944
  const inputFrames = buffer.frames;
36930
36945
  const expectedOutputBytes = inputFrames * channelCount * 4;
36946
+ let inputFramesDone = 0;
36931
36947
  const stdoutEnd = new Promise((resolve) => {
36932
36948
  handle.stdout.once("end", () => resolve());
36933
36949
  });
@@ -36940,15 +36956,22 @@ async function processStreamingThroughVstHost(handle, buffer, channelCount, samp
36940
36956
  const chunkFrames = chunk.samples[0]?.length ?? 0;
36941
36957
  if (chunkFrames === 0) break;
36942
36958
  const channelArrays = [];
36943
- for (let ch = 0; ch < channelCount; ch++) {
36944
- 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));
36945
36961
  }
36946
36962
  const interleaved = interleave(channelArrays, chunkFrames, channelCount);
36947
- const buf = Buffer.from(interleaved.buffer, interleaved.byteOffset, interleaved.byteLength);
36948
- const canWrite = handle.stdin.write(buf);
36963
+ const interleavedBuffer = Buffer.from(interleaved.buffer, interleaved.byteOffset, interleaved.byteLength);
36964
+ const canWrite = handle.stdin.write(interleavedBuffer);
36949
36965
  if (!canWrite) {
36950
36966
  await waitForDrain(handle.proc, handle.stdin);
36951
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
+ });
36952
36975
  if (chunkFrames < CHUNK_FRAMES8) break;
36953
36976
  }
36954
36977
  handle.stdin.end();
@@ -36957,6 +36980,7 @@ async function processStreamingThroughVstHost(handle, buffer, channelCount, samp
36957
36980
  let stdoutTail = Buffer.alloc(0);
36958
36981
  let stdoutError;
36959
36982
  const bytesPerFrame = channelCount * 4;
36983
+ let outputFramesDone = 0;
36960
36984
  let writeChain = Promise.resolve();
36961
36985
  const onData = (chunk) => {
36962
36986
  if (stdoutError !== void 0) return;
@@ -36971,7 +36995,16 @@ async function processStreamingThroughVstHost(handle, buffer, channelCount, samp
36971
36995
  const aligned = combined.subarray(0, alignedBytes);
36972
36996
  stdoutTail = combined.length === alignedBytes ? Buffer.alloc(0) : combined.subarray(alignedBytes);
36973
36997
  const channels = deinterleaveBuffer(aligned, channelCount);
36974
- 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) => {
36975
37008
  stdoutError ?? (stdoutError = error50 instanceof Error ? error50 : new Error(String(error50)));
36976
37009
  });
36977
37010
  };
@@ -36981,7 +37014,7 @@ async function processStreamingThroughVstHost(handle, buffer, channelCount, samp
36981
37014
  const exit = await exited;
36982
37015
  if (stdoutError !== void 0) throw stdoutError;
36983
37016
  if (exit.code !== 0) {
36984
- const stderrOutput = Buffer.concat(handle.stderrChunks).toString();
37017
+ const stderrOutput = handle.getStderrTail();
36985
37018
  throw new Error(`vst-host exited with code ${exit.code ?? "null"}${exit.signal ? ` (signal ${exit.signal})` : ""}: ${stderrOutput}`);
36986
37019
  }
36987
37020
  if (outputBytesReceived !== expectedOutputBytes) {
@@ -37022,6 +37055,8 @@ var Vst3Stream = class extends BufferedTransformStream {
37022
37055
  const channels = buffered.channels;
37023
37056
  const sampleRate = this.sampleRate ?? 44100;
37024
37057
  const bd = buffered.bitDepth;
37058
+ const inputGate = createProgressGate(buffered.frames);
37059
+ const outputGate = createProgressGate(buffered.frames);
37025
37060
  const args = [
37026
37061
  ...this.properties.extraArgs ?? [],
37027
37062
  "--stages-json",
@@ -37032,11 +37067,48 @@ var Vst3Stream = class extends BufferedTransformStream {
37032
37067
  String(channels)
37033
37068
  ];
37034
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
+ },
37035
37083
  onRetry: (failedAttempt, error50) => {
37036
37084
  this.log("vst-host init crash, retrying", { attempt: failedAttempt, error: error50.message }, "warn");
37037
37085
  }
37038
37086
  });
37039
- 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
+ });
37040
37112
  await buffered.reset();
37041
37113
  yield* buffered.iterate(44100);
37042
37114
  }