@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/README.md +16 -17
- package/dist/{chunk-YJWZP2OD.js → chunk-BKEMKXGG.js} +672 -531
- package/dist/index.d.ts +25 -10
- package/dist/index.js +1006 -934
- package/dist/nlm-worker.js +1 -1
- package/package.json +3 -3
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,
|
|
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
|
|
15552
|
-
|
|
15553
|
-
|
|
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(
|
|
15556
|
-
const interleaved = new Float32Array(
|
|
15557
|
-
const
|
|
15558
|
-
for (let
|
|
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
|
|
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
|
|
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(
|
|
15637
|
+
async function pullBytes(stream, isEnded, bytesNeeded) {
|
|
15571
15638
|
const chunks = [];
|
|
15572
15639
|
let collected = 0;
|
|
15573
15640
|
while (collected < bytesNeeded) {
|
|
15574
|
-
const chunk =
|
|
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
|
-
|
|
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
|
-
|
|
15591
|
-
|
|
15592
|
-
|
|
15593
|
-
|
|
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
|
-
|
|
15597
|
-
|
|
15598
|
-
|
|
15599
|
-
|
|
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
|
-
|
|
15607
|
-
|
|
15608
|
-
|
|
15609
|
-
|
|
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
|
|
15649
|
-
const
|
|
15650
|
-
interleaved[frame * channels +
|
|
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
|
|
15839
|
+
const writeBuffer = Buffer.from(interleaved.buffer, interleaved.byteOffset, interleaved.byteLength);
|
|
15654
15840
|
const ws = this.ensureWriteStream();
|
|
15655
|
-
const ok = ws.write(
|
|
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 +=
|
|
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
|
|
15702
|
-
const actualFrames = Math.floor(
|
|
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(
|
|
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.
|
|
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.
|
|
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
|
|
23376
|
-
const
|
|
23377
|
-
if (
|
|
23344
|
+
const last2 = abs % 10;
|
|
23345
|
+
const last22 = abs % 100;
|
|
23346
|
+
if (last22 >= 11 && last22 <= 19 || last2 === 0)
|
|
23378
23347
|
return "many";
|
|
23379
|
-
if (
|
|
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.
|
|
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
|
|
29976
|
-
|
|
29977
|
-
|
|
29978
|
-
|
|
29979
|
-
|
|
29980
|
-
|
|
29981
|
-
}
|
|
29982
|
-
|
|
29983
|
-
|
|
29984
|
-
}
|
|
29985
|
-
|
|
29986
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
30053
|
-
const
|
|
30054
|
-
|
|
30055
|
-
|
|
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
|
|
30069
|
-
const
|
|
30070
|
-
|
|
30071
|
-
|
|
30072
|
-
|
|
30073
|
-
|
|
30074
|
-
|
|
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
|
-
|
|
30078
|
-
|
|
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
|
|
30084
|
+
async setupChildren(children, readable, context) {
|
|
30087
30085
|
const pairs = teeReadable(readable, children);
|
|
30088
|
-
const nested = await Promise.all(pairs.map(([branch, child]) => this.
|
|
30086
|
+
const nested = await Promise.all(pairs.map(([branch, child]) => this.setup(child, branch, { ...context })));
|
|
30089
30087
|
return nested.flat();
|
|
30090
30088
|
}
|
|
30091
|
-
async
|
|
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.
|
|
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
|
|
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
|
|
30468
|
-
const byteOffset = frame * format.blockAlign +
|
|
30469
|
-
const channel = allChannels[
|
|
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
|
|
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
|
|
30946
|
+
for (let channel = 0; channel < this.channels; channel++) {
|
|
30852
30947
|
const newBuf = new Float32Array(newCapacity);
|
|
30853
|
-
newBuf.set(this.sampleBuffers[
|
|
30854
|
-
this.sampleBuffers[
|
|
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
|
|
30859
|
-
const
|
|
30860
|
-
if (!
|
|
30861
|
-
this.sampleBuffers[
|
|
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
|
|
30886
|
-
const
|
|
30887
|
-
|
|
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
|
|
30996
|
+
for (let channel = 0; channel < this.channels; channel++) {
|
|
30902
30997
|
const frames = computeSpectrogramFrames(
|
|
30903
|
-
this.sampleBuffers[
|
|
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(
|
|
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
|
|
30925
|
-
const
|
|
30926
|
-
|
|
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(
|
|
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
|
|
30937
|
-
if (!
|
|
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
|
-
|
|
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
|
|
30967
|
-
const sample = samples[
|
|
30968
|
-
const currentMin = min[
|
|
30969
|
-
const currentMax = max[
|
|
30970
|
-
if (currentMin !== void 0 && sample < currentMin) min[
|
|
30971
|
-
if (currentMax !== void 0 && sample > currentMax) max[
|
|
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
|
|
30976
|
-
target.writeFloatLE(min[
|
|
30977
|
-
target.writeFloatLE(max[
|
|
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.
|
|
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
|
|
31343
|
-
const sample = chunk.samples[
|
|
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 ??
|
|
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 ??
|
|
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
|
|
31447
|
-
const channel = chunk.samples[
|
|
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,
|
|
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[
|
|
31601
|
+
dithered += this.lastError[channelIndex] ?? 0;
|
|
31507
31602
|
}
|
|
31508
31603
|
const quantized = quantizeSample(dithered, levels);
|
|
31509
31604
|
if (noiseShaping) {
|
|
31510
|
-
this.lastError[
|
|
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,
|
|
31719
|
-
const { output, state } = applyAllpass(channel, coefficient, this.allpassState[
|
|
31720
|
-
this.allpassState[
|
|
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
|
|
31836
|
-
if (
|
|
31837
|
-
throw new Error(`Splice: target channel ${
|
|
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
|
|
31871
|
-
const channelSamples = samples[
|
|
31872
|
-
const insertChannel = this.insertSamples[
|
|
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
|
|
31998
|
-
const channel = samples[
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
32200
|
-
const channelArray = samples[
|
|
32290
|
+
for (let channel = 0; channel < channels; channel++) {
|
|
32291
|
+
const channelArray = samples[channel];
|
|
32201
32292
|
if (channelArray) {
|
|
32202
|
-
channelArray[frame] = floatView[frame * channels +
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
32265
|
-
|
|
32266
|
-
|
|
32267
|
-
|
|
32268
|
-
|
|
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
|
|
32282
|
-
|
|
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.
|
|
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
|
-
|
|
32300
|
-
await this.pendingDrain;
|
|
32301
|
-
}
|
|
32432
|
+
yield* this.serveWhileParked();
|
|
32302
32433
|
child.stdin.end();
|
|
32303
32434
|
if (this.stdinError) throw this.stdinError;
|
|
32304
|
-
|
|
32305
|
-
|
|
32306
|
-
|
|
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
|
|
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
|
|
34090
|
-
const value = chunk.samples[
|
|
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[
|
|
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
|
|
34101
|
-
const channelRing = channelRings[
|
|
34102
|
-
const channelWindow = channelWindows[
|
|
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
|
|
34190
|
-
const inputValue = channels[
|
|
34191
|
-
const outChannel = out[
|
|
34192
|
-
const chState = this.state[
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
34770
|
-
const view = this.scratch[
|
|
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
|
|
34789
|
-
const
|
|
34790
|
-
const dest = this.scratch[
|
|
34791
|
-
if (
|
|
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
|
|
34954
|
-
if (
|
|
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
|
|
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
|
|
35045
|
-
const
|
|
35046
|
-
if (
|
|
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,
|
|
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
|
|
35063
|
-
const targetStft = targetStfts[
|
|
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
|
|
35164
|
-
const stftOut = stft(targetScratch[
|
|
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
|
|
35178
|
-
const kalmanStates = kalmanStatesByCh[
|
|
35179
|
-
const interfererPsd = interfererPsdByCh[
|
|
35180
|
-
const msadChannelStates = msadChannelStatesByCh[
|
|
35181
|
-
const ispStates = ispStatesByCh[
|
|
35182
|
-
const targetStft = targetStfts[
|
|
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
|
|
35281
|
-
writeSamplesByChannel.push(cleanedByChannel[
|
|
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
|
|
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
|
|
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
|
|
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
|
|
35449
|
-
if (
|
|
35450
|
-
|
|
35451
|
-
|
|
35452
|
-
|
|
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
|
|
35489
|
-
const channel = chunk.samples[
|
|
35490
|
-
const dfnState = this.dfnStates[
|
|
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
|
|
35643
|
-
const stream = streams[
|
|
35644
|
-
const input = inputs[
|
|
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
|
|
35673
|
-
const
|
|
35674
|
-
const dest = stepBatch[
|
|
35675
|
-
if (!
|
|
35676
|
-
dest.set(
|
|
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,
|
|
35852
|
+
const { stepBatch, length, channels, output, sampleRate, bitDepth, originalFrames, writerState } = args;
|
|
35685
35853
|
if (length === 0) return;
|
|
35686
35854
|
const slices = [];
|
|
35687
|
-
for (let
|
|
35688
|
-
const
|
|
35689
|
-
slices.push(
|
|
35690
|
-
}
|
|
35691
|
-
|
|
35692
|
-
|
|
35693
|
-
|
|
35694
|
-
|
|
35695
|
-
|
|
35696
|
-
|
|
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,
|
|
35714
|
-
|
|
35715
|
-
|
|
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
|
-
|
|
35728
|
-
|
|
35729
|
-
|
|
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
|
-
|
|
35758
|
-
writerState.written += take;
|
|
35875
|
+
return out;
|
|
35759
35876
|
}
|
|
35760
|
-
async function padTail(output, channels, originalFrames, written,
|
|
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,
|
|
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(
|
|
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
|
-
|
|
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,
|
|
35942
|
+
const { buffer, output, channels, originalFrames, bitDepth } = args;
|
|
35838
35943
|
const streams = [];
|
|
35839
|
-
for (let
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
35866
|
-
const
|
|
35867
|
-
const dest = stepAccum[
|
|
35868
|
-
if (!
|
|
35869
|
-
dest.set(
|
|
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,
|
|
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,
|
|
35886
|
-
if (progressGate(doneFrames, Date.now())) this.emitProgress("process", doneFrames,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
36017
|
+
await commitStepBatch({ stepBatch, length: stepBatchLen, channels, output, sampleRate: DTLN_SAMPLE_RATE, bitDepth, originalFrames, writerState });
|
|
35917
36018
|
stepBatchLen = 0;
|
|
35918
36019
|
}
|
|
35919
|
-
|
|
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
|
|
36001
|
-
const stftCh =
|
|
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 *
|
|
36005
|
-
const imagIdx = (2 *
|
|
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
|
|
36041
|
-
const xtIndex = source * 2 * 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 *
|
|
36051
|
-
const imagIdx = baseOffset + (2 *
|
|
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 +
|
|
36070
|
-
const
|
|
36071
|
-
if (
|
|
36072
|
-
|
|
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(
|
|
36193
|
+
constructor(node, context) {
|
|
36194
|
+
super(node, context);
|
|
36099
36195
|
this.blockSize = WHOLE_FILE;
|
|
36196
|
+
this.renderContext = context;
|
|
36100
36197
|
}
|
|
36101
|
-
_setup(
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
36252
|
-
if (progressGate(doneFrames, Date.now())) this.emitProgress("process", doneFrames,
|
|
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
|
|
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,
|
|
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
|
-
|
|
36276
|
-
|
|
36277
|
-
|
|
36278
|
-
const
|
|
36279
|
-
const
|
|
36280
|
-
|
|
36281
|
-
|
|
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
|
|
36289
|
-
if (!
|
|
36290
|
-
|
|
36291
|
-
|
|
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,
|
|
36348
|
-
|
|
36349
|
-
|
|
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 =
|
|
36360
|
-
const right =
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
36695
|
-
if (progressGate(doneFrames, Date.now())) this.emitProgress("process", doneFrames,
|
|
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
|
|
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,
|
|
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
|
-
|
|
36725
|
-
|
|
36726
|
-
|
|
36727
|
-
const
|
|
36728
|
-
const
|
|
36729
|
-
|
|
36730
|
-
|
|
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,
|
|
36746
|
-
|
|
36747
|
-
|
|
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 =
|
|
36758
|
-
const right =
|
|
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,
|
|
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,
|
|
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
|
|
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 =
|
|
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,
|
|
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,
|
|
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
|
|
36944
|
-
channelArrays.push(chunk.samples[
|
|
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
|
|
36948
|
-
const canWrite = handle.stdin.write(
|
|
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(
|
|
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 =
|
|
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,
|
|
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
|
}
|