@aeonentertainment/mixer 0.4.0 → 0.5.0
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 +4 -2
- package/dist/index.d.mts +72 -4
- package/dist/index.mjs +25 -10
- package/native/binding/index.d.ts +36 -3
- package/native/binding/index.darwin-arm64.node +0 -0
- package/native/binding/index.darwin-x64.node +0 -0
- package/native/binding/index.linux-arm64-gnu.node +0 -0
- package/native/binding/index.linux-x64-gnu.node +0 -0
- package/native/binding/index.win32-x64-msvc.node +0 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Each [release](https://github.com/aeonentertainment/aeon-mixer/releases) carries
|
|
|
15
15
|
Depend on its URL, and the lockfile pins its hash:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
npm install https://github.com/aeonentertainment/aeon-mixer/releases/download/v0.
|
|
18
|
+
npm install https://github.com/aeonentertainment/aeon-mixer/releases/download/v0.5.0/aeonentertainment-mixer-0.5.0.tgz
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## Use
|
|
@@ -52,7 +52,9 @@ await engine.close();
|
|
|
52
52
|
takes Opus over UDP from a phone or mediasoup, `l16` takes raw PCM over loopback from a house
|
|
53
53
|
mic, and `push` takes PCM the host hands to `mixer.push`. Each slot has gain, pan, mute, solo,
|
|
54
54
|
reverb, a pitch reading, a melody guide and pitch correction toward it, whose delay `stats()`
|
|
55
|
-
reports.
|
|
55
|
+
reports. A slot can also take the room's echo of the mix out of its input and duck itself when
|
|
56
|
+
its loop through the speakers starts to howl; both add no delay, and `stats()` reports what
|
|
57
|
+
they found.
|
|
56
58
|
- **The backing** is any format Symphonia decodes, resampled to 48 kHz. The armed key and speed
|
|
57
59
|
are rendered before `start` resolves. Live, it supports seek, pause, resume, a key change and
|
|
58
60
|
stem gains, and `start`, `seek` and `resume` report the position the lyric clock follows.
|
package/dist/index.d.mts
CHANGED
|
@@ -73,7 +73,10 @@ declare class Mixer$1 {
|
|
|
73
73
|
* recording it for null.
|
|
74
74
|
*/
|
|
75
75
|
recordSlot(index: number, path?: string | undefined | null): void;
|
|
76
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* Every slot's ingest counters, corrector delay and echo findings, as the audio thread last
|
|
78
|
+
* published them.
|
|
79
|
+
*/
|
|
77
80
|
stats(): Array<SlotStatsInfo>;
|
|
78
81
|
/** The return stream's counters, or null for a mixer without one. */
|
|
79
82
|
returnStats(): RtpReturnStats | null;
|
|
@@ -91,11 +94,23 @@ interface BackingInput {
|
|
|
91
94
|
/** The armed speed factor, rendered before start. */
|
|
92
95
|
speed: number;
|
|
93
96
|
}
|
|
97
|
+
interface CleanupInput {
|
|
98
|
+
enabled: boolean;
|
|
99
|
+
}
|
|
94
100
|
interface CorrectorInput {
|
|
95
101
|
enabled: boolean;
|
|
96
102
|
strength: number;
|
|
97
103
|
retuneMs: number;
|
|
98
104
|
}
|
|
105
|
+
/** What a slot's echo canceller has found, as JavaScript reads it. */
|
|
106
|
+
interface EchoInfo {
|
|
107
|
+
/** From the master being rendered to its echo reaching the slot. */
|
|
108
|
+
pathMs: number;
|
|
109
|
+
/** How far under the master the echo arrives, over the last second. */
|
|
110
|
+
bleedDb: number;
|
|
111
|
+
/** How much quieter the slot came out than it went in, over the last second. */
|
|
112
|
+
removedDb: number;
|
|
113
|
+
}
|
|
99
114
|
interface GuideInput {
|
|
100
115
|
notes: Array<NoteInput>;
|
|
101
116
|
/** The key's pitch classes as bits, C in bit 0. */
|
|
@@ -155,8 +170,11 @@ interface OpenOptions {
|
|
|
155
170
|
/** A device clock's output: an id from `outputs` to pin, or none to follow the default output. */
|
|
156
171
|
device?: string;
|
|
157
172
|
periodFrames: number;
|
|
158
|
-
/**
|
|
159
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Pins the jitter buffer's playout delay for every `rtp` and `l16` slot. Without it, each
|
|
175
|
+
* stream chooses its own.
|
|
176
|
+
*/
|
|
177
|
+
playoutDelayFrames?: number;
|
|
160
178
|
}
|
|
161
179
|
interface OutputDevice {
|
|
162
180
|
id: string;
|
|
@@ -202,6 +220,9 @@ interface SlotParamsInput {
|
|
|
202
220
|
corrector: CorrectorInput;
|
|
203
221
|
reverb: ReverbInput;
|
|
204
222
|
voice: VoiceInput;
|
|
223
|
+
echoCancel: SwitchInput;
|
|
224
|
+
cleanup: CleanupInput;
|
|
225
|
+
howlGuard: SwitchInput;
|
|
205
226
|
guideOffsetMs: number;
|
|
206
227
|
}
|
|
207
228
|
/** `SlotStats` as JavaScript reads it. */
|
|
@@ -209,16 +230,25 @@ interface SlotStatsInfo {
|
|
|
209
230
|
pushed: number;
|
|
210
231
|
lost: number;
|
|
211
232
|
late: number;
|
|
233
|
+
lateAtOpening: number;
|
|
212
234
|
concealed: number;
|
|
213
235
|
resyncs: number;
|
|
214
236
|
jitterMs: number;
|
|
215
237
|
correctorDelayMs: number;
|
|
216
238
|
correctorMaxDelayMs: number;
|
|
239
|
+
/** Null while the canceller is off or has yet to find and learn the room's echo. */
|
|
240
|
+
echo: EchoInfo | null;
|
|
241
|
+
playoutDelayMs: number;
|
|
242
|
+
howlCatches: number;
|
|
217
243
|
}
|
|
218
244
|
interface StemGainsInput {
|
|
219
245
|
instrumental: number;
|
|
220
246
|
guide: number;
|
|
221
247
|
}
|
|
248
|
+
/** An effect with no knob but on and off. */
|
|
249
|
+
interface SwitchInput {
|
|
250
|
+
enabled: boolean;
|
|
251
|
+
}
|
|
222
252
|
interface VoiceInput {
|
|
223
253
|
enabled: boolean;
|
|
224
254
|
formantSemitones: number;
|
|
@@ -245,7 +275,10 @@ interface OpenEngineOptions {
|
|
|
245
275
|
};
|
|
246
276
|
/** Fixed for the engine's life, default 8, at most 16. */
|
|
247
277
|
slots?: number;
|
|
248
|
-
/**
|
|
278
|
+
/**
|
|
279
|
+
* Pins the ingest jitter buffer to this delay. Leave it out and each slot chooses its own from
|
|
280
|
+
* what its stream shows, opening at 15; `stats()` reports where each one is.
|
|
281
|
+
*/
|
|
249
282
|
playoutDelayMs?: number;
|
|
250
283
|
}
|
|
251
284
|
interface OutputInfo {
|
|
@@ -308,6 +341,24 @@ type SlotEffect = {
|
|
|
308
341
|
kind: "voice";
|
|
309
342
|
enabled: boolean;
|
|
310
343
|
formantSemitones: number;
|
|
344
|
+
} |
|
|
345
|
+
/** Takes the room's echo of the mix out of the slot's input, with no added delay. */
|
|
346
|
+
{
|
|
347
|
+
kind: "echo-cancel";
|
|
348
|
+
enabled: boolean;
|
|
349
|
+
} |
|
|
350
|
+
/**
|
|
351
|
+
* A high-pass and gate ahead of the corrector and a compressor after the meter. None reads
|
|
352
|
+
* ahead or buffers; decision 0012 has their numbers and costs.
|
|
353
|
+
*/
|
|
354
|
+
{
|
|
355
|
+
kind: "cleanup";
|
|
356
|
+
enabled: boolean;
|
|
357
|
+
} |
|
|
358
|
+
/** Ducks the slot when its loop through the speakers starts to howl, with no added delay. */
|
|
359
|
+
{
|
|
360
|
+
kind: "howl-guard";
|
|
361
|
+
enabled: boolean;
|
|
311
362
|
};
|
|
312
363
|
interface MelodyGuide {
|
|
313
364
|
notes: ReadonlyArray<{
|
|
@@ -322,6 +373,8 @@ interface SlotStats {
|
|
|
322
373
|
pushed: number;
|
|
323
374
|
lost: number;
|
|
324
375
|
late: number;
|
|
376
|
+
/** Of `late`, the packets that would also have been late had the delay stayed where it opened. */
|
|
377
|
+
lateAtOpening: number;
|
|
325
378
|
concealed: number;
|
|
326
379
|
resyncs: number;
|
|
327
380
|
jitterMs: number;
|
|
@@ -332,6 +385,21 @@ interface SlotStats {
|
|
|
332
385
|
correctorDelayMs: number;
|
|
333
386
|
/** The most that delay can reach; 0 with correction off. It holds still, so a budget adds this. */
|
|
334
387
|
correctorMaxDelayMs: number;
|
|
388
|
+
/**
|
|
389
|
+
* What the echo canceller found: `pathMs` from the mix being rendered to its echo reaching the
|
|
390
|
+
* slot, `bleedDb` how far under the mix that echo arrives, `removedDb` how much quieter the slot
|
|
391
|
+
* came out than it went in, both over the last second. Null while it is off, or until it has
|
|
392
|
+
* found the echo and learned it.
|
|
393
|
+
*/
|
|
394
|
+
echo: {
|
|
395
|
+
pathMs: number;
|
|
396
|
+
bleedDb: number;
|
|
397
|
+
removedDb: number;
|
|
398
|
+
} | null;
|
|
399
|
+
/** The delay the stream's earliest packets wait now; zero for a `push` slot. */
|
|
400
|
+
playoutDelayMs: number;
|
|
401
|
+
/** How often the howl guard has ducked this slot. */
|
|
402
|
+
howlCatches: number;
|
|
335
403
|
}
|
|
336
404
|
type EngineEvents = {
|
|
337
405
|
fault: (error: Error) => void;
|
package/dist/index.mjs
CHANGED
|
@@ -234,6 +234,9 @@ function defaultSlotParams() {
|
|
|
234
234
|
enabled: false,
|
|
235
235
|
formantSemitones: 0
|
|
236
236
|
},
|
|
237
|
+
echoCancel: { enabled: false },
|
|
238
|
+
cleanup: { enabled: false },
|
|
239
|
+
howlGuard: { enabled: false },
|
|
237
240
|
guideOffsetMs: 0
|
|
238
241
|
};
|
|
239
242
|
}
|
|
@@ -316,10 +319,19 @@ var Mixer = class {
|
|
|
316
319
|
mix: finiteIn("mix", effect.mix, 0, 1)
|
|
317
320
|
};
|
|
318
321
|
break;
|
|
319
|
-
case "voice":
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
322
|
+
case "voice":
|
|
323
|
+
next.voice = {
|
|
324
|
+
enabled: effect.enabled,
|
|
325
|
+
formantSemitones: finiteIn("formantSemitones", effect.formantSemitones, -12, 12)
|
|
326
|
+
};
|
|
327
|
+
break;
|
|
328
|
+
case "echo-cancel":
|
|
329
|
+
next.echoCancel = { enabled: effect.enabled };
|
|
330
|
+
break;
|
|
331
|
+
case "cleanup":
|
|
332
|
+
next.cleanup = { enabled: effect.enabled };
|
|
333
|
+
break;
|
|
334
|
+
case "howl-guard": next.howlGuard = { enabled: effect.enabled };
|
|
323
335
|
}
|
|
324
336
|
this.#write(slot, next);
|
|
325
337
|
}
|
|
@@ -498,11 +510,14 @@ var Mixer = class {
|
|
|
498
510
|
}
|
|
499
511
|
};
|
|
500
512
|
function defaultEffects() {
|
|
501
|
-
const { corrector, reverb, voice } = defaultSlotParams();
|
|
513
|
+
const { corrector, reverb, voice, cleanup, echoCancel, howlGuard } = defaultSlotParams();
|
|
502
514
|
return {
|
|
503
515
|
corrector,
|
|
504
516
|
reverb,
|
|
505
|
-
voice
|
|
517
|
+
voice,
|
|
518
|
+
cleanup,
|
|
519
|
+
echoCancel,
|
|
520
|
+
howlGuard
|
|
506
521
|
};
|
|
507
522
|
}
|
|
508
523
|
function toGuideInput(guide) {
|
|
@@ -551,7 +566,7 @@ var Engine = class {
|
|
|
551
566
|
#state = "running";
|
|
552
567
|
constructor(options) {
|
|
553
568
|
this.#slots = integerIn("slots", options.slots ?? 8, 1, 16);
|
|
554
|
-
const
|
|
569
|
+
const pinned = options.playoutDelayMs === void 0 ? {} : { playoutDelayFrames: msToFrames(finiteIn("playoutDelayMs", options.playoutDelayMs, 5, 100)) };
|
|
555
570
|
const deliver = (batch) => {
|
|
556
571
|
this.#deliver(batch);
|
|
557
572
|
};
|
|
@@ -559,13 +574,13 @@ var Engine = class {
|
|
|
559
574
|
this.#native = new NativeEngine({
|
|
560
575
|
clock: "offline",
|
|
561
576
|
periodFrames: 128,
|
|
562
|
-
|
|
577
|
+
...pinned
|
|
563
578
|
}, deliver);
|
|
564
579
|
this.pump = (blocks) => this.#native.pump(integerIn("blocks", blocks, 1, 1 << 20));
|
|
565
580
|
} else if ("timer" in options.output) this.#native = new NativeEngine({
|
|
566
581
|
clock: "timer",
|
|
567
582
|
periodFrames: 128,
|
|
568
|
-
|
|
583
|
+
...pinned
|
|
569
584
|
}, deliver);
|
|
570
585
|
else {
|
|
571
586
|
const { device, periodFrames = 128 } = options.output;
|
|
@@ -573,7 +588,7 @@ var Engine = class {
|
|
|
573
588
|
this.#native = new NativeEngine({
|
|
574
589
|
clock: "device",
|
|
575
590
|
periodFrames,
|
|
576
|
-
|
|
591
|
+
...pinned,
|
|
577
592
|
...device === void 0 ? {} : { device }
|
|
578
593
|
}, deliver);
|
|
579
594
|
}
|
|
@@ -86,7 +86,10 @@ export declare class Mixer {
|
|
|
86
86
|
* recording it for null.
|
|
87
87
|
*/
|
|
88
88
|
recordSlot(index: number, path?: string | undefined | null): void
|
|
89
|
-
/**
|
|
89
|
+
/**
|
|
90
|
+
* Every slot's ingest counters, corrector delay and echo findings, as the audio thread last
|
|
91
|
+
* published them.
|
|
92
|
+
*/
|
|
90
93
|
stats(): Array<SlotStatsInfo>
|
|
91
94
|
/** The return stream's counters, or null for a mixer without one. */
|
|
92
95
|
returnStats(): RtpReturnStats | null
|
|
@@ -106,6 +109,10 @@ export interface BackingInput {
|
|
|
106
109
|
speed: number
|
|
107
110
|
}
|
|
108
111
|
|
|
112
|
+
export interface CleanupInput {
|
|
113
|
+
enabled: boolean
|
|
114
|
+
}
|
|
115
|
+
|
|
109
116
|
/** What drives the audio thread. */
|
|
110
117
|
export declare const enum ClockKind {
|
|
111
118
|
/** The offline driver, pumped from JavaScript. */
|
|
@@ -122,6 +129,16 @@ export interface CorrectorInput {
|
|
|
122
129
|
retuneMs: number
|
|
123
130
|
}
|
|
124
131
|
|
|
132
|
+
/** What a slot's echo canceller has found, as JavaScript reads it. */
|
|
133
|
+
export interface EchoInfo {
|
|
134
|
+
/** From the master being rendered to its echo reaching the slot. */
|
|
135
|
+
pathMs: number
|
|
136
|
+
/** How far under the master the echo arrives, over the last second. */
|
|
137
|
+
bleedDb: number
|
|
138
|
+
/** How much quieter the slot came out than it went in, over the last second. */
|
|
139
|
+
removedDb: number
|
|
140
|
+
}
|
|
141
|
+
|
|
125
142
|
export interface GuideInput {
|
|
126
143
|
notes: Array<NoteInput>
|
|
127
144
|
/** The key's pitch classes as bits, C in bit 0. */
|
|
@@ -153,8 +170,11 @@ export interface OpenOptions {
|
|
|
153
170
|
/** A device clock's output: an id from `outputs` to pin, or none to follow the default output. */
|
|
154
171
|
device?: string
|
|
155
172
|
periodFrames: number
|
|
156
|
-
/**
|
|
157
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Pins the jitter buffer's playout delay for every `rtp` and `l16` slot. Without it, each
|
|
175
|
+
* stream chooses its own.
|
|
176
|
+
*/
|
|
177
|
+
playoutDelayFrames?: number
|
|
158
178
|
}
|
|
159
179
|
|
|
160
180
|
export interface OutputDevice {
|
|
@@ -207,6 +227,9 @@ export interface SlotParamsInput {
|
|
|
207
227
|
corrector: CorrectorInput
|
|
208
228
|
reverb: ReverbInput
|
|
209
229
|
voice: VoiceInput
|
|
230
|
+
echoCancel: SwitchInput
|
|
231
|
+
cleanup: CleanupInput
|
|
232
|
+
howlGuard: SwitchInput
|
|
210
233
|
guideOffsetMs: number
|
|
211
234
|
}
|
|
212
235
|
|
|
@@ -215,11 +238,16 @@ export interface SlotStatsInfo {
|
|
|
215
238
|
pushed: number
|
|
216
239
|
lost: number
|
|
217
240
|
late: number
|
|
241
|
+
lateAtOpening: number
|
|
218
242
|
concealed: number
|
|
219
243
|
resyncs: number
|
|
220
244
|
jitterMs: number
|
|
221
245
|
correctorDelayMs: number
|
|
222
246
|
correctorMaxDelayMs: number
|
|
247
|
+
/** Null while the canceller is off or has yet to find and learn the room's echo. */
|
|
248
|
+
echo: EchoInfo | null
|
|
249
|
+
playoutDelayMs: number
|
|
250
|
+
howlCatches: number
|
|
223
251
|
}
|
|
224
252
|
|
|
225
253
|
export interface StemGainsInput {
|
|
@@ -227,6 +255,11 @@ export interface StemGainsInput {
|
|
|
227
255
|
guide: number
|
|
228
256
|
}
|
|
229
257
|
|
|
258
|
+
/** An effect with no knob but on and off. */
|
|
259
|
+
export interface SwitchInput {
|
|
260
|
+
enabled: boolean
|
|
261
|
+
}
|
|
262
|
+
|
|
230
263
|
export interface VoiceInput {
|
|
231
264
|
enabled: boolean
|
|
232
265
|
formantSemitones: number
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aeonentertainment/mixer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Low-latency LAN audio mixer for Aeon Karaoke: phone and house mics, a backing track, one mix to the room.",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/aeonentertainment/aeon-mixer/issues"
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"@napi-rs/cli": "^3.10.9",
|
|
27
27
|
"@types/node": "^26.6.6",
|
|
28
28
|
"typescript": "^7.0.2",
|
|
29
|
-
"vite-plus": "1.
|
|
29
|
+
"vite-plus": "1.5.1"
|
|
30
30
|
},
|
|
31
31
|
"devEngines": {
|
|
32
32
|
"packageManager": {
|