@libraz/libsonare 1.4.1 → 1.5.1
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 +51 -20
- package/dist/index.d.ts +5416 -1
- package/dist/index.js +938 -583
- package/dist/index.js.map +1 -1
- package/dist/sonare.js +2 -2
- package/dist/sonare.wasm +0 -0
- package/dist/worklet.d.ts +1083 -5227
- package/dist/worklet.js +2683 -2451
- package/dist/worklet.js.map +1 -1
- package/package.json +4 -9
- package/src/clip_page_streamer.ts +298 -0
- package/src/effects_mastering.ts +85 -1089
- package/src/effects_transform.ts +286 -0
- package/src/effects_voice_change.ts +118 -0
- package/src/feature_music.ts +5 -2
- package/src/feature_spectrogram.ts +42 -2
- package/src/features.ts +1 -0
- package/src/index.ts +23 -0
- package/src/mastering_chain.ts +200 -0
- package/src/mastering_core.ts +248 -0
- package/src/mastering_dynamics.ts +105 -0
- package/src/mastering_repair.ts +161 -0
- package/src/mixer.ts +8 -0
- package/src/mixing_oneshot.ts +54 -0
- package/src/module_state.ts +1 -2
- package/src/project.ts +71 -1712
- package/src/project_class.ts +871 -0
- package/src/project_internal.ts +333 -0
- package/src/project_synth.ts +43 -0
- package/src/project_types.ts +570 -0
- package/src/public_types.ts +6 -1221
- package/src/public_types_acoustic.ts +115 -0
- package/src/public_types_mastering.ts +333 -0
- package/src/public_types_mixing.ts +97 -0
- package/src/public_types_music.ts +352 -0
- package/src/public_types_realtime.ts +163 -0
- package/src/public_types_spectral.ts +194 -0
- package/src/realtime_engine.ts +94 -0
- package/src/sonare.js.d.ts +117 -38
- package/src/stream_analyzer.ts +3 -0
- package/src/stream_types.ts +4 -0
- package/src/worklet/engine-automation.ts +73 -0
- package/src/worklet/engine-capture-facade.ts +80 -0
- package/src/worklet/engine-clips.ts +71 -0
- package/src/worklet/engine-markers.ts +93 -0
- package/src/worklet/engine-mixer-facade.ts +186 -0
- package/src/worklet/engine-node.ts +451 -0
- package/src/worklet/engine-offline.ts +162 -0
- package/src/worklet/engine-options.ts +13 -0
- package/src/worklet/engine-parameter-facade.ts +172 -0
- package/src/worklet/engine-processor.ts +764 -0
- package/src/worklet/engine-register.ts +136 -0
- package/src/worklet/engine-strips.ts +315 -0
- package/src/worklet/engine-sync.ts +94 -0
- package/src/worklet/engine-tempo-facade.ts +141 -0
- package/src/worklet/engine.ts +998 -0
- package/src/worklet/guards.ts +14 -1
- package/src/worklet/messages.ts +60 -20
- package/src/worklet/mixer-processor.ts +368 -0
- package/src/worklet/protocol.ts +3 -0
- package/src/worklet/voice-changer-processor.ts +246 -0
- package/src/worklet.ts +20 -3549
- package/dist/sonare-rt-module.js +0 -2
- package/dist/sonare-rt.js +0 -2
- package/dist/sonare-rt.wasm +0 -0
- package/src/sonare-rt.d.ts +0 -93
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { EngineMarker, RealtimeEngine } from '../index';
|
|
2
|
+
import { resolveMarkerSet } from './engine-offline';
|
|
3
|
+
import type { SonareEngineSyncMessage } from './messages';
|
|
4
|
+
import { type SonareEngineCommandRecord, SonareEngineCommandType } from './protocol';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Collaborator surface the marker helpers need from the owning
|
|
8
|
+
* {@link SonareEngine}: the marker store and id counter, the offline engine they
|
|
9
|
+
* mirror into, the sync poster, the realtime command sender, and the loop setter.
|
|
10
|
+
*/
|
|
11
|
+
export interface EngineMarkerContext {
|
|
12
|
+
readonly offlineEngine: RealtimeEngine;
|
|
13
|
+
readonly markers: Map<number, EngineMarker>;
|
|
14
|
+
getNextMarkerId(): number;
|
|
15
|
+
setNextMarkerId(value: number): void;
|
|
16
|
+
postSync(message: SonareEngineSyncMessage): void;
|
|
17
|
+
sendCommand(command: SonareEngineCommandRecord): boolean;
|
|
18
|
+
setLoop(startPpq: number, endPpq: number, enabled: boolean): boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function addMarker(ctx: EngineMarkerContext, ppq: number, name = ''): number {
|
|
22
|
+
const id = ctx.getNextMarkerId();
|
|
23
|
+
ctx.setNextMarkerId(id + 1);
|
|
24
|
+
ctx.markers.set(id, { id, ppq, name });
|
|
25
|
+
syncMarkers(ctx);
|
|
26
|
+
return id;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Replaces the whole marker set in one call.
|
|
31
|
+
*
|
|
32
|
+
* Entries without an `id` are assigned fresh ids; entries carrying an `id`
|
|
33
|
+
* keep it (ids must be positive and unique within the list). Returns the
|
|
34
|
+
* resolved markers in the order given, so a caller can map its own marker
|
|
35
|
+
* identities to the engine ids used by `seekMarker`/`setLoopFromMarkers`.
|
|
36
|
+
*
|
|
37
|
+
* @param markers The full marker list (an empty list clears all markers).
|
|
38
|
+
* @returns The markers with their resolved engine ids.
|
|
39
|
+
*/
|
|
40
|
+
export function setMarkers(
|
|
41
|
+
ctx: EngineMarkerContext,
|
|
42
|
+
markers: ReadonlyArray<{ ppq: number; name?: string; id?: number }>,
|
|
43
|
+
): EngineMarker[] {
|
|
44
|
+
const { resolved, nextMarkerId } = resolveMarkerSet(markers, ctx.getNextMarkerId());
|
|
45
|
+
ctx.setNextMarkerId(nextMarkerId);
|
|
46
|
+
ctx.markers.clear();
|
|
47
|
+
for (const marker of resolved) {
|
|
48
|
+
ctx.markers.set(marker.id, marker);
|
|
49
|
+
}
|
|
50
|
+
syncMarkers(ctx);
|
|
51
|
+
return resolved.map((marker) => ({ ...marker }));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function markerCount(ctx: EngineMarkerContext): number {
|
|
55
|
+
return ctx.offlineEngine.markerCount();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function markerByIndex(ctx: EngineMarkerContext, index: number): EngineMarker {
|
|
59
|
+
return ctx.offlineEngine.markerByIndex(index);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function marker(ctx: EngineMarkerContext, markerId: number): EngineMarker {
|
|
63
|
+
return ctx.offlineEngine.marker(markerId);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function seekMarker(ctx: EngineMarkerContext, markerId: number): boolean {
|
|
67
|
+
ctx.offlineEngine.seekMarker(markerId);
|
|
68
|
+
// Forward to the live worklet engine. Its marker set is kept in sync via the
|
|
69
|
+
// 'syncMarkers' message (see syncMarkers), so a queued kSeekMarker resolves
|
|
70
|
+
// the marker id to its frame on the audio thread.
|
|
71
|
+
return ctx.sendCommand({
|
|
72
|
+
type: SonareEngineCommandType.SeekMarker,
|
|
73
|
+
targetId: markerId,
|
|
74
|
+
sampleTime: -1,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function setLoopFromMarkers(
|
|
79
|
+
ctx: EngineMarkerContext,
|
|
80
|
+
startMarkerId: number,
|
|
81
|
+
endMarkerId: number,
|
|
82
|
+
): boolean {
|
|
83
|
+
ctx.offlineEngine.setLoopFromMarkers(startMarkerId, endMarkerId);
|
|
84
|
+
const start = ctx.offlineEngine.marker(startMarkerId);
|
|
85
|
+
const end = ctx.offlineEngine.marker(endMarkerId);
|
|
86
|
+
return ctx.setLoop(start.ppq, end.ppq, true);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function syncMarkers(ctx: EngineMarkerContext): void {
|
|
90
|
+
const markers = Array.from(ctx.markers.values()).sort((a, b) => a.ppq - b.ppq);
|
|
91
|
+
ctx.offlineEngine.setMarkers(markers);
|
|
92
|
+
ctx.postSync({ type: 'syncMarkers', markers });
|
|
93
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { EngineBus, EngineTrackLane, EngineTrackSend, RealtimeEngine } from '../index';
|
|
2
|
+
import { normalizeTrackLanes } from './engine-offline';
|
|
3
|
+
import { buildMixerLanes } from './engine-sync';
|
|
4
|
+
import type { SonareEngineSyncMessage } from './messages';
|
|
5
|
+
import { ENGINE_MIXER_PARAM_FADER_DB, engineMixerBusTarget } from './protocol';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Collaborator surface the mixer/routing setters need from the owning
|
|
9
|
+
* {@link SonareEngine}: the offline engine they mirror writes into, the mutable
|
|
10
|
+
* routing stores (held by reference), the out-of-band sync poster, and the
|
|
11
|
+
* lane/bus declaration and mixer-sync helpers.
|
|
12
|
+
*/
|
|
13
|
+
export interface EngineMixerContext {
|
|
14
|
+
readonly offlineEngine: RealtimeEngine;
|
|
15
|
+
readonly trackLaneIds: number[];
|
|
16
|
+
readonly trackSends: Map<number, EngineTrackSend[]>;
|
|
17
|
+
readonly trackOutputBus: Map<number, number>;
|
|
18
|
+
readonly laneSidechains: Map<
|
|
19
|
+
string,
|
|
20
|
+
{ trackId: number; insertIndex: number; sourceTrackId: number }
|
|
21
|
+
>;
|
|
22
|
+
readonly buses: EngineBus[];
|
|
23
|
+
readonly trackStripJson: Map<number, string>;
|
|
24
|
+
readonly busStripJson: Map<number, string>;
|
|
25
|
+
postSync(message: SonareEngineSyncMessage): void;
|
|
26
|
+
ensureTrackLane(target: string | number): number;
|
|
27
|
+
ensureBus(busId: number): number;
|
|
28
|
+
mixerLanes(): EngineTrackLane[];
|
|
29
|
+
syncMixer(): void;
|
|
30
|
+
sendSmoothedParam(paramId: number, value: number): boolean;
|
|
31
|
+
getMasterStripJson(): string | undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Builds the engine's track-lane descriptors from the current routing stores. */
|
|
35
|
+
export function mixerLanes(ctx: EngineMixerContext): EngineTrackLane[] {
|
|
36
|
+
return buildMixerLanes(ctx.trackLaneIds, ctx.trackSends, ctx.trackOutputBus);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Mirrors the current mixer routing into the offline engine and posts the full
|
|
41
|
+
* mixer-sync message (lanes, buses, strip JSON, sidechains) to the worklet.
|
|
42
|
+
*/
|
|
43
|
+
export function syncMixer(ctx: EngineMixerContext): void {
|
|
44
|
+
const lanes = mixerLanes(ctx);
|
|
45
|
+
const buses = ctx.buses.map((bus) => ({ ...bus }));
|
|
46
|
+
ctx.offlineEngine.setTrackBuses(buses);
|
|
47
|
+
if (lanes.length > 0) {
|
|
48
|
+
ctx.offlineEngine.setTrackLanes(lanes);
|
|
49
|
+
}
|
|
50
|
+
const trackStrips = Array.from(ctx.trackStripJson, ([trackId, sceneJson]) => ({
|
|
51
|
+
trackId,
|
|
52
|
+
sceneJson,
|
|
53
|
+
}));
|
|
54
|
+
const busStrips = Array.from(ctx.busStripJson, ([busId, sceneJson]) => ({
|
|
55
|
+
busId,
|
|
56
|
+
sceneJson,
|
|
57
|
+
}));
|
|
58
|
+
ctx.postSync({
|
|
59
|
+
type: 'syncMixer',
|
|
60
|
+
lanes,
|
|
61
|
+
buses,
|
|
62
|
+
trackStrips,
|
|
63
|
+
laneSidechains: Array.from(ctx.laneSidechains.values()),
|
|
64
|
+
busStrips,
|
|
65
|
+
masterStripJson: ctx.getMasterStripJson(),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Declares the mixer track lanes in an explicit order.
|
|
71
|
+
*
|
|
72
|
+
* Lane indices are append-only: once a track id occupies a lane, its index
|
|
73
|
+
* stays fixed for the engine's lifetime. The given list must therefore start
|
|
74
|
+
* with the already-declared lane ids in their current order and may only
|
|
75
|
+
* append new track ids after them. Entries carrying `sends` replace that
|
|
76
|
+
* track's send list; entries without `sends` leave existing sends untouched.
|
|
77
|
+
*
|
|
78
|
+
* @param lanes Track ids or lane descriptors in the desired lane order.
|
|
79
|
+
*/
|
|
80
|
+
export function setTrackLanes(
|
|
81
|
+
ctx: EngineMixerContext,
|
|
82
|
+
lanes: ReadonlyArray<number | EngineTrackLane>,
|
|
83
|
+
): void {
|
|
84
|
+
const { entries, ids } = normalizeTrackLanes(ctx.trackLaneIds, lanes);
|
|
85
|
+
for (const entry of entries) {
|
|
86
|
+
if (entry.sends) {
|
|
87
|
+
ctx.trackSends.set(
|
|
88
|
+
entry.trackId,
|
|
89
|
+
entry.sends.map((send) => ({ ...send })),
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
if (entry.outputBusId !== undefined) {
|
|
93
|
+
if (entry.outputBusId === 0) {
|
|
94
|
+
ctx.trackOutputBus.delete(entry.trackId);
|
|
95
|
+
} else {
|
|
96
|
+
ctx.trackOutputBus.set(entry.trackId, entry.outputBusId);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
ctx.trackLaneIds.splice(0, ctx.trackLaneIds.length, ...ids);
|
|
101
|
+
ctx.syncMixer();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Routes a track lane's post-fader output into a declared bus instead of
|
|
106
|
+
* the master mix (group/folder routing); busId 0 restores the master mix.
|
|
107
|
+
*/
|
|
108
|
+
export function setTrackOutputBus(
|
|
109
|
+
ctx: EngineMixerContext,
|
|
110
|
+
target: string | number,
|
|
111
|
+
busId: number,
|
|
112
|
+
): void {
|
|
113
|
+
const laneIndex = ctx.ensureTrackLane(target);
|
|
114
|
+
const trackId = ctx.trackLaneIds[laneIndex];
|
|
115
|
+
if (busId === 0) {
|
|
116
|
+
ctx.trackOutputBus.delete(trackId);
|
|
117
|
+
} else {
|
|
118
|
+
ctx.trackOutputBus.set(trackId, busId);
|
|
119
|
+
}
|
|
120
|
+
ctx.syncMixer();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Keys one insert of a lane strip from another lane's post-strip pre-fader
|
|
125
|
+
* audio (ducking/sidechainRouter inserts). sourceTarget null removes the
|
|
126
|
+
* binding.
|
|
127
|
+
*/
|
|
128
|
+
export function setLaneSidechain(
|
|
129
|
+
ctx: EngineMixerContext,
|
|
130
|
+
target: string | number,
|
|
131
|
+
insertIndex: number,
|
|
132
|
+
sourceTarget: string | number | null,
|
|
133
|
+
): void {
|
|
134
|
+
const laneIndex = ctx.ensureTrackLane(target);
|
|
135
|
+
const trackId = ctx.trackLaneIds[laneIndex];
|
|
136
|
+
const key = `${trackId}:${insertIndex}`;
|
|
137
|
+
let sourceTrackId = 0;
|
|
138
|
+
if (sourceTarget !== null) {
|
|
139
|
+
const sourceIndex = ctx.ensureTrackLane(sourceTarget);
|
|
140
|
+
sourceTrackId = ctx.trackLaneIds[sourceIndex];
|
|
141
|
+
}
|
|
142
|
+
if (sourceTrackId === 0) {
|
|
143
|
+
ctx.laneSidechains.delete(key);
|
|
144
|
+
} else {
|
|
145
|
+
ctx.laneSidechains.set(key, { trackId, insertIndex, sourceTrackId });
|
|
146
|
+
}
|
|
147
|
+
ctx.offlineEngine.setLaneSidechain(trackId, insertIndex, sourceTrackId);
|
|
148
|
+
ctx.postSync({
|
|
149
|
+
type: 'syncMixer',
|
|
150
|
+
lanes: ctx.mixerLanes(),
|
|
151
|
+
laneSidechains: [{ trackId, insertIndex, sourceTrackId }],
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function setSends(
|
|
156
|
+
ctx: EngineMixerContext,
|
|
157
|
+
target: string | number,
|
|
158
|
+
sends: EngineTrackSend[],
|
|
159
|
+
): void {
|
|
160
|
+
const laneIndex = ctx.ensureTrackLane(target);
|
|
161
|
+
const trackId = ctx.trackLaneIds[laneIndex];
|
|
162
|
+
ctx.trackSends.set(
|
|
163
|
+
trackId,
|
|
164
|
+
sends.map((send) => ({ ...send })),
|
|
165
|
+
);
|
|
166
|
+
ctx.syncMixer();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function setTrackBuses(ctx: EngineMixerContext, buses: EngineBus[]): void {
|
|
170
|
+
ctx.buses.splice(0, ctx.buses.length, ...buses.map((bus) => ({ ...bus })));
|
|
171
|
+
ctx.syncMixer();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function setBusGain(ctx: EngineMixerContext, busId: number, db: number): boolean {
|
|
175
|
+
const busIndex = ctx.ensureBus(busId);
|
|
176
|
+
ctx.buses[busIndex] = { ...ctx.buses[busIndex], busId, gainDb: db };
|
|
177
|
+
ctx.offlineEngine.setTrackBuses(ctx.buses);
|
|
178
|
+
return ctx.sendSmoothedParam(engineMixerBusTarget(busIndex, ENGINE_MIXER_PARAM_FADER_DB), db);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function setBusStripJson(ctx: EngineMixerContext, busId: number, sceneJson: string): void {
|
|
182
|
+
ctx.ensureBus(busId);
|
|
183
|
+
ctx.offlineEngine.setBusStripJson(busId, sceneJson);
|
|
184
|
+
ctx.busStripJson.set(busId, sceneJson);
|
|
185
|
+
ctx.syncMixer();
|
|
186
|
+
}
|