@libraz/libsonare 1.8.1 → 1.8.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 +5 -5
- package/dist/analysis.js +2 -0
- package/dist/analysis.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1927 -140
- package/dist/index.js.map +1 -1
- package/dist/instrument_types.d.ts +27 -3
- package/dist/instrument_types.d.ts.map +1 -1
- package/dist/mastering_core.d.ts +3 -2
- package/dist/mastering_core.d.ts.map +1 -1
- package/dist/mixer.d.ts +2 -2
- package/dist/project.d.ts +5 -4
- package/dist/project.d.ts.map +1 -1
- package/dist/project_class.d.ts +57 -3
- package/dist/project_class.d.ts.map +1 -1
- package/dist/project_internal.d.ts +35 -1
- package/dist/project_internal.d.ts.map +1 -1
- package/dist/project_types.d.ts +10 -1
- package/dist/project_types.d.ts.map +1 -1
- package/dist/public_types.d.ts +1 -0
- package/dist/public_types.d.ts.map +1 -1
- package/dist/public_types_mastering.d.ts +1 -1
- package/dist/public_types_mastering.d.ts.map +1 -1
- package/dist/public_types_vocal_edit.d.ts +250 -0
- package/dist/public_types_vocal_edit.d.ts.map +1 -0
- package/dist/public_types_vocal_project.d.ts +63 -0
- package/dist/public_types_vocal_project.d.ts.map +1 -0
- package/dist/realtime_engine.d.ts +57 -10
- package/dist/realtime_engine.d.ts.map +1 -1
- package/dist/repair_dereverb.d.ts +1 -0
- package/dist/repair_dereverb.d.ts.map +1 -1
- package/dist/repair_noise.d.ts +1 -0
- package/dist/repair_noise.d.ts.map +1 -1
- package/dist/sonare-analysis.wasm +0 -0
- package/dist/sonare.d.ts +53 -0
- package/dist/sonare.js +1 -1
- package/dist/sonare.wasm +0 -0
- package/dist/vocal_edit.d.ts +131 -0
- package/dist/vocal_edit.d.ts.map +1 -0
- package/dist/vocal_edit_worker.d.ts +51 -0
- package/dist/vocal_edit_worker.d.ts.map +1 -0
- package/dist/vocal_edit_worker.js +1462 -0
- package/dist/vocal_edit_worker.js.map +1 -0
- package/dist/vocal_edit_worker_client.d.ts +118 -0
- package/dist/vocal_edit_worker_client.d.ts.map +1 -0
- package/dist/vocal_edit_worker_protocol.d.ts +139 -0
- package/dist/vocal_edit_worker_protocol.d.ts.map +1 -0
- package/dist/vocal_project.d.ts +17 -0
- package/dist/vocal_project.d.ts.map +1 -0
- package/dist/web_midi.d.ts +4 -0
- package/dist/web_midi.d.ts.map +1 -1
- package/dist/worker.js.map +1 -1
- package/dist/worklet/engine-mixer-facade.d.ts +2 -1
- package/dist/worklet/engine-mixer-facade.d.ts.map +1 -1
- package/dist/worklet/engine-node.d.ts +8 -0
- package/dist/worklet/engine-node.d.ts.map +1 -1
- package/dist/worklet/engine-processor.d.ts +2 -1
- package/dist/worklet/engine-processor.d.ts.map +1 -1
- package/dist/worklet/engine-register.d.ts.map +1 -1
- package/dist/worklet/engine-strips.d.ts +4 -2
- package/dist/worklet/engine-strips.d.ts.map +1 -1
- package/dist/worklet/engine.d.ts +15 -3
- package/dist/worklet/engine.d.ts.map +1 -1
- package/dist/worklet/guards.d.ts +3 -1
- package/dist/worklet/guards.d.ts.map +1 -1
- package/dist/worklet/messages.d.ts +50 -5
- package/dist/worklet/messages.d.ts.map +1 -1
- package/dist/worklet/protocol.d.ts +10 -0
- package/dist/worklet/protocol.d.ts.map +1 -1
- package/dist/worklet.d.ts +1 -1
- package/dist/worklet.d.ts.map +1 -1
- package/dist/worklet.js +223 -16
- package/dist/worklet.js.map +1 -1
- package/package.json +6 -1
- package/src/index.ts +33 -0
- package/src/instrument_types.ts +29 -3
- package/src/mastering_core.ts +3 -2
- package/src/mixer.ts +2 -2
- package/src/project.ts +8 -1
- package/src/project_class.ts +136 -1
- package/src/project_internal.ts +90 -0
- package/src/project_types.ts +11 -1
- package/src/public_types.ts +1 -0
- package/src/public_types_mastering.ts +2 -0
- package/src/public_types_vocal_edit.ts +289 -0
- package/src/public_types_vocal_project.ts +69 -0
- package/src/realtime_engine.ts +86 -9
- package/src/repair_dereverb.ts +1 -0
- package/src/repair_noise.ts +1 -0
- package/src/sonare.js.d.ts +53 -0
- package/src/vocal_edit.ts +885 -0
- package/src/vocal_edit_worker.ts +832 -0
- package/src/vocal_edit_worker_client.ts +812 -0
- package/src/vocal_edit_worker_protocol.ts +206 -0
- package/src/vocal_project.ts +327 -0
- package/src/web_midi.ts +100 -5
- package/src/worklet/engine-mixer-facade.ts +2 -1
- package/src/worklet/engine-node.ts +47 -0
- package/src/worklet/engine-processor.ts +46 -1
- package/src/worklet/engine-register.ts +3 -0
- package/src/worklet/engine-strips.ts +31 -4
- package/src/worklet/engine.ts +27 -3
- package/src/worklet/guards.ts +26 -0
- package/src/worklet/messages.ts +59 -2
- package/src/worklet/protocol.ts +10 -0
- package/src/worklet.ts +2 -0
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
isClipPageRequestMessage,
|
|
6
6
|
isEngineCaptureResponseForOperation,
|
|
7
7
|
isEngineCaptureResponseMessage,
|
|
8
|
+
isEngineInsertGainReductionResponseMessage,
|
|
8
9
|
isEngineTelemetryRecord,
|
|
9
10
|
isEngineTransportResponseMessage,
|
|
10
11
|
isExternalMidiBatchMessage,
|
|
@@ -144,6 +145,14 @@ export class SonareRealtimeEngineNode {
|
|
|
144
145
|
reject: (reason?: unknown) => void;
|
|
145
146
|
}
|
|
146
147
|
>();
|
|
148
|
+
private insertGainReductionRequestId = 1;
|
|
149
|
+
private readonly insertGainReductionRequests = new Map<
|
|
150
|
+
number,
|
|
151
|
+
{
|
|
152
|
+
resolve: (values: number[]) => void;
|
|
153
|
+
reject: (reason?: unknown) => void;
|
|
154
|
+
}
|
|
155
|
+
>();
|
|
147
156
|
private resolveReady!: () => void;
|
|
148
157
|
private rejectReady!: (reason?: unknown) => void;
|
|
149
158
|
private destroyed = false;
|
|
@@ -205,6 +214,16 @@ export class SonareRealtimeEngineNode {
|
|
|
205
214
|
pending.reject(new Error(event.data.error ?? 'Transport request failed'));
|
|
206
215
|
}
|
|
207
216
|
}
|
|
217
|
+
} else if (isEngineInsertGainReductionResponseMessage(event.data)) {
|
|
218
|
+
const pending = this.insertGainReductionRequests.get(event.data.requestId);
|
|
219
|
+
if (pending) {
|
|
220
|
+
this.insertGainReductionRequests.delete(event.data.requestId);
|
|
221
|
+
if (event.data.ok && Array.isArray(event.data.values)) {
|
|
222
|
+
pending.resolve(event.data.values);
|
|
223
|
+
} else {
|
|
224
|
+
pending.reject(new Error(event.data.error ?? 'Insert gain reduction request failed'));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
208
227
|
} else if (isEngineTelemetryRecord(event.data)) {
|
|
209
228
|
this.emitTelemetry(event.data);
|
|
210
229
|
} else if (isMeterSnapshot(event.data)) {
|
|
@@ -659,6 +678,10 @@ export class SonareRealtimeEngineNode {
|
|
|
659
678
|
pending.reject(new Error('Realtime engine node is destroyed.'));
|
|
660
679
|
}
|
|
661
680
|
this.transportRequests.clear();
|
|
681
|
+
for (const pending of this.insertGainReductionRequests.values()) {
|
|
682
|
+
pending.reject(new Error('Realtime engine node is destroyed.'));
|
|
683
|
+
}
|
|
684
|
+
this.insertGainReductionRequests.clear();
|
|
662
685
|
this.telemetryListeners.clear();
|
|
663
686
|
this.meterListeners.clear();
|
|
664
687
|
this.scopeListeners.clear();
|
|
@@ -728,6 +751,30 @@ export class SonareRealtimeEngineNode {
|
|
|
728
751
|
}
|
|
729
752
|
}
|
|
730
753
|
|
|
754
|
+
/**
|
|
755
|
+
* Reads one target's per-insert gain reduction (dB, each <= 0, pre-to-post insert order)
|
|
756
|
+
* from the worklet. Works in both ring and postMessage meter modes; rejects when the
|
|
757
|
+
* target is invalid or mixing is not compiled in.
|
|
758
|
+
*/
|
|
759
|
+
pollInsertGainReduction(targetId: number): Promise<number[]> {
|
|
760
|
+
if (this.destroyed) {
|
|
761
|
+
return Promise.reject(new Error('Realtime engine node is destroyed.'));
|
|
762
|
+
}
|
|
763
|
+
const requestId = this.insertGainReductionRequestId++;
|
|
764
|
+
let rejectRequest!: (reason?: unknown) => void;
|
|
765
|
+
const promise = new Promise<number[]>((resolve, reject) => {
|
|
766
|
+
rejectRequest = reject;
|
|
767
|
+
this.insertGainReductionRequests.set(requestId, { resolve, reject });
|
|
768
|
+
});
|
|
769
|
+
try {
|
|
770
|
+
this.node.port.postMessage({ type: 'insertGainReductionRequest', requestId, targetId });
|
|
771
|
+
} catch (error) {
|
|
772
|
+
this.insertGainReductionRequests.delete(requestId);
|
|
773
|
+
rejectRequest(error);
|
|
774
|
+
}
|
|
775
|
+
return promise;
|
|
776
|
+
}
|
|
777
|
+
|
|
731
778
|
private emitScope(scope: SonareWorkletScopeSnapshot): void {
|
|
732
779
|
for (const listener of this.scopeListeners) {
|
|
733
780
|
listener(scope);
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
resolveMetronomeConfig,
|
|
15
15
|
type SonareEngineCaptureRequestMessage,
|
|
16
16
|
type SonareEngineCaptureResponseMessageInternal,
|
|
17
|
+
type SonareEngineInsertGainReductionRequestMessage,
|
|
18
|
+
type SonareEngineInsertGainReductionResponseMessage,
|
|
17
19
|
type SonareEngineSyncMessage,
|
|
18
20
|
type SonareEngineTransportRequestMessage,
|
|
19
21
|
type SonareEngineTransportResponseMessage,
|
|
@@ -46,6 +48,7 @@ import {
|
|
|
46
48
|
type SonareEngineTelemetryRecord,
|
|
47
49
|
type SonareEngineTelemetryRingBuffer,
|
|
48
50
|
SonareEngineTelemetryType,
|
|
51
|
+
type SonareWorkletMeterSnapshot,
|
|
49
52
|
type SonareWorkletScopeSnapshot,
|
|
50
53
|
scopeRingFromSharedBuffer,
|
|
51
54
|
telemetryFromEngine,
|
|
@@ -682,6 +685,9 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
682
685
|
case 'syncTrackStripDualPan':
|
|
683
686
|
this.engine.setTrackStripDualPan(message.trackId, message.leftPan, message.rightPan);
|
|
684
687
|
break;
|
|
688
|
+
case 'syncTrackStripSurroundPan':
|
|
689
|
+
this.engine.setTrackStripSurroundPan(message.trackId, message.pan);
|
|
690
|
+
break;
|
|
685
691
|
case 'syncBusStripPan':
|
|
686
692
|
this.engine.setBusStripPan(message.busId, message.pan);
|
|
687
693
|
break;
|
|
@@ -774,7 +780,11 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
774
780
|
);
|
|
775
781
|
break;
|
|
776
782
|
case 'syncMidiUmp':
|
|
777
|
-
this.engine.pushMidiUmp(
|
|
783
|
+
this.engine.pushMidiUmp(
|
|
784
|
+
message.destinationId,
|
|
785
|
+
message.words ?? [message.word0],
|
|
786
|
+
message.renderFrame,
|
|
787
|
+
);
|
|
778
788
|
break;
|
|
779
789
|
case 'syncMidiSysex':
|
|
780
790
|
this.engine.pushMidiSysex(message.destinationId, message.data, message.renderFrame);
|
|
@@ -852,6 +862,9 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
852
862
|
message.portTimeSamples,
|
|
853
863
|
);
|
|
854
864
|
break;
|
|
865
|
+
case 'syncMidiInputUmp':
|
|
866
|
+
this.engine.pushMidiInputUmp(message.words, message.portTimeSamples);
|
|
867
|
+
break;
|
|
855
868
|
}
|
|
856
869
|
}
|
|
857
870
|
|
|
@@ -931,6 +944,27 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
931
944
|
}
|
|
932
945
|
}
|
|
933
946
|
|
|
947
|
+
receiveInsertGainReductionRequest(message: SonareEngineInsertGainReductionRequestMessage): void {
|
|
948
|
+
if (this.closed) {
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
try {
|
|
952
|
+
this.transport?.postMessage?.({
|
|
953
|
+
type: 'insertGainReductionResponse',
|
|
954
|
+
requestId: message.requestId,
|
|
955
|
+
ok: true,
|
|
956
|
+
values: this.engine.meterTargetInsertGainReduction(message.targetId),
|
|
957
|
+
} satisfies SonareEngineInsertGainReductionResponseMessage);
|
|
958
|
+
} catch (error) {
|
|
959
|
+
this.transport?.postMessage?.({
|
|
960
|
+
type: 'insertGainReductionResponse',
|
|
961
|
+
requestId: message.requestId,
|
|
962
|
+
ok: false,
|
|
963
|
+
error: error instanceof Error ? error.message : String(error),
|
|
964
|
+
} satisfies SonareEngineInsertGainReductionResponseMessage);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
934
968
|
destroy(): void {
|
|
935
969
|
if (!this.closed) {
|
|
936
970
|
this.clearPagedClipProviders();
|
|
@@ -1151,6 +1185,8 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
1151
1185
|
}
|
|
1152
1186
|
return;
|
|
1153
1187
|
}
|
|
1188
|
+
const delivered: SonareWorkletMeterSnapshot[] = [];
|
|
1189
|
+
const newestByTarget = new Map<number, SonareWorkletMeterSnapshot>();
|
|
1154
1190
|
for (const item of this.engine.drainMeterTelemetry(64)) {
|
|
1155
1191
|
const meter = meterFromEngine(item);
|
|
1156
1192
|
if (
|
|
@@ -1162,6 +1198,15 @@ export class SonareRealtimeEngineWorkletProcessor {
|
|
|
1162
1198
|
if (meter.frame !== this.lastMeterFrame) {
|
|
1163
1199
|
this.lastMeterFrame = meter.frame;
|
|
1164
1200
|
}
|
|
1201
|
+
delivered.push(meter);
|
|
1202
|
+
newestByTarget.set(meter.targetId, meter);
|
|
1203
|
+
}
|
|
1204
|
+
for (const meter of delivered) {
|
|
1205
|
+
// The board holds only the latest block, so only the newest record per
|
|
1206
|
+
// target in this drain can carry it; the ring layout is fixed and cannot.
|
|
1207
|
+
if (newestByTarget.get(meter.targetId) === meter) {
|
|
1208
|
+
meter.insertGainReductionDb = this.engine.meterTargetInsertGainReduction(meter.targetId);
|
|
1209
|
+
}
|
|
1165
1210
|
// The ring branch returned above, so this is the structured-clone
|
|
1166
1211
|
// fallback. `onMeter` and `postMessage` are alternative channels for the
|
|
1167
1212
|
// same record, not a broadcast pair: the AudioWorklet registration
|
|
@@ -5,6 +5,7 @@ import { SonareRealtimeEngineWorkletProcessor } from './engine-processor';
|
|
|
5
5
|
import {
|
|
6
6
|
isEngineCaptureRequestMessage,
|
|
7
7
|
isEngineCommandRecord,
|
|
8
|
+
isEngineInsertGainReductionRequestMessage,
|
|
8
9
|
isEngineSyncMessage,
|
|
9
10
|
isEngineTransportRequestMessage,
|
|
10
11
|
} from './guards';
|
|
@@ -82,6 +83,8 @@ export function registerSonareRealtimeEngineWorkletProcessor(
|
|
|
82
83
|
bridge.receiveCaptureRequest(data);
|
|
83
84
|
} else if (isEngineTransportRequestMessage(data)) {
|
|
84
85
|
bridge.receiveTransportRequest(data);
|
|
86
|
+
} else if (isEngineInsertGainReductionRequestMessage(data)) {
|
|
87
|
+
bridge.receiveInsertGainReductionRequest(data);
|
|
85
88
|
} else if (isRecord(data) && typeof data.type === 'string' && data.type.startsWith('sync')) {
|
|
86
89
|
// A sync the guard does not accept would otherwise vanish, leaving the
|
|
87
90
|
// live engine out of step with the offline mirror and nothing to see.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { panLawCode, panModeCode } from '../codes';
|
|
2
|
-
import type { EqBand, PanLawInput, PanMode, RealtimeEngine } from '../index';
|
|
2
|
+
import type { EqBand, PanLawInput, PanMode, RealtimeEngine, UmpWords } from '../index';
|
|
3
|
+
import type { SurroundPan } from '../public_types';
|
|
3
4
|
import type { InsertParamOverrideMap } from './engine-mixer-facade';
|
|
4
5
|
import {
|
|
5
6
|
emptyStripJson,
|
|
@@ -205,6 +206,26 @@ export function setTrackStripDualPan(
|
|
|
205
206
|
ctx.postSync({ type: 'syncTrackStripDualPan', trackId, leftPan, rightPan });
|
|
206
207
|
}
|
|
207
208
|
|
|
209
|
+
export function setTrackStripSurroundPan(
|
|
210
|
+
ctx: EngineStripContext,
|
|
211
|
+
target: string | number,
|
|
212
|
+
pan: SurroundPan,
|
|
213
|
+
): void {
|
|
214
|
+
const trackId = trackIdFor(ctx, target);
|
|
215
|
+
ctx.offlineEngine.setTrackStripSurroundPan(trackId, pan);
|
|
216
|
+
const resolved = {
|
|
217
|
+
azimuth: pan.azimuth ?? 0,
|
|
218
|
+
elevation: pan.elevation ?? 0,
|
|
219
|
+
divergence: pan.divergence ?? 0,
|
|
220
|
+
lfe: pan.lfe ?? 0,
|
|
221
|
+
distance: pan.distance === undefined || pan.distance <= 0 ? 1 : pan.distance,
|
|
222
|
+
};
|
|
223
|
+
mergeStripJson(ctx, { kind: 'track', trackId }, (entry) => {
|
|
224
|
+
entry.surroundPan = resolved;
|
|
225
|
+
});
|
|
226
|
+
ctx.postSync({ type: 'syncTrackStripSurroundPan', trackId, pan: resolved });
|
|
227
|
+
}
|
|
228
|
+
|
|
208
229
|
export function setTrackStripChannelDelaySamples(
|
|
209
230
|
ctx: EngineStripContext,
|
|
210
231
|
target: string | number,
|
|
@@ -499,12 +520,18 @@ export function pushMidiPolyPressure(
|
|
|
499
520
|
export function pushMidiUmp(
|
|
500
521
|
ctx: EngineStripContext,
|
|
501
522
|
trackId: string | number,
|
|
502
|
-
word0: number,
|
|
523
|
+
word0: number | UmpWords,
|
|
503
524
|
renderFrame: number,
|
|
504
525
|
): void {
|
|
505
526
|
const destinationId = ctx.resolveTargetId(trackId);
|
|
506
|
-
|
|
507
|
-
|
|
527
|
+
if (typeof word0 === 'number') {
|
|
528
|
+
ctx.offlineEngine.pushMidiUmp(destinationId, word0, renderFrame);
|
|
529
|
+
ctx.postSync({ type: 'syncMidiUmp', destinationId, word0, renderFrame });
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
const words = word0 instanceof Uint32Array ? new Uint32Array(word0) : word0.slice();
|
|
533
|
+
ctx.offlineEngine.pushMidiUmp(destinationId, words, renderFrame);
|
|
534
|
+
ctx.postSync({ type: 'syncMidiUmp', destinationId, words, renderFrame });
|
|
508
535
|
}
|
|
509
536
|
|
|
510
537
|
export function setBuiltinInstrument(
|
package/src/worklet/engine.ts
CHANGED
|
@@ -23,9 +23,11 @@ import type {
|
|
|
23
23
|
PanLaw,
|
|
24
24
|
PanMode,
|
|
25
25
|
SidechainSourceKind,
|
|
26
|
+
UmpWords,
|
|
26
27
|
} from '../index';
|
|
27
28
|
import { RealtimeEngine } from '../index';
|
|
28
29
|
import { createOpfsClipPageProvider, type OpfsClipPageProviderBinding } from '../opfs_clip_pages';
|
|
30
|
+
import type { SurroundPan } from '../public_types';
|
|
29
31
|
import type { ClipPageProvider } from '../realtime_engine';
|
|
30
32
|
import type { EngineAutomationContext } from './engine-automation';
|
|
31
33
|
import * as automation from './engine-automation';
|
|
@@ -505,7 +507,9 @@ export class SonareEngine {
|
|
|
505
507
|
/**
|
|
506
508
|
* Keys one insert of a lane strip from another lane's post-strip pre-fader
|
|
507
509
|
* audio (ducking/sidechainRouter inserts). sourceTarget null removes the
|
|
508
|
-
* binding.
|
|
510
|
+
* binding. Throws, leaving the existing bindings unchanged, for a lane keying
|
|
511
|
+
* itself or a binding that would close a cycle over the lane bindings; a
|
|
512
|
+
* refused binding is never recorded, so it is not replayed on resync.
|
|
509
513
|
*/
|
|
510
514
|
setLaneSidechain(
|
|
511
515
|
target: string | number,
|
|
@@ -611,6 +615,15 @@ export class SonareEngine {
|
|
|
611
615
|
strips.setTrackStripDualPan(this.stripContext, target, leftPan, rightPan);
|
|
612
616
|
}
|
|
613
617
|
|
|
618
|
+
/**
|
|
619
|
+
* Sets a track lane strip's surround placement (glitch-free, 5 ms placement
|
|
620
|
+
* glide). Takes effect on a destination wider than stereo, so set `channelCount`
|
|
621
|
+
* to the layout's plane count. Omitted fields default to a centered point source.
|
|
622
|
+
*/
|
|
623
|
+
setTrackStripSurroundPan(target: string | number, pan: SurroundPan): void {
|
|
624
|
+
strips.setTrackStripSurroundPan(this.stripContext, target, pan);
|
|
625
|
+
}
|
|
626
|
+
|
|
614
627
|
setTrackStripChannelDelaySamples(target: string | number, delaySamples: number): void {
|
|
615
628
|
strips.setTrackStripChannelDelaySamples(this.stripContext, target, delaySamples);
|
|
616
629
|
}
|
|
@@ -1031,8 +1044,8 @@ export class SonareEngine {
|
|
|
1031
1044
|
);
|
|
1032
1045
|
}
|
|
1033
1046
|
|
|
1034
|
-
pushMidiUmp(trackId: string | number,
|
|
1035
|
-
strips.pushMidiUmp(this.stripContext, trackId,
|
|
1047
|
+
pushMidiUmp(trackId: string | number, words: number | UmpWords, renderFrame = -1): void {
|
|
1048
|
+
strips.pushMidiUmp(this.stripContext, trackId, words, renderFrame);
|
|
1036
1049
|
}
|
|
1037
1050
|
|
|
1038
1051
|
pushMidiSysex(trackId: string | number, data: Uint8Array, renderFrame = -1): void {
|
|
@@ -1167,6 +1180,12 @@ export class SonareEngine {
|
|
|
1167
1180
|
});
|
|
1168
1181
|
}
|
|
1169
1182
|
|
|
1183
|
+
pushMidiInputUmp(words: UmpWords, portTimeSamples = 0): void {
|
|
1184
|
+
const copiedWords = words instanceof Uint32Array ? new Uint32Array(words) : words.slice();
|
|
1185
|
+
this.offlineEngine.pushMidiInputUmp(copiedWords, portTimeSamples);
|
|
1186
|
+
this.postSync({ type: 'syncMidiInputUmp', words: copiedWords, portTimeSamples });
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1170
1189
|
pushMidiPanic(renderFrame = -1): void {
|
|
1171
1190
|
this.offlineEngine.pushMidiPanic(renderFrame);
|
|
1172
1191
|
this.postSync({ type: 'syncMidiPanic', renderFrame });
|
|
@@ -1288,6 +1307,11 @@ export class SonareEngine {
|
|
|
1288
1307
|
return this.realtimeNode.pollScope();
|
|
1289
1308
|
}
|
|
1290
1309
|
|
|
1310
|
+
/** Per-insert gain reduction for a meter target; see {@link SonareRealtimeEngineNode.pollInsertGainReduction}. */
|
|
1311
|
+
pollInsertGainReduction(targetId: number): Promise<number[]> {
|
|
1312
|
+
return this.realtimeNode.pollInsertGainReduction(targetId);
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1291
1315
|
destroy(): void {
|
|
1292
1316
|
if (this.destroyed) {
|
|
1293
1317
|
return;
|
package/src/worklet/guards.ts
CHANGED
|
@@ -3,6 +3,8 @@ import type {
|
|
|
3
3
|
SonareEngineCaptureRequestMessage,
|
|
4
4
|
SonareEngineCaptureResponseMessageInternal,
|
|
5
5
|
SonareEngineClipPageRequestMessage,
|
|
6
|
+
SonareEngineInsertGainReductionRequestMessage,
|
|
7
|
+
SonareEngineInsertGainReductionResponseMessage,
|
|
6
8
|
SonareEngineSyncMessage,
|
|
7
9
|
SonareEngineTransportRequestMessage,
|
|
8
10
|
SonareEngineTransportResponseMessage,
|
|
@@ -86,6 +88,7 @@ export const ENGINE_SYNC_MESSAGE_TYPES: Record<SonareEngineSyncMessage['type'],
|
|
|
86
88
|
syncMidiInputPitchBend: true,
|
|
87
89
|
syncMidiInputPolyPressure: true,
|
|
88
90
|
syncMidiInputSource: true,
|
|
91
|
+
syncMidiInputUmp: true,
|
|
89
92
|
syncMidiNoteOff: true,
|
|
90
93
|
syncMidiNoteOn: true,
|
|
91
94
|
syncMidiPanic: true,
|
|
@@ -106,6 +109,7 @@ export const ENGINE_SYNC_MESSAGE_TYPES: Record<SonareEngineSyncMessage['type'],
|
|
|
106
109
|
syncTrackStripPan: true,
|
|
107
110
|
syncTrackStripPanLaw: true,
|
|
108
111
|
syncTrackStripPanMode: true,
|
|
112
|
+
syncTrackStripSurroundPan: true,
|
|
109
113
|
syncWarpVoiceCapacity: true,
|
|
110
114
|
};
|
|
111
115
|
|
|
@@ -254,6 +258,28 @@ export function isEngineTransportResponseMessage(
|
|
|
254
258
|
);
|
|
255
259
|
}
|
|
256
260
|
|
|
261
|
+
export function isEngineInsertGainReductionRequestMessage(
|
|
262
|
+
value: unknown,
|
|
263
|
+
): value is SonareEngineInsertGainReductionRequestMessage {
|
|
264
|
+
return (
|
|
265
|
+
isRecord(value) &&
|
|
266
|
+
value.type === 'insertGainReductionRequest' &&
|
|
267
|
+
typeof value.requestId === 'number' &&
|
|
268
|
+
typeof value.targetId === 'number'
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function isEngineInsertGainReductionResponseMessage(
|
|
273
|
+
value: unknown,
|
|
274
|
+
): value is SonareEngineInsertGainReductionResponseMessage {
|
|
275
|
+
return (
|
|
276
|
+
isRecord(value) &&
|
|
277
|
+
value.type === 'insertGainReductionResponse' &&
|
|
278
|
+
typeof value.requestId === 'number' &&
|
|
279
|
+
typeof value.ok === 'boolean'
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
257
283
|
export function isRealtimeVoiceChangerMessage(
|
|
258
284
|
value: unknown,
|
|
259
285
|
): value is SonareRealtimeVoiceChangerMessage {
|
package/src/worklet/messages.ts
CHANGED
|
@@ -15,8 +15,9 @@ import type {
|
|
|
15
15
|
PlaybackRendererConfig,
|
|
16
16
|
RealtimeVoiceChangerConfigInput,
|
|
17
17
|
RealtimeVoiceChangerPodConfig,
|
|
18
|
+
UmpWords,
|
|
18
19
|
} from '../index';
|
|
19
|
-
import type { AutomationCurve } from '../public_types';
|
|
20
|
+
import type { AutomationCurve, SurroundPan } from '../public_types';
|
|
20
21
|
import type {
|
|
21
22
|
SonareEngineCommandRecord,
|
|
22
23
|
SonareEngineTelemetryRecord,
|
|
@@ -45,6 +46,18 @@ export interface SonareRealtimeEngineWorkletProcessorOptions {
|
|
|
45
46
|
initialCommands?: SonareEngineCommandRecord[];
|
|
46
47
|
sampleRate?: number;
|
|
47
48
|
blockSize?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Output plane count (default 2). Planes follow the canonical order
|
|
51
|
+
* L R C LFE Ls Rs [Lss Rss] for 6 (5.1) and 8 (7.1) planes.
|
|
52
|
+
*
|
|
53
|
+
* The Web Audio default destination is 2-channel `'speakers'`, so a wider
|
|
54
|
+
* output is mixed down to stereo unless the app raises
|
|
55
|
+
* `audioContext.destination.channelCount` (up to its `maxChannelCount`) to
|
|
56
|
+
* match. Interpretation matters only while the counts differ: `'speakers'`
|
|
57
|
+
* applies the spec's down-mix for mono, stereo, quad and 5.1 sources only,
|
|
58
|
+
* and `'discrete'` copies the first planes and drops the rest. With equal
|
|
59
|
+
* counts the planes pass through unchanged in either mode.
|
|
60
|
+
*/
|
|
48
61
|
channelCount?: number;
|
|
49
62
|
meterIntervalFrames?: number;
|
|
50
63
|
commandSharedBuffer?: SharedArrayBuffer;
|
|
@@ -301,6 +314,7 @@ export interface WorkletTransport {
|
|
|
301
314
|
| SonareWorkletTransportMessage
|
|
302
315
|
| SonareEngineCaptureResponseMessageInternal
|
|
303
316
|
| SonareEngineTransportResponseMessage
|
|
317
|
+
| SonareEngineInsertGainReductionResponseMessage
|
|
304
318
|
| SonareEngineSyncErrorMessage,
|
|
305
319
|
transfer?: Transferable[],
|
|
306
320
|
) => void;
|
|
@@ -633,6 +647,12 @@ export interface SonareEngineSyncTrackStripDualPanMessage {
|
|
|
633
647
|
rightPan: number;
|
|
634
648
|
}
|
|
635
649
|
|
|
650
|
+
export interface SonareEngineSyncTrackStripSurroundPanMessage {
|
|
651
|
+
type: 'syncTrackStripSurroundPan';
|
|
652
|
+
trackId: number;
|
|
653
|
+
pan: Required<SurroundPan>;
|
|
654
|
+
}
|
|
655
|
+
|
|
636
656
|
export interface SonareEngineSyncBusStripPanMessage {
|
|
637
657
|
type: 'syncBusStripPan';
|
|
638
658
|
busId: number;
|
|
@@ -721,13 +741,26 @@ export interface SonareEngineSyncMidiCcMessage {
|
|
|
721
741
|
renderFrame: number;
|
|
722
742
|
}
|
|
723
743
|
|
|
724
|
-
export interface
|
|
744
|
+
export interface SonareEngineSyncMidiUmpWordMessage {
|
|
725
745
|
type: 'syncMidiUmp';
|
|
726
746
|
destinationId: number;
|
|
727
747
|
word0: number;
|
|
748
|
+
words?: never;
|
|
728
749
|
renderFrame: number;
|
|
729
750
|
}
|
|
730
751
|
|
|
752
|
+
export interface SonareEngineSyncMidiUmpWordsMessage {
|
|
753
|
+
type: 'syncMidiUmp';
|
|
754
|
+
destinationId: number;
|
|
755
|
+
word0?: never;
|
|
756
|
+
words: UmpWords;
|
|
757
|
+
renderFrame: number;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
export type SonareEngineSyncMidiUmpMessage =
|
|
761
|
+
| SonareEngineSyncMidiUmpWordMessage
|
|
762
|
+
| SonareEngineSyncMidiUmpWordsMessage;
|
|
763
|
+
|
|
731
764
|
/**
|
|
732
765
|
* One of the three per-note expression dimensions, addressed to a destination.
|
|
733
766
|
* `data0`/`data1` carry the dimension's own payload the way the live-input
|
|
@@ -796,6 +829,12 @@ export interface SonareEngineSyncMidiInputEventMessage {
|
|
|
796
829
|
portTimeSamples: number;
|
|
797
830
|
}
|
|
798
831
|
|
|
832
|
+
export interface SonareEngineSyncMidiInputUmpMessage {
|
|
833
|
+
type: 'syncMidiInputUmp';
|
|
834
|
+
words: UmpWords;
|
|
835
|
+
portTimeSamples: number;
|
|
836
|
+
}
|
|
837
|
+
|
|
799
838
|
/** Releases the realtime engine and all worklet-owned clip buffers. */
|
|
800
839
|
export interface SonareEngineDestroyMessage {
|
|
801
840
|
type: 'destroy';
|
|
@@ -839,6 +878,7 @@ export type SonareEngineSyncMessage =
|
|
|
839
878
|
| SonareEngineSyncTrackStripPanLawMessage
|
|
840
879
|
| SonareEngineSyncTrackStripPanModeMessage
|
|
841
880
|
| SonareEngineSyncTrackStripDualPanMessage
|
|
881
|
+
| SonareEngineSyncTrackStripSurroundPanMessage
|
|
842
882
|
| SonareEngineSyncBusStripPanMessage
|
|
843
883
|
| SonareEngineSyncBusStripPanLawMessage
|
|
844
884
|
| SonareEngineSyncBusStripPanModeMessage
|
|
@@ -860,6 +900,7 @@ export type SonareEngineSyncMessage =
|
|
|
860
900
|
| SonareEngineSyncMidiInputSourceMessage
|
|
861
901
|
| SonareEngineSyncMidiCcBindingMessage
|
|
862
902
|
| SonareEngineSyncMidiInputEventMessage
|
|
903
|
+
| SonareEngineSyncMidiInputUmpMessage
|
|
863
904
|
| SonareEngineDestroyMessage;
|
|
864
905
|
|
|
865
906
|
export interface WorkletPort {
|
|
@@ -937,3 +978,19 @@ export interface SonareEngineTransportResponseMessage {
|
|
|
937
978
|
state?: EngineTransportState;
|
|
938
979
|
error?: string;
|
|
939
980
|
}
|
|
981
|
+
|
|
982
|
+
/** Asks the worklet for one target's per-insert gain reduction (any meter delivery mode). */
|
|
983
|
+
export interface SonareEngineInsertGainReductionRequestMessage {
|
|
984
|
+
type: 'insertGainReductionRequest';
|
|
985
|
+
requestId: number;
|
|
986
|
+
targetId: number;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/** Reply to {@link SonareEngineInsertGainReductionRequestMessage}; `values` is present when `ok`. */
|
|
990
|
+
export interface SonareEngineInsertGainReductionResponseMessage {
|
|
991
|
+
type: 'insertGainReductionResponse';
|
|
992
|
+
requestId: number;
|
|
993
|
+
ok: boolean;
|
|
994
|
+
values?: number[];
|
|
995
|
+
error?: string;
|
|
996
|
+
}
|
package/src/worklet/protocol.ts
CHANGED
|
@@ -45,6 +45,16 @@ export interface SonareWorkletMeterSnapshot {
|
|
|
45
45
|
inputPeakDbL: number;
|
|
46
46
|
/** Maximum pre-trim input peak for the right channel, in dBFS. */
|
|
47
47
|
inputPeakDbR: number;
|
|
48
|
+
/**
|
|
49
|
+
* Per-insert gain reduction in dB (each <= 0) of the target's strip, in combined
|
|
50
|
+
* pre-to-post insert order, folded to the deepest value across the sub-blocks of the
|
|
51
|
+
* latest committed host block. It describes that latest block as of the drain, not
|
|
52
|
+
* necessarily this record's own block, so it is attached only to the newest record per
|
|
53
|
+
* target within one drain; older backlogged records omit it. Present only on snapshots
|
|
54
|
+
* delivered over postMessage; the SharedArrayBuffer meter ring has a fixed layout that
|
|
55
|
+
* does not carry it, so ring hosts read it with `pollInsertGainReduction(targetId)`.
|
|
56
|
+
*/
|
|
57
|
+
insertGainReductionDb?: number[];
|
|
48
58
|
}
|
|
49
59
|
|
|
50
60
|
export interface SonareWorkletSpectrumSnapshot {
|
package/src/worklet.ts
CHANGED
|
@@ -24,6 +24,8 @@ export type {
|
|
|
24
24
|
SonareEngineCaptureRequestMessage,
|
|
25
25
|
SonareEngineCaptureResponseMessage,
|
|
26
26
|
SonareEngineClipPageRequestMessage,
|
|
27
|
+
SonareEngineInsertGainReductionRequestMessage,
|
|
28
|
+
SonareEngineInsertGainReductionResponseMessage,
|
|
27
29
|
SonareEngineSyncAutomationMessage,
|
|
28
30
|
SonareEngineSyncBuiltinInstrumentMessage,
|
|
29
31
|
SonareEngineSyncBusStripEqBandMessage,
|