@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.
Files changed (66) hide show
  1. package/README.md +51 -20
  2. package/dist/index.d.ts +5416 -1
  3. package/dist/index.js +938 -583
  4. package/dist/index.js.map +1 -1
  5. package/dist/sonare.js +2 -2
  6. package/dist/sonare.wasm +0 -0
  7. package/dist/worklet.d.ts +1083 -5227
  8. package/dist/worklet.js +2683 -2451
  9. package/dist/worklet.js.map +1 -1
  10. package/package.json +4 -9
  11. package/src/clip_page_streamer.ts +298 -0
  12. package/src/effects_mastering.ts +85 -1089
  13. package/src/effects_transform.ts +286 -0
  14. package/src/effects_voice_change.ts +118 -0
  15. package/src/feature_music.ts +5 -2
  16. package/src/feature_spectrogram.ts +42 -2
  17. package/src/features.ts +1 -0
  18. package/src/index.ts +23 -0
  19. package/src/mastering_chain.ts +200 -0
  20. package/src/mastering_core.ts +248 -0
  21. package/src/mastering_dynamics.ts +105 -0
  22. package/src/mastering_repair.ts +161 -0
  23. package/src/mixer.ts +8 -0
  24. package/src/mixing_oneshot.ts +54 -0
  25. package/src/module_state.ts +1 -2
  26. package/src/project.ts +71 -1712
  27. package/src/project_class.ts +871 -0
  28. package/src/project_internal.ts +333 -0
  29. package/src/project_synth.ts +43 -0
  30. package/src/project_types.ts +570 -0
  31. package/src/public_types.ts +6 -1221
  32. package/src/public_types_acoustic.ts +115 -0
  33. package/src/public_types_mastering.ts +333 -0
  34. package/src/public_types_mixing.ts +97 -0
  35. package/src/public_types_music.ts +352 -0
  36. package/src/public_types_realtime.ts +163 -0
  37. package/src/public_types_spectral.ts +194 -0
  38. package/src/realtime_engine.ts +94 -0
  39. package/src/sonare.js.d.ts +117 -38
  40. package/src/stream_analyzer.ts +3 -0
  41. package/src/stream_types.ts +4 -0
  42. package/src/worklet/engine-automation.ts +73 -0
  43. package/src/worklet/engine-capture-facade.ts +80 -0
  44. package/src/worklet/engine-clips.ts +71 -0
  45. package/src/worklet/engine-markers.ts +93 -0
  46. package/src/worklet/engine-mixer-facade.ts +186 -0
  47. package/src/worklet/engine-node.ts +451 -0
  48. package/src/worklet/engine-offline.ts +162 -0
  49. package/src/worklet/engine-options.ts +13 -0
  50. package/src/worklet/engine-parameter-facade.ts +172 -0
  51. package/src/worklet/engine-processor.ts +764 -0
  52. package/src/worklet/engine-register.ts +136 -0
  53. package/src/worklet/engine-strips.ts +315 -0
  54. package/src/worklet/engine-sync.ts +94 -0
  55. package/src/worklet/engine-tempo-facade.ts +141 -0
  56. package/src/worklet/engine.ts +998 -0
  57. package/src/worklet/guards.ts +14 -1
  58. package/src/worklet/messages.ts +60 -20
  59. package/src/worklet/mixer-processor.ts +368 -0
  60. package/src/worklet/protocol.ts +3 -0
  61. package/src/worklet/voice-changer-processor.ts +246 -0
  62. package/src/worklet.ts +20 -3549
  63. package/dist/sonare-rt-module.js +0 -2
  64. package/dist/sonare-rt.js +0 -2
  65. package/dist/sonare-rt.wasm +0 -0
  66. 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
+ }