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