@libraz/libsonare 1.7.2 → 1.8.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/NOTICE +178 -0
- package/README.md +26 -203
- package/dist/_chain_config.d.ts +14 -0
- package/dist/_chain_config.d.ts.map +1 -0
- package/dist/_effects_common.d.ts +6 -0
- package/dist/_effects_common.d.ts.map +1 -0
- package/dist/_feature_validation.d.ts +8 -0
- package/dist/_feature_validation.d.ts.map +1 -0
- package/dist/_fft_options.d.ts +24 -0
- package/dist/_fft_options.d.ts.map +1 -0
- package/dist/align_take.d.ts +50 -0
- package/dist/align_take.d.ts.map +1 -0
- package/dist/analysis.d.ts +29 -5840
- package/dist/analysis.d.ts.map +1 -0
- package/dist/analysis.js +873 -722
- package/dist/analysis.js.map +1 -1
- package/dist/analysis_helpers.d.ts +9 -0
- package/dist/analysis_helpers.d.ts.map +1 -0
- package/dist/audio.d.ts +163 -0
- package/dist/audio.d.ts.map +1 -0
- package/dist/clip_page_streamer.d.ts +133 -0
- package/dist/clip_page_streamer.d.ts.map +1 -0
- package/dist/codes.d.ts +44 -0
- package/dist/codes.d.ts.map +1 -0
- package/dist/effects_mastering.d.ts +23 -0
- package/dist/effects_mastering.d.ts.map +1 -0
- package/dist/effects_note_ops.d.ts +477 -0
- package/dist/effects_note_ops.d.ts.map +1 -0
- package/dist/effects_percussive.d.ts +185 -0
- package/dist/effects_percussive.d.ts.map +1 -0
- package/dist/effects_separation.d.ts +65 -0
- package/dist/effects_separation.d.ts.map +1 -0
- package/dist/effects_spectral.d.ts +28 -0
- package/dist/effects_spectral.d.ts.map +1 -0
- package/dist/effects_timepitch.d.ts +129 -0
- package/dist/effects_timepitch.d.ts.map +1 -0
- package/dist/effects_voice_change.d.ts +53 -0
- package/dist/effects_voice_change.d.ts.map +1 -0
- package/dist/errors.d.ts +51 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/feature_core.d.ts +341 -0
- package/dist/feature_core.d.ts.map +1 -0
- package/dist/feature_decompose.d.ts +278 -0
- package/dist/feature_decompose.d.ts.map +1 -0
- package/dist/feature_inverse.d.ts +128 -0
- package/dist/feature_inverse.d.ts.map +1 -0
- package/dist/feature_loudness.d.ts +66 -0
- package/dist/feature_loudness.d.ts.map +1 -0
- package/dist/feature_music.d.ts +307 -0
- package/dist/feature_music.d.ts.map +1 -0
- package/dist/feature_pitch.d.ts +108 -0
- package/dist/feature_pitch.d.ts.map +1 -0
- package/dist/feature_resample.d.ts +16 -0
- package/dist/feature_resample.d.ts.map +1 -0
- package/dist/feature_spectral.d.ts +137 -0
- package/dist/feature_spectral.d.ts.map +1 -0
- package/dist/feature_spectrogram.d.ts +198 -0
- package/dist/feature_spectrogram.d.ts.map +1 -0
- package/dist/features.d.ts +10 -0
- package/dist/features.d.ts.map +1 -0
- package/dist/hrtf/default.shrf +0 -0
- package/dist/index.d.ts +74 -7431
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3758 -1362
- package/dist/index.js.map +1 -1
- package/dist/instrument_types.d.ts +517 -0
- package/dist/instrument_types.d.ts.map +1 -0
- package/dist/live_audio.d.ts +35 -0
- package/dist/live_audio.d.ts.map +1 -0
- package/dist/mastering_chain.d.ts +213 -0
- package/dist/mastering_chain.d.ts.map +1 -0
- package/dist/mastering_core.d.ts +457 -0
- package/dist/mastering_core.d.ts.map +1 -0
- package/dist/mastering_dynamics.d.ts +80 -0
- package/dist/mastering_dynamics.d.ts.map +1 -0
- package/dist/metering.d.ts +287 -0
- package/dist/metering.d.ts.map +1 -0
- package/dist/mixer.d.ts +464 -0
- package/dist/mixer.d.ts.map +1 -0
- package/dist/mixing_assistant.d.ts +62 -0
- package/dist/mixing_assistant.d.ts.map +1 -0
- package/dist/mixing_oneshot.d.ts +40 -0
- package/dist/mixing_oneshot.d.ts.map +1 -0
- package/dist/module_state.d.ts +15 -0
- package/dist/module_state.d.ts.map +1 -0
- package/dist/opfs_clip_pages.d.ts +28 -0
- package/dist/opfs_clip_pages.d.ts.map +1 -0
- package/dist/playback_renderer.d.ts +128 -0
- package/dist/playback_renderer.d.ts.map +1 -0
- package/dist/polyphony.d.ts +202 -0
- package/dist/polyphony.d.ts.map +1 -0
- package/dist/project.d.ts +8 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project_class.d.ts +562 -0
- package/dist/project_class.d.ts.map +1 -0
- package/dist/project_internal.d.ts +194 -0
- package/dist/project_internal.d.ts.map +1 -0
- package/dist/project_synth.d.ts +74 -0
- package/dist/project_synth.d.ts.map +1 -0
- package/dist/project_types.d.ts +652 -0
- package/dist/project_types.d.ts.map +1 -0
- package/dist/public_types.d.ts +185 -0
- package/dist/public_types.d.ts.map +1 -0
- package/dist/public_types_acoustic.d.ts +215 -0
- package/dist/public_types_acoustic.d.ts.map +1 -0
- package/dist/public_types_mastering.d.ts +510 -0
- package/dist/public_types_mastering.d.ts.map +1 -0
- package/dist/public_types_mixing.d.ts +436 -0
- package/dist/public_types_mixing.d.ts.map +1 -0
- package/dist/public_types_music.d.ts +619 -0
- package/dist/public_types_music.d.ts.map +1 -0
- package/dist/public_types_playback.d.ts +164 -0
- package/dist/public_types_playback.d.ts.map +1 -0
- package/dist/public_types_realtime.d.ts +174 -0
- package/dist/public_types_realtime.d.ts.map +1 -0
- package/dist/public_types_repair.d.ts +424 -0
- package/dist/public_types_repair.d.ts.map +1 -0
- package/dist/public_types_spectral.d.ts +697 -0
- package/dist/public_types_spectral.d.ts.map +1 -0
- package/dist/quick_analysis.d.ts +445 -0
- package/dist/quick_analysis.d.ts.map +1 -0
- package/dist/realtime_engine.d.ts +850 -0
- package/dist/realtime_engine.d.ts.map +1 -0
- package/dist/realtime_voice_changer.d.ts +158 -0
- package/dist/realtime_voice_changer.d.ts.map +1 -0
- package/dist/repair_dereverb.d.ts +187 -0
- package/dist/repair_dereverb.d.ts.map +1 -0
- package/dist/repair_impulsive.d.ts +186 -0
- package/dist/repair_impulsive.d.ts.map +1 -0
- package/dist/repair_noise.d.ts +239 -0
- package/dist/repair_noise.d.ts.map +1 -0
- package/dist/repair_trim.d.ts +123 -0
- package/dist/repair_trim.d.ts.map +1 -0
- package/dist/sample_bank.d.ts +84 -0
- package/dist/sample_bank.d.ts.map +1 -0
- package/dist/scale.d.ts +10 -0
- package/dist/scale.d.ts.map +1 -0
- package/dist/schemas/mixer-scene.schema.json +393 -0
- package/dist/schemas/playback-renderer-config.schema.json +392 -0
- package/dist/sonare-analysis.d.ts +8 -0
- package/dist/sonare-analysis.js +2 -2
- package/dist/sonare-analysis.wasm +0 -0
- package/dist/sonare.d.ts +3909 -0
- package/dist/sonare.js +2 -2
- package/dist/sonare.wasm +0 -0
- package/dist/stream_analyzer.d.ts +163 -0
- package/dist/stream_analyzer.d.ts.map +1 -0
- package/dist/stream_types.d.ts +214 -0
- package/dist/stream_types.d.ts.map +1 -0
- package/dist/streaming_mixing.d.ts +6 -0
- package/dist/streaming_mixing.d.ts.map +1 -0
- package/dist/streaming_processors.d.ts +335 -0
- package/dist/streaming_processors.d.ts.map +1 -0
- package/dist/transcribe.d.ts +77 -0
- package/dist/transcribe.d.ts.map +1 -0
- package/dist/validation.d.ts +140 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/web_midi.d.ts +77 -0
- package/dist/web_midi.d.ts.map +1 -0
- package/dist/worker.d.ts +5 -48
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +94 -41
- package/dist/worker.js.map +1 -1
- package/dist/worker_client.d.ts +96 -0
- package/dist/worker_client.d.ts.map +1 -0
- package/dist/worker_protocol.d.ts +43 -0
- package/dist/worker_protocol.d.ts.map +1 -0
- package/dist/worklet/audio_types.d.ts +21 -0
- package/dist/worklet/audio_types.d.ts.map +1 -0
- package/dist/worklet/engine-automation.d.ts +29 -0
- package/dist/worklet/engine-automation.d.ts.map +1 -0
- package/dist/worklet/engine-capture-facade.d.ts +35 -0
- package/dist/worklet/engine-capture-facade.d.ts.map +1 -0
- package/dist/worklet/engine-clips.d.ts +23 -0
- package/dist/worklet/engine-clips.d.ts.map +1 -0
- package/dist/worklet/engine-markers.d.ts +40 -0
- package/dist/worklet/engine-markers.d.ts.map +1 -0
- package/dist/worklet/engine-mixer-facade.d.ts +128 -0
- package/dist/worklet/engine-mixer-facade.d.ts.map +1 -0
- package/dist/worklet/engine-node.d.ts +81 -0
- package/dist/worklet/engine-node.d.ts.map +1 -0
- package/dist/worklet/engine-offline.d.ts +81 -0
- package/dist/worklet/engine-offline.d.ts.map +1 -0
- package/dist/worklet/engine-options.d.ts +12 -0
- package/dist/worklet/engine-options.d.ts.map +1 -0
- package/dist/worklet/engine-parameter-facade.d.ts +106 -0
- package/dist/worklet/engine-parameter-facade.d.ts.map +1 -0
- package/dist/worklet/engine-processor.d.ts +68 -0
- package/dist/worklet/engine-processor.d.ts.map +1 -0
- package/dist/worklet/engine-register.d.ts +2 -0
- package/dist/worklet/engine-register.d.ts.map +1 -0
- package/dist/worklet/engine-strips.d.ts +73 -0
- package/dist/worklet/engine-strips.d.ts.map +1 -0
- package/dist/worklet/engine-sync.d.ts +37 -0
- package/dist/worklet/engine-sync.d.ts.map +1 -0
- package/dist/worklet/engine-tempo-facade.d.ts +48 -0
- package/dist/worklet/engine-tempo-facade.d.ts.map +1 -0
- package/dist/worklet/engine.d.ts +397 -0
- package/dist/worklet/engine.d.ts.map +1 -0
- package/dist/worklet/guards.d.ts +47 -0
- package/dist/worklet/guards.d.ts.map +1 -0
- package/dist/worklet/messages.d.ts +710 -0
- package/dist/worklet/messages.d.ts.map +1 -0
- package/dist/worklet/mixer-processor.d.ts +46 -0
- package/dist/worklet/mixer-processor.d.ts.map +1 -0
- package/dist/worklet/playback-processor.d.ts +62 -0
- package/dist/worklet/playback-processor.d.ts.map +1 -0
- package/dist/worklet/protocol.d.ts +323 -0
- package/dist/worklet/protocol.d.ts.map +1 -0
- package/dist/worklet/voice-changer-processor.d.ts +41 -0
- package/dist/worklet/voice-changer-processor.d.ts.map +1 -0
- package/dist/worklet.d.ts +16 -2515
- package/dist/worklet.d.ts.map +1 -0
- package/dist/worklet.js +2763 -501
- package/dist/worklet.js.map +1 -1
- package/package.json +23 -12
- package/src/_effects_common.ts +17 -0
- package/src/_feature_validation.ts +34 -0
- package/src/_fft_options.ts +39 -0
- package/src/align_take.ts +64 -0
- package/src/analysis.ts +56 -3
- package/src/analysis_helpers.ts +7 -0
- package/src/audio.ts +106 -3
- package/src/codes.ts +39 -2
- package/src/effects_mastering.ts +97 -22
- package/src/effects_note_ops.ts +635 -0
- package/src/effects_percussive.ts +217 -0
- package/src/effects_separation.ts +150 -0
- package/src/effects_spectral.ts +60 -0
- package/src/effects_timepitch.ts +377 -0
- package/src/errors.ts +23 -1
- package/src/feature_core.ts +127 -2
- package/src/feature_decompose.ts +633 -0
- package/src/feature_inverse.ts +454 -0
- package/src/feature_loudness.ts +125 -0
- package/src/feature_music.ts +107 -14
- package/src/feature_pitch.ts +96 -1
- package/src/feature_spectral.ts +16 -611
- package/src/feature_spectrogram.ts +63 -450
- package/src/features.ts +36 -22
- package/src/index.ts +282 -30
- package/src/instrument_types.ts +645 -0
- package/src/live_audio.ts +27 -1
- package/src/mastering_chain.ts +184 -0
- package/src/mastering_core.ts +346 -32
- package/src/mastering_dynamics.ts +22 -11
- package/src/metering.ts +67 -24
- package/src/mixer.ts +212 -3
- package/src/mixing_assistant.ts +138 -0
- package/src/mixing_oneshot.ts +10 -5
- package/src/module_state.ts +24 -2
- package/src/playback_renderer.ts +252 -0
- package/src/polyphony.ts +279 -0
- package/src/project.ts +61 -24
- package/src/project_class.ts +450 -27
- package/src/project_internal.ts +149 -42
- package/src/project_synth.ts +67 -1
- package/src/project_types.ts +268 -270
- package/src/public_types.ts +122 -3
- package/src/public_types_acoustic.ts +112 -3
- package/src/public_types_mastering.ts +254 -73
- package/src/public_types_mixing.ts +363 -1
- package/src/public_types_music.ts +312 -2
- package/src/public_types_playback.ts +195 -0
- package/src/public_types_realtime.ts +39 -7
- package/src/public_types_repair.ts +446 -0
- package/src/public_types_spectral.ts +487 -1
- package/src/quick_analysis.ts +203 -26
- package/src/realtime_engine.ts +711 -28
- package/src/realtime_voice_changer.ts +55 -1
- package/src/repair_dereverb.ts +299 -0
- package/src/repair_impulsive.ts +395 -0
- package/src/repair_noise.ts +425 -0
- package/src/repair_trim.ts +226 -0
- package/src/sample_bank.ts +113 -0
- package/src/sonare.js.d.ts +1122 -30
- package/src/stream_analyzer.ts +36 -4
- package/src/stream_types.ts +37 -0
- package/src/streaming_mixing.ts +1 -1
- package/src/streaming_processors.ts +194 -10
- package/src/transcribe.ts +89 -0
- package/src/validation.ts +285 -11
- package/src/web_midi.ts +1 -6
- package/src/worker.ts +18 -2
- package/src/worklet/audio_types.ts +37 -0
- package/src/worklet/engine-mixer-facade.ts +444 -10
- package/src/worklet/engine-node.ts +78 -26
- package/src/worklet/engine-offline.ts +14 -8
- package/src/worklet/engine-parameter-facade.ts +21 -0
- package/src/worklet/engine-processor.ts +272 -83
- package/src/worklet/engine-register.ts +32 -18
- package/src/worklet/engine-strips.ts +280 -9
- package/src/worklet/engine-sync.ts +14 -6
- package/src/worklet/engine.ts +365 -31
- package/src/worklet/guards.ts +140 -44
- package/src/worklet/messages.ts +227 -2
- package/src/worklet/mixer-processor.ts +109 -47
- package/src/worklet/playback-processor.ts +300 -0
- package/src/worklet/protocol.ts +53 -3
- package/src/worklet/voice-changer-processor.ts +17 -11
- package/src/worklet.ts +17 -0
- package/src/effects_transform.ts +0 -718
- package/src/mastering_repair.ts +0 -273
package/src/worklet/engine.ts
CHANGED
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
MidiCcBindOptions,
|
|
23
23
|
PanLaw,
|
|
24
24
|
PanMode,
|
|
25
|
+
SidechainSourceKind,
|
|
25
26
|
} from '../index';
|
|
26
27
|
import { RealtimeEngine } from '../index';
|
|
27
28
|
import { createOpfsClipPageProvider, type OpfsClipPageProviderBinding } from '../opfs_clip_pages';
|
|
@@ -34,7 +35,7 @@ import type { EngineClipContext } from './engine-clips';
|
|
|
34
35
|
import * as clips from './engine-clips';
|
|
35
36
|
import type { EngineMarkerContext } from './engine-markers';
|
|
36
37
|
import * as markers from './engine-markers';
|
|
37
|
-
import type { EngineMixerContext } from './engine-mixer-facade';
|
|
38
|
+
import type { EngineMixerContext, InsertParamOverrideMap } from './engine-mixer-facade';
|
|
38
39
|
import * as mixer from './engine-mixer-facade';
|
|
39
40
|
import { SonareRealtimeEngineNode } from './engine-node';
|
|
40
41
|
import { buildTransportFacade, type CaptureOptions } from './engine-offline';
|
|
@@ -94,6 +95,7 @@ export class SonareEngine {
|
|
|
94
95
|
private readonly offlineBlockSize: number;
|
|
95
96
|
private readonly offlineChannelCount: number;
|
|
96
97
|
private readonly automationLanes = new Map<number, EngineAutomationPoint[]>();
|
|
98
|
+
private readonly insertAutomationIdsByTarget = new Map<string, Set<number>>();
|
|
97
99
|
private readonly clips = new Map<number, EngineClip>();
|
|
98
100
|
private readonly midiClips = new Map<number, EngineMidiClipSchedule>();
|
|
99
101
|
private readonly markers = new Map<number, EngineMarker>();
|
|
@@ -104,9 +106,18 @@ export class SonareEngine {
|
|
|
104
106
|
string,
|
|
105
107
|
{ trackId: number; insertIndex: number; sourceTrackId: number }
|
|
106
108
|
>();
|
|
109
|
+
private readonly busSidechains = new Map<
|
|
110
|
+
string,
|
|
111
|
+
{ busId: number; insertIndex: number; sourceKind: number; sourceId: number }
|
|
112
|
+
>();
|
|
113
|
+
private readonly masterSidechains = new Map<
|
|
114
|
+
number,
|
|
115
|
+
{ insertIndex: number; sourceKind: number; sourceId: number }
|
|
116
|
+
>();
|
|
107
117
|
private readonly buses: EngineBus[] = [];
|
|
108
118
|
private readonly trackStripJson = new Map<number, string>();
|
|
109
119
|
private readonly busStripJson = new Map<number, string>();
|
|
120
|
+
private readonly insertParamOverrides: InsertParamOverrideMap = new Map();
|
|
110
121
|
private masterStripJson: string | undefined;
|
|
111
122
|
private captureConfig: Omit<SonareEngineSyncCaptureMessage, 'type'> | undefined;
|
|
112
123
|
private tempoBpm = 120;
|
|
@@ -320,12 +331,17 @@ export class SonareEngine {
|
|
|
320
331
|
insertIndex: number,
|
|
321
332
|
paramName: string,
|
|
322
333
|
): number {
|
|
323
|
-
|
|
334
|
+
const id = parameter.resolveTrackInsertAutomationId(
|
|
324
335
|
this.parameterContext,
|
|
325
336
|
target,
|
|
326
337
|
insertIndex,
|
|
327
338
|
paramName,
|
|
328
339
|
);
|
|
340
|
+
if (id >= 0) {
|
|
341
|
+
const trackId = this.trackLaneIds[this.ensureTrackLane(target)];
|
|
342
|
+
this.rememberInsertAutomationId({ kind: 'track', trackId }, id);
|
|
343
|
+
}
|
|
344
|
+
return id;
|
|
329
345
|
}
|
|
330
346
|
|
|
331
347
|
/**
|
|
@@ -337,7 +353,15 @@ export class SonareEngine {
|
|
|
337
353
|
* @returns Reserved insert-automation id, or -1 when insert/key unknown.
|
|
338
354
|
*/
|
|
339
355
|
resolveMasterInsertAutomationId(insertIndex: number, paramName: string): number {
|
|
340
|
-
|
|
356
|
+
const id = parameter.resolveMasterInsertAutomationId(
|
|
357
|
+
this.parameterContext,
|
|
358
|
+
insertIndex,
|
|
359
|
+
paramName,
|
|
360
|
+
);
|
|
361
|
+
if (id >= 0) {
|
|
362
|
+
this.rememberInsertAutomationId({ kind: 'master' }, id);
|
|
363
|
+
}
|
|
364
|
+
return id;
|
|
341
365
|
}
|
|
342
366
|
|
|
343
367
|
/**
|
|
@@ -351,12 +375,30 @@ export class SonareEngine {
|
|
|
351
375
|
* @returns Reserved insert-automation id, or -1 when bus/insert/key unknown.
|
|
352
376
|
*/
|
|
353
377
|
resolveBusInsertAutomationId(busId: number, insertIndex: number, paramName: string): number {
|
|
354
|
-
|
|
378
|
+
const id = parameter.resolveBusInsertAutomationId(
|
|
355
379
|
this.parameterContext,
|
|
356
380
|
busId,
|
|
357
381
|
insertIndex,
|
|
358
382
|
paramName,
|
|
359
383
|
);
|
|
384
|
+
if (id >= 0) {
|
|
385
|
+
this.rememberInsertAutomationId({ kind: 'bus', busId }, id);
|
|
386
|
+
}
|
|
387
|
+
return id;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Resolves a hosted instrument's continuous parameter to its reserved
|
|
392
|
+
* instrument-automation id, so an instrument parameter follows a breakpoint
|
|
393
|
+
* lane at audio-block precision like a strip insert. Bind the instrument
|
|
394
|
+
* (`setSynthInstrument`) before resolving.
|
|
395
|
+
*
|
|
396
|
+
* @param destinationId MIDI destination the instrument is bound to.
|
|
397
|
+
* @param paramName Instrument JSON-key parameter name (e.g. `cutoffHz`).
|
|
398
|
+
* @returns Reserved instrument-automation id, or -1 when destination/key unknown.
|
|
399
|
+
*/
|
|
400
|
+
resolveInstrumentAutomationId(destinationId: number, paramName: string): number {
|
|
401
|
+
return parameter.resolveInstrumentAutomationId(this.parameterContext, destinationId, paramName);
|
|
360
402
|
}
|
|
361
403
|
|
|
362
404
|
/**
|
|
@@ -397,11 +439,19 @@ export class SonareEngine {
|
|
|
397
439
|
}
|
|
398
440
|
|
|
399
441
|
setStripGain(target: string | number, db: number): boolean {
|
|
400
|
-
|
|
442
|
+
const sent = this.sendSmoothedParam(this.stripParamId(target, ENGINE_MIXER_PARAM_FADER_DB), db);
|
|
443
|
+
if (sent && target === 'master') {
|
|
444
|
+
strips.cacheMasterStripScalar(this.stripContext, 'faderDb', db);
|
|
445
|
+
}
|
|
446
|
+
return sent;
|
|
401
447
|
}
|
|
402
448
|
|
|
403
449
|
setStripPan(target: string | number, pan: number): boolean {
|
|
404
|
-
|
|
450
|
+
const sent = this.sendSmoothedParam(this.stripParamId(target, ENGINE_MIXER_PARAM_PAN), pan);
|
|
451
|
+
if (sent && target === 'master') {
|
|
452
|
+
strips.cacheMasterStripScalar(this.stripContext, 'pan', pan);
|
|
453
|
+
}
|
|
454
|
+
return sent;
|
|
405
455
|
}
|
|
406
456
|
|
|
407
457
|
/**
|
|
@@ -448,6 +498,31 @@ export class SonareEngine {
|
|
|
448
498
|
mixer.setTrackBuses(this.mixerContext, buses);
|
|
449
499
|
}
|
|
450
500
|
|
|
501
|
+
/**
|
|
502
|
+
* Keys one insert of a bus strip from a track lane or another bus
|
|
503
|
+
* (ducking/sidechainRouter inserts). `sourceId` 0 removes the binding.
|
|
504
|
+
*/
|
|
505
|
+
setBusSidechain(
|
|
506
|
+
busId: number,
|
|
507
|
+
insertIndex: number,
|
|
508
|
+
sourceKind: SidechainSourceKind | number,
|
|
509
|
+
sourceId: number,
|
|
510
|
+
): void {
|
|
511
|
+
mixer.setBusSidechain(this.mixerContext, busId, insertIndex, sourceKind, sourceId);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Keys one insert of the master strip from a track lane or a bus. Same
|
|
516
|
+
* source rules as {@link setBusSidechain}.
|
|
517
|
+
*/
|
|
518
|
+
setMasterSidechain(
|
|
519
|
+
insertIndex: number,
|
|
520
|
+
sourceKind: SidechainSourceKind | number,
|
|
521
|
+
sourceId: number,
|
|
522
|
+
): void {
|
|
523
|
+
mixer.setMasterSidechain(this.mixerContext, insertIndex, sourceKind, sourceId);
|
|
524
|
+
}
|
|
525
|
+
|
|
451
526
|
setBusGain(busId: number, db: number): boolean {
|
|
452
527
|
return mixer.setBusGain(this.mixerContext, busId, db);
|
|
453
528
|
}
|
|
@@ -455,8 +530,17 @@ export class SonareEngine {
|
|
|
455
530
|
setTrackStripJson(target: string | number, sceneJson: string): void {
|
|
456
531
|
const laneIndex = this.ensureTrackLane(target);
|
|
457
532
|
const trackId = this.trackLaneIds[laneIndex];
|
|
458
|
-
strips.setTrackStripJson(
|
|
459
|
-
|
|
533
|
+
const { resetInserts } = strips.setTrackStripJson(
|
|
534
|
+
this.stripContext,
|
|
535
|
+
trackId,
|
|
536
|
+
sceneJson,
|
|
537
|
+
this.trackStripJson,
|
|
538
|
+
);
|
|
539
|
+
if (resetInserts) {
|
|
540
|
+
this.clearInsertAutomationLanes({ kind: 'track', trackId });
|
|
541
|
+
}
|
|
542
|
+
mixer.pruneStripSidechains(this.mixerContext, { kind: 'track', trackId }, sceneJson);
|
|
543
|
+
this.syncMixer(resetInserts ? [{ kind: 'track', trackId }] : []);
|
|
460
544
|
}
|
|
461
545
|
|
|
462
546
|
setTrackStripEqBand(target: string | number, bandIndex: number, band: EqBand | string): void {
|
|
@@ -541,9 +625,18 @@ export class SonareEngine {
|
|
|
541
625
|
}
|
|
542
626
|
|
|
543
627
|
setMasterStripJson(sceneJson: string): void {
|
|
544
|
-
|
|
628
|
+
const target = { kind: 'master' } as const;
|
|
629
|
+
const resetInserts =
|
|
630
|
+
(this.masterStripJson !== undefined && this.masterStripJson !== sceneJson) ||
|
|
631
|
+
mixer.hasInsertParamOverrides(this.insertParamOverrides, target);
|
|
632
|
+
mixer.applyFullStripJson(this.offlineEngine, target, sceneJson, resetInserts);
|
|
633
|
+
if (resetInserts) {
|
|
634
|
+
this.clearInsertAutomationLanes(target);
|
|
635
|
+
}
|
|
636
|
+
mixer.pruneStripSidechains(this.mixerContext, target, sceneJson);
|
|
637
|
+
mixer.clearInsertParamOverrides(this.insertParamOverrides, target);
|
|
545
638
|
this.masterStripJson = sceneJson;
|
|
546
|
-
this.syncMixer();
|
|
639
|
+
this.syncMixer(resetInserts ? [target] : []);
|
|
547
640
|
}
|
|
548
641
|
|
|
549
642
|
setMasterStripEqBand(bandIndex: number, band: EqBand | string): void {
|
|
@@ -572,6 +665,58 @@ export class SonareEngine {
|
|
|
572
665
|
strips.setBusStripInsertParamByName(this.stripContext, busId, insertIndex, paramName, value);
|
|
573
666
|
}
|
|
574
667
|
|
|
668
|
+
/**
|
|
669
|
+
* Bus-strip counterpart of {@link setStripInsertBypassed}. A bus is addressed
|
|
670
|
+
* by its numeric id, which the `target` of the track/master form cannot
|
|
671
|
+
* express, so it is its own method exactly like
|
|
672
|
+
* {@link setBusStripInsertParamByName}. Bypassing keeps the insert's internal
|
|
673
|
+
* state (reverb tail, compressor envelope) unless `resetOnBypass` is set;
|
|
674
|
+
* re-posting the bus scene JSON, the only workaround before this existed,
|
|
675
|
+
* rebuilds the chain and loses it.
|
|
676
|
+
*/
|
|
677
|
+
setBusStripInsertBypassed(
|
|
678
|
+
busId: number,
|
|
679
|
+
insertIndex: number,
|
|
680
|
+
bypassed: boolean,
|
|
681
|
+
resetOnBypass = false,
|
|
682
|
+
): void {
|
|
683
|
+
this.ensureBus(busId);
|
|
684
|
+
strips.setBusStripInsertBypassed(
|
|
685
|
+
this.stripContext,
|
|
686
|
+
busId,
|
|
687
|
+
insertIndex,
|
|
688
|
+
bypassed,
|
|
689
|
+
resetOnBypass,
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/** Bus-strip counterpart of {@link setTrackStripEqBand}. */
|
|
694
|
+
setBusStripEqBand(busId: number, bandIndex: number, band: EqBand | string): void {
|
|
695
|
+
this.ensureBus(busId);
|
|
696
|
+
strips.setBusStripEqBand(this.stripContext, busId, bandIndex, band);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/** Bus-strip counterpart of {@link setTrackStripPan}; refused on a surround bus. */
|
|
700
|
+
setBusStripPan(busId: number, pan: number): void {
|
|
701
|
+
this.ensureBus(busId);
|
|
702
|
+
strips.setBusStripPan(this.stripContext, busId, pan);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
setBusStripPanLaw(busId: number, panLaw: PanLaw | number): void {
|
|
706
|
+
this.ensureBus(busId);
|
|
707
|
+
strips.setBusStripPanLaw(this.stripContext, busId, panLaw);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
setBusStripPanMode(busId: number, panMode: PanMode | number): void {
|
|
711
|
+
this.ensureBus(busId);
|
|
712
|
+
strips.setBusStripPanMode(this.stripContext, busId, panMode);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
setBusStripDualPan(busId: number, leftPan: number, rightPan: number): void {
|
|
716
|
+
this.ensureBus(busId);
|
|
717
|
+
strips.setBusStripDualPan(this.stripContext, busId, leftPan, rightPan);
|
|
718
|
+
}
|
|
719
|
+
|
|
575
720
|
setStripInsertParamByName(
|
|
576
721
|
target: string | number,
|
|
577
722
|
insertIndex: number,
|
|
@@ -664,6 +809,52 @@ export class SonareEngine {
|
|
|
664
809
|
return { binding, provider: binding.provider };
|
|
665
810
|
}
|
|
666
811
|
|
|
812
|
+
/**
|
|
813
|
+
* Sets the clip-page look-ahead window in timeline frames on both the worklet
|
|
814
|
+
* engine and this thread's offline engine.
|
|
815
|
+
*
|
|
816
|
+
* The audio thread reports the pages it is *about to* read that are not
|
|
817
|
+
* resident yet, so the sliding-window streamer can service them before the
|
|
818
|
+
* playhead reaches them. Without look-ahead a page miss is only reported
|
|
819
|
+
* after the read already produced silence, which costs one block of silence
|
|
820
|
+
* at every page boundary that was not primed.
|
|
821
|
+
*
|
|
822
|
+
* Defaults to half a second at the engine's sample rate. `0` disables it.
|
|
823
|
+
* Safe to call during playback.
|
|
824
|
+
*/
|
|
825
|
+
setClipPagePrefetchFrames(frames: number): void {
|
|
826
|
+
if (this.destroyed) {
|
|
827
|
+
throw new Error('SonareEngine is destroyed.');
|
|
828
|
+
}
|
|
829
|
+
if (!Number.isFinite(frames) || frames < 0) {
|
|
830
|
+
throw new Error('clip page prefetch frames must be a finite value >= 0.');
|
|
831
|
+
}
|
|
832
|
+
this.offlineEngine.setClipPagePrefetchFrames(frames);
|
|
833
|
+
this.postSync({ type: 'syncClipPagePrefetchFrames', frames });
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Sets the number of concurrent time-stretch voices on both this thread's
|
|
838
|
+
* offline engine and the live worklet engine. `voices` must be an integer
|
|
839
|
+
* in `[0, 64]`; a non-integer, negative, or larger value throws and leaves
|
|
840
|
+
* the capacity unchanged on both engines. Default is 8. Capacity 0 disables
|
|
841
|
+
* time-stretch, so every warped clip plays resampled instead. A change
|
|
842
|
+
* applied while the engine is running restarts the splice state of any clip
|
|
843
|
+
* stretching through a voice at that moment.
|
|
844
|
+
*/
|
|
845
|
+
setWarpVoiceCapacity(voices: number): void {
|
|
846
|
+
if (this.destroyed) {
|
|
847
|
+
throw new Error('SonareEngine is destroyed.');
|
|
848
|
+
}
|
|
849
|
+
this.offlineEngine.setWarpVoiceCapacity(voices);
|
|
850
|
+
this.postSync({ type: 'syncWarpVoiceCapacity', voices });
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/** Reads the current time-stretch voice capacity (default 8). */
|
|
854
|
+
warpVoiceCapacity(): number {
|
|
855
|
+
return this.offlineEngine.warpVoiceCapacity();
|
|
856
|
+
}
|
|
857
|
+
|
|
667
858
|
addClip(
|
|
668
859
|
trackId: string | number,
|
|
669
860
|
buffer: Float32Array[] | ClipPageProvider,
|
|
@@ -703,6 +894,8 @@ export class SonareEngine {
|
|
|
703
894
|
gain?: number;
|
|
704
895
|
polyphony?: number;
|
|
705
896
|
preferModelForModeledFamilies?: boolean;
|
|
897
|
+
clearBankRig?: boolean;
|
|
898
|
+
gsEfxRealization?: 'modern' | 'classic';
|
|
706
899
|
} = {},
|
|
707
900
|
): void {
|
|
708
901
|
strips.setSf2Instrument(this.stripContext, trackId, config);
|
|
@@ -776,6 +969,52 @@ export class SonareEngine {
|
|
|
776
969
|
strips.pushMidiCc(this.stripContext, trackId, group, channel, controller, value, renderFrame);
|
|
777
970
|
}
|
|
778
971
|
|
|
972
|
+
pushMidiPitchBend(
|
|
973
|
+
trackId: string | number,
|
|
974
|
+
group: number,
|
|
975
|
+
channel: number,
|
|
976
|
+
bend14: number,
|
|
977
|
+
renderFrame = -1,
|
|
978
|
+
): void {
|
|
979
|
+
strips.pushMidiPitchBend(this.stripContext, trackId, group, channel, bend14, renderFrame);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
pushMidiChannelPressure(
|
|
983
|
+
trackId: string | number,
|
|
984
|
+
group: number,
|
|
985
|
+
channel: number,
|
|
986
|
+
pressure: number,
|
|
987
|
+
renderFrame = -1,
|
|
988
|
+
): void {
|
|
989
|
+
strips.pushMidiChannelPressure(
|
|
990
|
+
this.stripContext,
|
|
991
|
+
trackId,
|
|
992
|
+
group,
|
|
993
|
+
channel,
|
|
994
|
+
pressure,
|
|
995
|
+
renderFrame,
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
pushMidiPolyPressure(
|
|
1000
|
+
trackId: string | number,
|
|
1001
|
+
group: number,
|
|
1002
|
+
channel: number,
|
|
1003
|
+
note: number,
|
|
1004
|
+
pressure: number,
|
|
1005
|
+
renderFrame = -1,
|
|
1006
|
+
): void {
|
|
1007
|
+
strips.pushMidiPolyPressure(
|
|
1008
|
+
this.stripContext,
|
|
1009
|
+
trackId,
|
|
1010
|
+
group,
|
|
1011
|
+
channel,
|
|
1012
|
+
note,
|
|
1013
|
+
pressure,
|
|
1014
|
+
renderFrame,
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
779
1018
|
pushMidiUmp(trackId: string | number, word0: number, renderFrame = -1): void {
|
|
780
1019
|
strips.pushMidiUmp(this.stripContext, trackId, word0, renderFrame);
|
|
781
1020
|
}
|
|
@@ -860,6 +1099,58 @@ export class SonareEngine {
|
|
|
860
1099
|
});
|
|
861
1100
|
}
|
|
862
1101
|
|
|
1102
|
+
pushMidiInputPitchBend(
|
|
1103
|
+
group: number,
|
|
1104
|
+
channel: number,
|
|
1105
|
+
bend14: number,
|
|
1106
|
+
portTimeSamples = 0,
|
|
1107
|
+
): void {
|
|
1108
|
+
this.offlineEngine.pushMidiInputPitchBend(group, channel, bend14, portTimeSamples);
|
|
1109
|
+
this.postSync({
|
|
1110
|
+
type: 'syncMidiInputPitchBend',
|
|
1111
|
+
group,
|
|
1112
|
+
channel,
|
|
1113
|
+
data0: bend14,
|
|
1114
|
+
data1: 0,
|
|
1115
|
+
portTimeSamples,
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
pushMidiInputChannelPressure(
|
|
1120
|
+
group: number,
|
|
1121
|
+
channel: number,
|
|
1122
|
+
pressure: number,
|
|
1123
|
+
portTimeSamples = 0,
|
|
1124
|
+
): void {
|
|
1125
|
+
this.offlineEngine.pushMidiInputChannelPressure(group, channel, pressure, portTimeSamples);
|
|
1126
|
+
this.postSync({
|
|
1127
|
+
type: 'syncMidiInputChannelPressure',
|
|
1128
|
+
group,
|
|
1129
|
+
channel,
|
|
1130
|
+
data0: pressure,
|
|
1131
|
+
data1: 0,
|
|
1132
|
+
portTimeSamples,
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
pushMidiInputPolyPressure(
|
|
1137
|
+
group: number,
|
|
1138
|
+
channel: number,
|
|
1139
|
+
note: number,
|
|
1140
|
+
pressure: number,
|
|
1141
|
+
portTimeSamples = 0,
|
|
1142
|
+
): void {
|
|
1143
|
+
this.offlineEngine.pushMidiInputPolyPressure(group, channel, note, pressure, portTimeSamples);
|
|
1144
|
+
this.postSync({
|
|
1145
|
+
type: 'syncMidiInputPolyPressure',
|
|
1146
|
+
group,
|
|
1147
|
+
channel,
|
|
1148
|
+
data0: note,
|
|
1149
|
+
data1: pressure,
|
|
1150
|
+
portTimeSamples,
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
|
|
863
1154
|
pushMidiPanic(renderFrame = -1): void {
|
|
864
1155
|
this.offlineEngine.pushMidiPanic(renderFrame);
|
|
865
1156
|
this.postSync({ type: 'syncMidiPanic', renderFrame });
|
|
@@ -1106,8 +1397,42 @@ export class SonareEngine {
|
|
|
1106
1397
|
return mixer.mixerLanes(this.mixerContext);
|
|
1107
1398
|
}
|
|
1108
1399
|
|
|
1109
|
-
private syncMixer(): void {
|
|
1110
|
-
mixer.syncMixer(this.mixerContext);
|
|
1400
|
+
private syncMixer(forceInsertResets: mixer.StripJsonTarget[] = []): void {
|
|
1401
|
+
mixer.syncMixer(this.mixerContext, false, forceInsertResets);
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
private insertAutomationTargetKey(target: mixer.StripJsonTarget): string {
|
|
1405
|
+
return target.kind === 'track'
|
|
1406
|
+
? `track:${target.trackId}`
|
|
1407
|
+
: target.kind === 'bus'
|
|
1408
|
+
? `bus:${target.busId}`
|
|
1409
|
+
: 'master';
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
private rememberInsertAutomationId(target: mixer.StripJsonTarget, id: number): void {
|
|
1413
|
+
const key = this.insertAutomationTargetKey(target);
|
|
1414
|
+
const ids = this.insertAutomationIdsByTarget.get(key) ?? new Set<number>();
|
|
1415
|
+
ids.add(id);
|
|
1416
|
+
this.insertAutomationIdsByTarget.set(key, ids);
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
private clearInsertAutomationLanes(
|
|
1420
|
+
target: mixer.StripJsonTarget,
|
|
1421
|
+
forgetResolvedIds = false,
|
|
1422
|
+
): void {
|
|
1423
|
+
const key = this.insertAutomationTargetKey(target);
|
|
1424
|
+
const ids = this.insertAutomationIdsByTarget.get(key);
|
|
1425
|
+
if (!ids) {
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
for (const id of ids) {
|
|
1429
|
+
if (this.automationLanes.has(id)) {
|
|
1430
|
+
this.setAutomationLane(id, []);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
if (forgetResolvedIds) {
|
|
1434
|
+
this.insertAutomationIdsByTarget.delete(key);
|
|
1435
|
+
}
|
|
1111
1436
|
}
|
|
1112
1437
|
|
|
1113
1438
|
private postInstrumentSync(message: SonareEngineInstrumentSyncMessage): void {
|
|
@@ -1138,22 +1463,17 @@ export class SonareEngine {
|
|
|
1138
1463
|
if (this.destroyed) {
|
|
1139
1464
|
return;
|
|
1140
1465
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
this.realtimeNode.node.port.postMessage(message);
|
|
1146
|
-
}
|
|
1147
|
-
} finally {
|
|
1148
|
-
this.flushOfflineMirror();
|
|
1466
|
+
if (transfer && transfer.length > 0) {
|
|
1467
|
+
this.realtimeNode.node.port.postMessage(message, transfer);
|
|
1468
|
+
} else {
|
|
1469
|
+
this.realtimeNode.node.port.postMessage(message);
|
|
1149
1470
|
}
|
|
1150
1471
|
}
|
|
1151
1472
|
|
|
1152
|
-
// The
|
|
1153
|
-
//
|
|
1154
|
-
// call this from the worklet/audio path: it is intentionally control-only.
|
|
1473
|
+
// The control-only mirror applies due commands immediately while preserving
|
|
1474
|
+
// scheduled ones for its next render block.
|
|
1155
1475
|
private flushOfflineMirror(): void {
|
|
1156
|
-
this.offlineEngine.
|
|
1476
|
+
this.offlineEngine.applyCommandsDueNowPreservingFuture();
|
|
1157
1477
|
}
|
|
1158
1478
|
|
|
1159
1479
|
private sendMirroredCommand(
|
|
@@ -1175,16 +1495,25 @@ export class SonareEngine {
|
|
|
1175
1495
|
trackSends: this.trackSends,
|
|
1176
1496
|
trackOutputBus: this.trackOutputBus,
|
|
1177
1497
|
laneSidechains: this.laneSidechains,
|
|
1498
|
+
busSidechains: this.busSidechains,
|
|
1499
|
+
masterSidechains: this.masterSidechains,
|
|
1178
1500
|
buses: this.buses,
|
|
1179
1501
|
trackStripJson: this.trackStripJson,
|
|
1180
1502
|
busStripJson: this.busStripJson,
|
|
1503
|
+
insertParamOverrides: this.insertParamOverrides,
|
|
1504
|
+
flushOfflineMirror: () => this.flushOfflineMirror(),
|
|
1181
1505
|
postSync: (message) => this.postSync(message),
|
|
1182
1506
|
ensureTrackLane: (target) => this.ensureTrackLane(target),
|
|
1183
1507
|
ensureBus: (busId) => this.ensureBus(busId),
|
|
1184
1508
|
mixerLanes: () => this.mixerLanes(),
|
|
1185
|
-
syncMixer: () => this.syncMixer(),
|
|
1509
|
+
syncMixer: (forceInsertResets) => this.syncMixer(forceInsertResets),
|
|
1510
|
+
clearInsertAutomationLanes: (target, forgetResolvedIds) =>
|
|
1511
|
+
this.clearInsertAutomationLanes(target, forgetResolvedIds),
|
|
1186
1512
|
sendSmoothedParam: (paramId, value) => this.sendSmoothedParam(paramId, value),
|
|
1187
1513
|
getMasterStripJson: () => this.masterStripJson,
|
|
1514
|
+
cacheMasterStripJson: (sceneJson) => {
|
|
1515
|
+
this.masterStripJson = sceneJson;
|
|
1516
|
+
},
|
|
1188
1517
|
};
|
|
1189
1518
|
}
|
|
1190
1519
|
|
|
@@ -1199,6 +1528,10 @@ export class SonareEngine {
|
|
|
1199
1528
|
postInstrumentSync: (message) => this.postInstrumentSync(message),
|
|
1200
1529
|
ensureTrackLane: (target) => this.ensureTrackLane(target),
|
|
1201
1530
|
resolveTargetId: (target) => this.resolveTargetId(target),
|
|
1531
|
+
readStripJson: (target) => mixer.cachedStripJson(this.mixerContext, target),
|
|
1532
|
+
writeStripJson: (target, sceneJson) =>
|
|
1533
|
+
mixer.cacheStripJson(this.mixerContext, target, sceneJson),
|
|
1534
|
+
insertParamOverrides: this.insertParamOverrides,
|
|
1202
1535
|
};
|
|
1203
1536
|
}
|
|
1204
1537
|
|
|
@@ -1352,7 +1685,7 @@ export class SonareEngine {
|
|
|
1352
1685
|
private ensureTrackLane(target: string | number): number {
|
|
1353
1686
|
const trackId = this.resolveTargetId(target);
|
|
1354
1687
|
if (!Number.isInteger(trackId) || trackId <= 0) {
|
|
1355
|
-
throw new
|
|
1688
|
+
throw new RangeError(`Invalid track id for mixer lane: ${String(target)}`);
|
|
1356
1689
|
}
|
|
1357
1690
|
const existing = this.trackLaneIds.indexOf(trackId);
|
|
1358
1691
|
if (existing >= 0) {
|
|
@@ -1364,15 +1697,16 @@ export class SonareEngine {
|
|
|
1364
1697
|
}
|
|
1365
1698
|
|
|
1366
1699
|
private ensureBus(busId: number): number {
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1700
|
+
// Tested on the raw id: truncating first leaves the integrality check
|
|
1701
|
+
// unreachable, and registers a bus under an id the caller never named.
|
|
1702
|
+
if (!Number.isInteger(busId) || busId <= 0) {
|
|
1703
|
+
throw new RangeError(`Invalid bus id for mixer bus: ${String(busId)}`);
|
|
1370
1704
|
}
|
|
1371
|
-
const existing = this.buses.findIndex((bus) => bus.busId ===
|
|
1705
|
+
const existing = this.buses.findIndex((bus) => bus.busId === busId);
|
|
1372
1706
|
if (existing >= 0) {
|
|
1373
1707
|
return existing;
|
|
1374
1708
|
}
|
|
1375
|
-
this.buses.push({ busId
|
|
1709
|
+
this.buses.push({ busId });
|
|
1376
1710
|
this.syncMixer();
|
|
1377
1711
|
return this.buses.length - 1;
|
|
1378
1712
|
}
|