@libraz/libsonare 1.5.5 → 1.6.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 +109 -20
- package/dist/analysis.d.ts +5730 -0
- package/dist/analysis.js +2464 -0
- package/dist/analysis.js.map +1 -0
- package/dist/index.d.ts +759 -35
- package/dist/index.js +904 -209
- package/dist/index.js.map +1 -1
- package/dist/schemas/realtime-voice-changer-preset-pack.schema.json +27 -0
- package/dist/schemas/realtime-voice-changer-preset.schema.json +140 -0
- package/dist/sonare-analysis.js +2 -0
- package/dist/sonare-analysis.wasm +0 -0
- package/dist/sonare.js +1 -1
- package/dist/sonare.wasm +0 -0
- package/dist/worker.d.ts +65 -0
- package/dist/worker.js +707 -0
- package/dist/worker.js.map +1 -0
- package/dist/worklet.d.ts +574 -86
- package/dist/worklet.js +1634 -235
- package/dist/worklet.js.map +1 -1
- package/package.json +23 -11
- package/src/_chain_config.ts +5 -3
- package/src/analysis.ts +137 -0
- package/src/analysis_helpers.ts +6 -0
- package/src/clip_page_streamer.ts +62 -8
- package/src/codes.ts +51 -64
- package/src/effects_mastering.ts +1 -0
- package/src/errors.ts +1 -0
- package/src/feature_core.ts +140 -2
- package/src/feature_music.ts +17 -2
- package/src/feature_pitch.ts +70 -1
- package/src/feature_spectral.ts +231 -5
- package/src/feature_spectrogram.ts +159 -0
- package/src/features.ts +16 -1
- package/src/index.ts +101 -7
- package/src/mastering_chain.ts +20 -8
- package/src/mastering_core.ts +5 -0
- package/src/mastering_dynamics.ts +7 -1
- package/src/metering.ts +6 -1
- package/src/opfs_clip_pages.ts +26 -1
- package/src/project.ts +7 -0
- package/src/project_class.ts +62 -8
- package/src/project_internal.ts +24 -29
- package/src/project_types.ts +69 -10
- package/src/public_types.ts +68 -0
- package/src/public_types_mastering.ts +22 -0
- package/src/public_types_mixing.ts +11 -2
- package/src/public_types_music.ts +9 -0
- package/src/public_types_spectral.ts +33 -0
- package/src/quick_analysis.ts +25 -7
- package/src/realtime_engine.ts +153 -3
- package/src/realtime_voice_changer.ts +51 -77
- package/src/sonare-analysis.js.d.ts +8 -0
- package/src/sonare.js.d.ts +276 -6
- package/src/streaming_processors.ts +15 -0
- package/src/web_midi.ts +36 -4
- package/src/worker.ts +196 -0
- package/src/worker_client.ts +352 -0
- package/src/worker_protocol.ts +59 -0
- package/src/worklet/engine-automation.ts +2 -1
- package/src/worklet/engine-capture-facade.ts +13 -4
- package/src/worklet/engine-clips.ts +12 -3
- package/src/worklet/engine-node.ts +212 -4
- package/src/worklet/engine-offline.ts +7 -2
- package/src/worklet/engine-parameter-facade.ts +26 -5
- package/src/worklet/engine-processor.ts +383 -42
- package/src/worklet/engine-register.ts +8 -0
- package/src/worklet/engine-strips.ts +17 -1
- package/src/worklet/engine-sync.ts +2 -6
- package/src/worklet/engine-tempo-facade.ts +8 -6
- package/src/worklet/engine.ts +380 -13
- package/src/worklet/guards.ts +22 -1
- package/src/worklet/messages.ts +125 -7
- package/src/worklet/protocol.ts +301 -17
- package/src/worklet/voice-changer-processor.ts +12 -15
- package/src/worklet.ts +20 -1
package/src/mastering_chain.ts
CHANGED
|
@@ -22,6 +22,7 @@ export interface MasteringChainRequest {
|
|
|
22
22
|
sampleRate?: number;
|
|
23
23
|
config?: MasteringChainConfig;
|
|
24
24
|
onProgress?: ProgressCallback;
|
|
25
|
+
cancel?: () => boolean;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
export interface MasteringChainStereoRequest {
|
|
@@ -30,6 +31,7 @@ export interface MasteringChainStereoRequest {
|
|
|
30
31
|
sampleRate?: number;
|
|
31
32
|
config?: MasteringChainConfig;
|
|
32
33
|
onProgress?: ProgressCallback;
|
|
34
|
+
cancel?: () => boolean;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
/** Canonical request form for one-shot preset mastering. */
|
|
@@ -39,6 +41,7 @@ export interface MasterAudioRequest {
|
|
|
39
41
|
preset?: MasteringPreset;
|
|
40
42
|
overrides?: MasteringChainConfig;
|
|
41
43
|
onProgress?: ProgressCallback;
|
|
44
|
+
cancel?: () => boolean;
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
/** Canonical request form for one-shot stereo preset mastering. */
|
|
@@ -49,6 +52,7 @@ export interface MasterAudioStereoRequest {
|
|
|
49
52
|
preset?: MasteringPreset;
|
|
50
53
|
overrides?: MasteringChainConfig;
|
|
51
54
|
onProgress?: ProgressCallback;
|
|
55
|
+
cancel?: () => boolean;
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
function masterAudioRequest(
|
|
@@ -114,12 +118,13 @@ export function masteringChain(
|
|
|
114
118
|
): MasteringChainResult {
|
|
115
119
|
const request =
|
|
116
120
|
samples instanceof Float32Array ? { samples, sampleRate, config, onProgress } : samples;
|
|
117
|
-
if (request.onProgress) {
|
|
121
|
+
if (request.onProgress || request.cancel) {
|
|
118
122
|
return requireModule().masteringChainWithProgress(
|
|
119
123
|
request.samples,
|
|
120
124
|
request.sampleRate ?? 22050,
|
|
121
125
|
canonicalChainConfig(request.config ?? {}),
|
|
122
|
-
request.onProgress,
|
|
126
|
+
request.onProgress ?? (() => {}),
|
|
127
|
+
request.cancel ?? (() => false),
|
|
123
128
|
);
|
|
124
129
|
}
|
|
125
130
|
return requireModule().masteringChain(
|
|
@@ -162,13 +167,14 @@ export function masteringChainStereo(
|
|
|
162
167
|
if (request.left.length !== request.right.length) {
|
|
163
168
|
throw new Error('Stereo channel lengths must match.');
|
|
164
169
|
}
|
|
165
|
-
if (request.onProgress) {
|
|
170
|
+
if (request.onProgress || request.cancel) {
|
|
166
171
|
return requireModule().masteringChainStereoWithProgress(
|
|
167
172
|
request.left,
|
|
168
173
|
request.right,
|
|
169
174
|
request.sampleRate ?? 22050,
|
|
170
175
|
canonicalChainConfig(request.config ?? {}),
|
|
171
|
-
request.onProgress,
|
|
176
|
+
request.onProgress ?? (() => {}),
|
|
177
|
+
request.cancel ?? (() => false),
|
|
172
178
|
);
|
|
173
179
|
}
|
|
174
180
|
return requireModule().masteringChainStereo(
|
|
@@ -213,6 +219,7 @@ export function masteringChainWithProgress(
|
|
|
213
219
|
request.sampleRate ?? 22050,
|
|
214
220
|
canonicalChainConfig(request.config ?? {}),
|
|
215
221
|
request.onProgress,
|
|
222
|
+
request.cancel ?? (() => false),
|
|
216
223
|
);
|
|
217
224
|
}
|
|
218
225
|
|
|
@@ -259,6 +266,7 @@ export function masteringChainStereoWithProgress(
|
|
|
259
266
|
request.sampleRate ?? 22050,
|
|
260
267
|
canonicalChainConfig(request.config ?? {}),
|
|
261
268
|
request.onProgress,
|
|
269
|
+
request.cancel ?? (() => false),
|
|
262
270
|
);
|
|
263
271
|
}
|
|
264
272
|
|
|
@@ -298,13 +306,14 @@ export function masterAudio(
|
|
|
298
306
|
): MasteringChainResult {
|
|
299
307
|
const request = masterAudioRequest(samples, sampleRate, presetName, overrides, onProgress);
|
|
300
308
|
const flat = flattenChainConfig(request.overrides ?? {});
|
|
301
|
-
if (request.onProgress) {
|
|
309
|
+
if (request.onProgress || request.cancel) {
|
|
302
310
|
return requireModule().masterAudioWithProgress(
|
|
303
311
|
request.preset ?? 'pop',
|
|
304
312
|
request.samples,
|
|
305
313
|
request.sampleRate ?? 22050,
|
|
306
314
|
flat,
|
|
307
|
-
request.onProgress,
|
|
315
|
+
request.onProgress ?? (() => {}),
|
|
316
|
+
request.cancel ?? (() => false),
|
|
308
317
|
);
|
|
309
318
|
}
|
|
310
319
|
return requireModule().masterAudio(
|
|
@@ -355,14 +364,15 @@ export function masterAudioStereo(
|
|
|
355
364
|
if (request.left.length !== request.right.length) {
|
|
356
365
|
throw new Error('Stereo channel lengths must match.');
|
|
357
366
|
}
|
|
358
|
-
if (request.onProgress) {
|
|
367
|
+
if (request.onProgress || request.cancel) {
|
|
359
368
|
return requireModule().masterAudioStereoWithProgress(
|
|
360
369
|
request.preset ?? 'pop',
|
|
361
370
|
request.left,
|
|
362
371
|
request.right,
|
|
363
372
|
request.sampleRate ?? 22050,
|
|
364
373
|
flat,
|
|
365
|
-
request.onProgress,
|
|
374
|
+
request.onProgress ?? (() => {}),
|
|
375
|
+
request.cancel ?? (() => false),
|
|
366
376
|
);
|
|
367
377
|
}
|
|
368
378
|
return requireModule().masterAudioStereo(
|
|
@@ -405,6 +415,7 @@ export function masterAudioWithProgress(
|
|
|
405
415
|
request.sampleRate ?? 22050,
|
|
406
416
|
flattenChainConfig(request.overrides ?? {}),
|
|
407
417
|
request.onProgress,
|
|
418
|
+
request.cancel ?? (() => false),
|
|
408
419
|
);
|
|
409
420
|
}
|
|
410
421
|
|
|
@@ -451,5 +462,6 @@ export function masterAudioStereoWithProgress(
|
|
|
451
462
|
request.sampleRate ?? 22050,
|
|
452
463
|
flattenChainConfig(request.overrides ?? {}),
|
|
453
464
|
request.onProgress,
|
|
465
|
+
request.cancel ?? (() => false),
|
|
454
466
|
);
|
|
455
467
|
}
|
package/src/mastering_core.ts
CHANGED
|
@@ -186,6 +186,9 @@ export type MasteringChannelPolicy =
|
|
|
186
186
|
| 'perChannel'
|
|
187
187
|
| 'passthrough';
|
|
188
188
|
|
|
189
|
+
/** Coarse algorithmic work estimate for a realtime insert; not a benchmark. */
|
|
190
|
+
export type MasteringRealtimeCost = 'low' | 'moderate' | 'high';
|
|
191
|
+
|
|
189
192
|
/** One processor's realtime/offline/pair classification in the catalog. */
|
|
190
193
|
export interface MasteringProcessorCatalogEntry {
|
|
191
194
|
/** Processor id (the name used for scene inserts / named processors). */
|
|
@@ -210,6 +213,8 @@ export interface MasteringProcessorCatalogEntry {
|
|
|
210
213
|
* offline, dry-only, and no-tail processors.
|
|
211
214
|
*/
|
|
212
215
|
tailSamples: number;
|
|
216
|
+
/** Coarse realtime work estimate, or null when the processor is not an insert. */
|
|
217
|
+
realtimeCost: MasteringRealtimeCost | null;
|
|
213
218
|
/**
|
|
214
219
|
* How the mixer wraps the processor on a >2-channel (surround) bus insert:
|
|
215
220
|
* "multichannel" (one full-buffer call) or "stereoPairOnly" (front L/R pair,
|
|
@@ -107,7 +107,13 @@ export function masteringDynamicsCompressor(
|
|
|
107
107
|
typeof request.detector === 'string'
|
|
108
108
|
? COMPRESSOR_DETECTOR_MAP[request.detector]
|
|
109
109
|
: request.detector;
|
|
110
|
-
|
|
110
|
+
// Preserve inherited option values as well as own properties. The embind
|
|
111
|
+
// boundary's hasProperty() intentionally follows the prototype chain, so a
|
|
112
|
+
// spread copy here would otherwise erase values before C++ can read them.
|
|
113
|
+
const opts: Record<string, unknown> = Object.assign(
|
|
114
|
+
Object.create(Object.getPrototypeOf(request)) as Record<string, unknown>,
|
|
115
|
+
request,
|
|
116
|
+
);
|
|
111
117
|
if (detector !== undefined) {
|
|
112
118
|
opts.detector = detector;
|
|
113
119
|
}
|
package/src/metering.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ErrorCode, SonareError } from './errors';
|
|
1
2
|
import { getSonareModule } from './module_state';
|
|
2
3
|
import type { ValidateOptions } from './validation';
|
|
3
4
|
import { assertSamples } from './validation';
|
|
@@ -17,7 +18,11 @@ function assertOversampleFactor(fnName: string, factor: number): void {
|
|
|
17
18
|
normalized > 16 ||
|
|
18
19
|
(normalized & (normalized - 1)) !== 0
|
|
19
20
|
) {
|
|
20
|
-
throw new
|
|
21
|
+
throw new SonareError(
|
|
22
|
+
ErrorCode.InvalidParameter,
|
|
23
|
+
'InvalidParameter',
|
|
24
|
+
`${fnName}: oversampleFactor must be 0 or a power of two from 1 to 16`,
|
|
25
|
+
);
|
|
21
26
|
}
|
|
22
27
|
}
|
|
23
28
|
|
package/src/opfs_clip_pages.ts
CHANGED
|
@@ -8,12 +8,20 @@ export interface OpfsClipPageProviderOptions {
|
|
|
8
8
|
dataOffsetBytes?: number;
|
|
9
9
|
worker?: Worker;
|
|
10
10
|
terminateWorkerOnClose?: boolean;
|
|
11
|
+
/** Internal bridge hook used to mirror a supplied page into an AudioWorklet. */
|
|
12
|
+
onPageSupplied?: (pageIndex: number, channels: Float32Array[]) => void;
|
|
13
|
+
/** Internal bridge hook used to mirror an eviction into an AudioWorklet. */
|
|
14
|
+
onPageCleared?: (pageIndex: number) => void;
|
|
15
|
+
/** Internal bridge hook called after the local provider is destroyed. */
|
|
16
|
+
onClose?: () => void;
|
|
11
17
|
}
|
|
12
18
|
|
|
13
19
|
export interface OpfsClipPageProviderBinding {
|
|
14
20
|
provider: ClipPageProvider;
|
|
15
21
|
supplyPage(pageIndex: number): Promise<boolean>;
|
|
16
22
|
supplyRequest(request: ClipPageRequest): Promise<boolean>;
|
|
23
|
+
/** Evict one resident page from every configured consumer. */
|
|
24
|
+
clearPage?(pageIndex: number): void;
|
|
17
25
|
close(): void;
|
|
18
26
|
}
|
|
19
27
|
|
|
@@ -116,7 +124,15 @@ self.onmessage = async (event) => {
|
|
|
116
124
|
|
|
117
125
|
export function createOpfsClipPageWorker(): Worker {
|
|
118
126
|
const blob = new Blob([opfsClipPageWorkerSource], { type: 'text/javascript' });
|
|
119
|
-
|
|
127
|
+
const url = URL.createObjectURL(blob);
|
|
128
|
+
try {
|
|
129
|
+
return new Worker(url);
|
|
130
|
+
} finally {
|
|
131
|
+
// Worker construction retains the script URL internally; keeping this
|
|
132
|
+
// object URL alive after construction leaks one browser registration per
|
|
133
|
+
// attach/close cycle.
|
|
134
|
+
URL.revokeObjectURL(url);
|
|
135
|
+
}
|
|
120
136
|
}
|
|
121
137
|
|
|
122
138
|
export function createOpfsClipPageProvider(
|
|
@@ -166,6 +182,7 @@ export function createOpfsClipPageProvider(
|
|
|
166
182
|
}
|
|
167
183
|
try {
|
|
168
184
|
provider.supply(response.pageIndex, channels);
|
|
185
|
+
options.onPageSupplied?.(response.pageIndex, channels);
|
|
169
186
|
} catch {
|
|
170
187
|
entry.resolve(false);
|
|
171
188
|
return;
|
|
@@ -218,6 +235,13 @@ export function createOpfsClipPageProvider(
|
|
|
218
235
|
supplyRequest(request: ClipPageRequest) {
|
|
219
236
|
return supplyPage(Math.floor(request.sample / options.pageFrames));
|
|
220
237
|
},
|
|
238
|
+
clearPage(pageIndex: number) {
|
|
239
|
+
if (closed) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
provider.clear(pageIndex);
|
|
243
|
+
options.onPageCleared?.(pageIndex);
|
|
244
|
+
},
|
|
221
245
|
close() {
|
|
222
246
|
if (closed) {
|
|
223
247
|
return;
|
|
@@ -229,6 +253,7 @@ export function createOpfsClipPageProvider(
|
|
|
229
253
|
}
|
|
230
254
|
pending.clear();
|
|
231
255
|
provider.destroy();
|
|
256
|
+
options.onClose?.();
|
|
232
257
|
if (ownsWorker) {
|
|
233
258
|
worker.terminate();
|
|
234
259
|
}
|
package/src/project.ts
CHANGED
|
@@ -10,6 +10,9 @@ export type {
|
|
|
10
10
|
BuiltinSynthBinding,
|
|
11
11
|
BuiltinSynthConfig,
|
|
12
12
|
BuiltinSynthWaveform,
|
|
13
|
+
ExternalSeparatedStem,
|
|
14
|
+
ExternalSeparatedStemImportRequest,
|
|
15
|
+
ExternalSeparatedStemImportResult,
|
|
13
16
|
MidiCcLearnOptions,
|
|
14
17
|
ProjectAssistSidecar,
|
|
15
18
|
ProjectAutomationCurve,
|
|
@@ -17,6 +20,7 @@ export type {
|
|
|
17
20
|
ProjectAutomationPoint,
|
|
18
21
|
ProjectBounceOptions,
|
|
19
22
|
ProjectChordSymbol,
|
|
23
|
+
ProjectClip,
|
|
20
24
|
ProjectClipCompSegment,
|
|
21
25
|
ProjectClipDesc,
|
|
22
26
|
ProjectClipFade,
|
|
@@ -37,8 +41,10 @@ export type {
|
|
|
37
41
|
ProjectMidiRouteConfig,
|
|
38
42
|
ProjectMidiRouteResult,
|
|
39
43
|
ProjectNotePairValidation,
|
|
44
|
+
ProjectSource,
|
|
40
45
|
ProjectTempoSegment,
|
|
41
46
|
ProjectTimeSignatureSegment,
|
|
47
|
+
ProjectTrack,
|
|
42
48
|
ProjectTrackDesc,
|
|
43
49
|
ProjectTrackKind,
|
|
44
50
|
ProjectWarpAnchor,
|
|
@@ -59,6 +65,7 @@ export type {
|
|
|
59
65
|
SynthPatch,
|
|
60
66
|
} from './project_types';
|
|
61
67
|
export {
|
|
68
|
+
BUILTIN_SYNTH_WAVEFORMS,
|
|
62
69
|
EXPECTED_PROJECT_ABI_VERSION,
|
|
63
70
|
MarkerKind,
|
|
64
71
|
SYNTH_BODY_TYPES,
|
package/src/project_class.ts
CHANGED
|
@@ -9,12 +9,15 @@ import {
|
|
|
9
9
|
} from './project_internal';
|
|
10
10
|
import type {
|
|
11
11
|
BuiltinSynthBinding,
|
|
12
|
+
ExternalSeparatedStemImportRequest,
|
|
13
|
+
ExternalSeparatedStemImportResult,
|
|
12
14
|
MidiCcLearnOptions,
|
|
13
15
|
ProjectAssistSidecar,
|
|
14
16
|
ProjectAutomationLaneDesc,
|
|
15
17
|
ProjectAutomationPoint,
|
|
16
18
|
ProjectBounceOptions,
|
|
17
19
|
ProjectChordSymbol,
|
|
20
|
+
ProjectClip,
|
|
18
21
|
ProjectClipCompSegment,
|
|
19
22
|
ProjectClipDesc,
|
|
20
23
|
ProjectClipFade,
|
|
@@ -32,9 +35,11 @@ import type {
|
|
|
32
35
|
ProjectMidiRouteConfig,
|
|
33
36
|
ProjectMidiRouteResult,
|
|
34
37
|
ProjectNotePairValidation,
|
|
38
|
+
ProjectSource,
|
|
35
39
|
ProjectTempoCandidate,
|
|
36
40
|
ProjectTempoSegment,
|
|
37
41
|
ProjectTimeSignatureSegment,
|
|
42
|
+
ProjectTrack,
|
|
38
43
|
ProjectTrackDesc,
|
|
39
44
|
ProjectTrackKind,
|
|
40
45
|
ProjectWarpMapDesc,
|
|
@@ -337,6 +342,19 @@ export class Project {
|
|
|
337
342
|
return this.native.addClip(desc);
|
|
338
343
|
}
|
|
339
344
|
|
|
345
|
+
/** Import host-separated PCM through the normal audio-track/clip path. */
|
|
346
|
+
importExternalStems(
|
|
347
|
+
request: ExternalSeparatedStemImportRequest,
|
|
348
|
+
): ExternalSeparatedStemImportResult {
|
|
349
|
+
return this.native.importExternalStems({
|
|
350
|
+
...request,
|
|
351
|
+
stems: request.stems.map((stem) => ({
|
|
352
|
+
...stem,
|
|
353
|
+
layout: stem.layout === 'mono' ? 1 : stem.layout === 'stereo' ? 2 : stem.layout,
|
|
354
|
+
})),
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
340
358
|
/** Split captured loop-recording audio into takes and add one clip. */
|
|
341
359
|
addLoopRecordingTakes(desc: ProjectLoopRecordingDesc): ProjectLoopRecordingResult {
|
|
342
360
|
return this.native.addLoopRecordingTakes(desc);
|
|
@@ -391,7 +409,11 @@ export class Project {
|
|
|
391
409
|
* Route a track's MIDI to host-instrument `destinationId` (0 = default). The
|
|
392
410
|
* compiler stamps every MIDI clip on the track with this id so the engine
|
|
393
411
|
* dispatches its events to the instrument registered for that destination.
|
|
394
|
-
* Routes through an undoable edit command.
|
|
412
|
+
* Routes through an undoable edit command. Builtin, NativeSynth, and SF2
|
|
413
|
+
* instruments retain source-track provenance inside a shared destination
|
|
414
|
+
* voice pool. With only zero-latency instruments bound, live lanes and
|
|
415
|
+
* channel-strip bounces remain aligned. Configure one live lane per source
|
|
416
|
+
* track that needs strip processing.
|
|
395
417
|
*/
|
|
396
418
|
setTrackMidiDestination(trackId: number, destinationId: number): void {
|
|
397
419
|
this.native.setTrackMidiDestination(trackId, destinationId);
|
|
@@ -685,6 +707,21 @@ export class Project {
|
|
|
685
707
|
this.native.setClipFade(clipId, fadeIn, fadeOut);
|
|
686
708
|
}
|
|
687
709
|
|
|
710
|
+
/** Audio source ids that need decoded PCM after deserialization. */
|
|
711
|
+
unresolvedAudioSourceIds(): number[] {
|
|
712
|
+
return this.native.unresolvedAudioSourceIds();
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
/** Register decoded interleaved PCM for an existing audio source (undoable). */
|
|
716
|
+
setSourceAudio(
|
|
717
|
+
sourceId: number,
|
|
718
|
+
audio: Float32Array,
|
|
719
|
+
channels: number,
|
|
720
|
+
sampleRate: number,
|
|
721
|
+
): void {
|
|
722
|
+
this.native.setSourceAudio(sourceId, audio, channels, sampleRate);
|
|
723
|
+
}
|
|
724
|
+
|
|
688
725
|
/** Replace a clip's take list and active take id (undoable). */
|
|
689
726
|
setClipTakes(clipId: number, takes: ReadonlyArray<ProjectClipTake>, activeTakeId = 0): void {
|
|
690
727
|
this.native.setClipTakes(clipId, takes, activeTakeId);
|
|
@@ -739,7 +776,7 @@ export class Project {
|
|
|
739
776
|
this.native.setTrackRoute(trackId, channelStripRef ?? '', outputTarget ?? '');
|
|
740
777
|
}
|
|
741
778
|
|
|
742
|
-
/** Append an automation lane
|
|
779
|
+
/** Append an automation lane; returns its stable target parameter id (undoable). */
|
|
743
780
|
addAutomationLane(trackId: number, desc: ProjectAutomationLaneDesc): number {
|
|
744
781
|
return this.native.addAutomationLane(trackId, {
|
|
745
782
|
targetParamId: desc.targetParamId,
|
|
@@ -747,17 +784,21 @@ export class Project {
|
|
|
747
784
|
});
|
|
748
785
|
}
|
|
749
786
|
|
|
750
|
-
/** Replace
|
|
751
|
-
editAutomationLane(
|
|
752
|
-
|
|
787
|
+
/** Replace the lane identified by its stable target parameter id (undoable). */
|
|
788
|
+
editAutomationLane(
|
|
789
|
+
trackId: number,
|
|
790
|
+
targetParamId: number,
|
|
791
|
+
desc: ProjectAutomationLaneDesc,
|
|
792
|
+
): void {
|
|
793
|
+
this.native.editAutomationLane(trackId, targetParamId, {
|
|
753
794
|
targetParamId: desc.targetParamId,
|
|
754
795
|
points: desc.points,
|
|
755
796
|
});
|
|
756
797
|
}
|
|
757
798
|
|
|
758
|
-
/** Remove
|
|
759
|
-
removeAutomationLane(trackId: number,
|
|
760
|
-
this.native.removeAutomationLane(trackId,
|
|
799
|
+
/** Remove the lane identified by its stable target parameter id (undoable). */
|
|
800
|
+
removeAutomationLane(trackId: number, targetParamId: number): void {
|
|
801
|
+
this.native.removeAutomationLane(trackId, targetParamId);
|
|
761
802
|
}
|
|
762
803
|
|
|
763
804
|
/** Replace the project's key annotation stream (undoable). */
|
|
@@ -841,6 +882,19 @@ export class Project {
|
|
|
841
882
|
return this.native.markerByIndex(index);
|
|
842
883
|
}
|
|
843
884
|
|
|
885
|
+
/** Read a stored project track by 0-based index. */
|
|
886
|
+
trackByIndex(index: number): ProjectTrack {
|
|
887
|
+
return this.native.trackByIndex(index);
|
|
888
|
+
}
|
|
889
|
+
/** Read a stored project clip by 0-based index. */
|
|
890
|
+
clipByIndex(index: number): ProjectClip {
|
|
891
|
+
return this.native.clipByIndex(index);
|
|
892
|
+
}
|
|
893
|
+
/** Read a stored project source by 0-based index. */
|
|
894
|
+
sourceByIndex(index: number): ProjectSource {
|
|
895
|
+
return this.native.sourceByIndex(index);
|
|
896
|
+
}
|
|
897
|
+
|
|
844
898
|
/** Number of markers in the project. */
|
|
845
899
|
markerCount(): number {
|
|
846
900
|
return this.native.markerCount();
|
package/src/project_internal.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveEnumOrdinal } from './codes';
|
|
1
2
|
import { getSonareModule } from './module_state';
|
|
2
3
|
import type {
|
|
3
4
|
BuiltinSynthBinding,
|
|
@@ -5,6 +6,7 @@ import type {
|
|
|
5
6
|
ProjectAutomationPoint,
|
|
6
7
|
ProjectBounceOptions,
|
|
7
8
|
ProjectChordSymbol,
|
|
9
|
+
ProjectClip,
|
|
8
10
|
ProjectClipCompSegment,
|
|
9
11
|
ProjectClipDesc,
|
|
10
12
|
ProjectClipFade,
|
|
@@ -21,9 +23,11 @@ import type {
|
|
|
21
23
|
ProjectMidiRouteConfig,
|
|
22
24
|
ProjectMidiRouteResult,
|
|
23
25
|
ProjectNotePairValidation,
|
|
26
|
+
ProjectSource,
|
|
24
27
|
ProjectTempoCandidate,
|
|
25
28
|
ProjectTempoSegment,
|
|
26
29
|
ProjectTimeSignatureSegment,
|
|
30
|
+
ProjectTrack,
|
|
27
31
|
ProjectTrackKind,
|
|
28
32
|
ProjectWarpMapDesc,
|
|
29
33
|
ProjectWarpMode,
|
|
@@ -41,6 +45,7 @@ export interface WasmProject {
|
|
|
41
45
|
setSampleRate: (sampleRate: number) => void;
|
|
42
46
|
addTrack: (desc: { kind?: number | string; name?: string }) => number;
|
|
43
47
|
addClip: (desc: ProjectClipDesc) => number;
|
|
48
|
+
importExternalStems: (request: unknown) => { trackIds: number[]; clipIds: number[] };
|
|
44
49
|
addLoopRecordingTakes: (desc: ProjectLoopRecordingDesc) => ProjectLoopRecordingResult;
|
|
45
50
|
addMidiClip: (startPpq: number, lengthPpq: number) => ProjectMidiClipResult;
|
|
46
51
|
splitClip: (clipId: number, splitPpq: number) => number;
|
|
@@ -108,6 +113,13 @@ export interface WasmProject {
|
|
|
108
113
|
removeClip: (clipId: number) => void;
|
|
109
114
|
setClipGain: (clipId: number, gain: number) => void;
|
|
110
115
|
setClipFade: (clipId: number, fadeIn: ProjectClipFade, fadeOut: ProjectClipFade) => void;
|
|
116
|
+
unresolvedAudioSourceIds: () => number[];
|
|
117
|
+
setSourceAudio: (
|
|
118
|
+
sourceId: number,
|
|
119
|
+
audio: Float32Array,
|
|
120
|
+
channels: number,
|
|
121
|
+
sampleRate: number,
|
|
122
|
+
) => void;
|
|
111
123
|
setClipTakes: (
|
|
112
124
|
clipId: number,
|
|
113
125
|
takes: ReadonlyArray<ProjectClipTake>,
|
|
@@ -131,10 +143,10 @@ export interface WasmProject {
|
|
|
131
143
|
) => number;
|
|
132
144
|
editAutomationLane: (
|
|
133
145
|
trackId: number,
|
|
134
|
-
|
|
146
|
+
targetParamId: number,
|
|
135
147
|
desc: { targetParamId: number; points: ReadonlyArray<ProjectAutomationPoint> },
|
|
136
148
|
) => void;
|
|
137
|
-
removeAutomationLane: (trackId: number,
|
|
149
|
+
removeAutomationLane: (trackId: number, targetParamId: number) => void;
|
|
138
150
|
annotateKeys: (keys: ReadonlyArray<ProjectKeySegment>) => void;
|
|
139
151
|
annotateChords: (chords: ReadonlyArray<ProjectChordSymbol>) => void;
|
|
140
152
|
setAssistSidecar: (
|
|
@@ -154,6 +166,9 @@ export interface WasmProject {
|
|
|
154
166
|
setMarker: (markerId: number, ppq: number, name: string) => number;
|
|
155
167
|
setMarkerEx: (marker: ProjectMarker) => number;
|
|
156
168
|
markerByIndex: (index: number) => ProjectMarker;
|
|
169
|
+
trackByIndex: (index: number) => ProjectTrack;
|
|
170
|
+
clipByIndex: (index: number) => ProjectClip;
|
|
171
|
+
sourceByIndex: (index: number) => ProjectSource;
|
|
157
172
|
markerCount: () => number;
|
|
158
173
|
trackCount: () => number;
|
|
159
174
|
clipCount: () => number;
|
|
@@ -307,37 +322,17 @@ export function assertProjectMidiEvents(
|
|
|
307
322
|
}
|
|
308
323
|
|
|
309
324
|
export function projectTrackKindValue(kind: ProjectTrackKind | undefined): number {
|
|
310
|
-
|
|
311
|
-
return 0;
|
|
312
|
-
}
|
|
313
|
-
if (kind === 'midi') {
|
|
314
|
-
return 1;
|
|
315
|
-
}
|
|
316
|
-
if (kind === 'aux') {
|
|
317
|
-
return 2;
|
|
318
|
-
}
|
|
319
|
-
return kind;
|
|
325
|
+
return resolveEnumOrdinal(kind ?? 'audio', { audio: 0, midi: 1, aux: 2 }, 'project track kind');
|
|
320
326
|
}
|
|
321
327
|
|
|
322
328
|
export function projectWarpModeValue(mode: ProjectWarpMode | undefined): number {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
}
|
|
329
|
-
if (mode === 'tempo-sync') {
|
|
330
|
-
return 2;
|
|
331
|
-
}
|
|
332
|
-
return mode;
|
|
329
|
+
return resolveEnumOrdinal(
|
|
330
|
+
mode ?? 'off',
|
|
331
|
+
{ off: 0, repitch: 1, 'tempo-sync': 2 },
|
|
332
|
+
'project warp mode',
|
|
333
|
+
);
|
|
333
334
|
}
|
|
334
335
|
|
|
335
336
|
export function projectLoopModeValue(mode: ProjectLoopMode | undefined): number {
|
|
336
|
-
|
|
337
|
-
return 0;
|
|
338
|
-
}
|
|
339
|
-
if (mode === 'loop') {
|
|
340
|
-
return 1;
|
|
341
|
-
}
|
|
342
|
-
return mode;
|
|
337
|
+
return resolveEnumOrdinal(mode ?? 'off', { off: 0, loop: 1 }, 'project loop mode');
|
|
343
338
|
}
|
package/src/project_types.ts
CHANGED
|
@@ -25,6 +25,29 @@ export interface ProjectBounceOptions {
|
|
|
25
25
|
instrumentLatencySamples?: number;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/** One decoded output from a host-owned source-separation model. */
|
|
29
|
+
export interface ExternalSeparatedStem {
|
|
30
|
+
name: string;
|
|
31
|
+
/** Optional host metadata; it does not change DSP. */
|
|
32
|
+
role?: string;
|
|
33
|
+
layout: 'mono' | 'stereo' | 1 | 2;
|
|
34
|
+
planarSamples: Float32Array[];
|
|
35
|
+
/** Absolute project-rate frame at which this stem begins. */
|
|
36
|
+
startFrame?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Request for {@link Project.importExternalStems}. */
|
|
40
|
+
export interface ExternalSeparatedStemImportRequest {
|
|
41
|
+
sampleRate: number;
|
|
42
|
+
stems: ExternalSeparatedStem[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Ids of the normal tracks and clips created by the import. */
|
|
46
|
+
export interface ExternalSeparatedStemImportResult {
|
|
47
|
+
trackIds: number[];
|
|
48
|
+
clipIds: number[];
|
|
49
|
+
}
|
|
50
|
+
|
|
28
51
|
/**
|
|
29
52
|
* Marker kind ordinals. Mirrors `SonareMarkerKind` in `src/sonare_c_types.h`;
|
|
30
53
|
* the values are part of the ABI and must not be renumbered.
|
|
@@ -53,17 +76,47 @@ export interface ProjectMarker {
|
|
|
53
76
|
keyMinor?: boolean;
|
|
54
77
|
}
|
|
55
78
|
|
|
79
|
+
/** Read-only stored project track returned by {@link Project.trackByIndex}. */
|
|
80
|
+
export interface ProjectTrack {
|
|
81
|
+
id: number;
|
|
82
|
+
kind: number;
|
|
83
|
+
midiDestinationId: number;
|
|
84
|
+
gain: number;
|
|
85
|
+
pan: number;
|
|
86
|
+
mute: boolean;
|
|
87
|
+
solo: boolean;
|
|
88
|
+
name: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Read-only stored project clip returned by {@link Project.clipByIndex}. */
|
|
92
|
+
export interface ProjectClip {
|
|
93
|
+
id: number;
|
|
94
|
+
trackId: number;
|
|
95
|
+
sourceId: number;
|
|
96
|
+
sourceKind: number;
|
|
97
|
+
startPpq: number;
|
|
98
|
+
lengthPpq: number;
|
|
99
|
+
sourceOffsetPpq: number;
|
|
100
|
+
gain: number;
|
|
101
|
+
loopMode: number;
|
|
102
|
+
loopLengthPpq: number;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Read-only stored project source returned by {@link Project.sourceByIndex}. */
|
|
106
|
+
export interface ProjectSource {
|
|
107
|
+
id: number;
|
|
108
|
+
kind: number;
|
|
109
|
+
channelCount: number;
|
|
110
|
+
storageHandleId: number;
|
|
111
|
+
sampleRateHint: number;
|
|
112
|
+
nameOrUri: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Names accepted by the minimal built-in oscillator synth. */
|
|
116
|
+
export const BUILTIN_SYNTH_WAVEFORMS = ['sine', 'saw', 'sawtooth', 'square', 'triangle'] as const;
|
|
117
|
+
|
|
56
118
|
/** Oscillator waveform for the built-in synth. */
|
|
57
|
-
export type BuiltinSynthWaveform =
|
|
58
|
-
| 'sine'
|
|
59
|
-
| 'saw'
|
|
60
|
-
| 'sawtooth'
|
|
61
|
-
| 'square'
|
|
62
|
-
| 'triangle'
|
|
63
|
-
| 0
|
|
64
|
-
| 1
|
|
65
|
-
| 2
|
|
66
|
-
| 3;
|
|
119
|
+
export type BuiltinSynthWaveform = (typeof BUILTIN_SYNTH_WAVEFORMS)[number] | 0 | 1 | 2 | 3;
|
|
67
120
|
|
|
68
121
|
/**
|
|
69
122
|
* Built-in synth patch + MIDI routing for
|
|
@@ -111,6 +164,8 @@ export interface Sf2InstrumentConfig {
|
|
|
111
164
|
gain?: number;
|
|
112
165
|
/** Max simultaneous voices (0 => 48, clamped to [1, 64]). */
|
|
113
166
|
polyphony?: number;
|
|
167
|
+
/** Prefer dedicated physical models for covered melodic GM programs. Defaults to false; drums stay SF2-first. */
|
|
168
|
+
preferModelForModeledFamilies?: boolean;
|
|
114
169
|
}
|
|
115
170
|
|
|
116
171
|
/** Source backend a resolved MIDI program renders through. */
|
|
@@ -198,6 +253,7 @@ export const SYNTH_MOD_DESTINATIONS = [
|
|
|
198
253
|
export interface SynthEnumTables {
|
|
199
254
|
engineModes: string[];
|
|
200
255
|
waveforms: string[];
|
|
256
|
+
builtinWaveforms: string[];
|
|
201
257
|
filterModels: string[];
|
|
202
258
|
filterOutputs: string[];
|
|
203
259
|
bodyTypes: string[];
|
|
@@ -312,8 +368,11 @@ export type ProjectFadeCurve =
|
|
|
312
368
|
| 'equal-power'
|
|
313
369
|
| 'equal_power'
|
|
314
370
|
| 'equalPower'
|
|
371
|
+
| 'equalpower'
|
|
315
372
|
| 'exponential'
|
|
373
|
+
| 'exp'
|
|
316
374
|
| 'logarithmic'
|
|
375
|
+
| 'log'
|
|
317
376
|
| 0
|
|
318
377
|
| 1
|
|
319
378
|
| 2
|