@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
|
@@ -0,0 +1,645 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Instrument definitions a project source plays through: the built-in synth
|
|
3
|
+
* patch, the SoundFont player, and the sample bank's descriptors.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { SampleBank } from './sample_bank';
|
|
7
|
+
|
|
8
|
+
/** Names accepted by the minimal built-in oscillator synth. */
|
|
9
|
+
export const BUILTIN_SYNTH_WAVEFORMS = ['sine', 'saw', 'sawtooth', 'square', 'triangle'] as const;
|
|
10
|
+
|
|
11
|
+
/** Oscillator waveform for the built-in synth. */
|
|
12
|
+
export type BuiltinSynthWaveform = (typeof BUILTIN_SYNTH_WAVEFORMS)[number] | 0 | 1 | 2 | 3;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Built-in synth patch + MIDI routing for
|
|
16
|
+
* {@link Project.bounceWithBuiltinInstrument}. Every field is optional; a
|
|
17
|
+
* non-positive (or omitted) numeric field falls back to the C-ABI default
|
|
18
|
+
* (gain 0.2, attack 5ms, decay 60ms, sustain 0.7, release 120ms, 16 voices),
|
|
19
|
+
* so `{}` is a usable default sine patch.
|
|
20
|
+
*/
|
|
21
|
+
export interface BuiltinSynthBinding {
|
|
22
|
+
/** MIDI destination id this patch answers to (default 0; see {@link Project.setTrackMidiDestination}). */
|
|
23
|
+
destinationId?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Oscillator waveform (default `'sine'`). The one field with no nearest
|
|
26
|
+
* sensible value — a name or an ordinal outside the set throws rather than
|
|
27
|
+
* falling back to sine.
|
|
28
|
+
*/
|
|
29
|
+
waveform?: BuiltinSynthWaveform;
|
|
30
|
+
/** Master output gain, linear (0 => 0.2). */
|
|
31
|
+
gain?: number;
|
|
32
|
+
/** ADSR attack in ms (0 => 5). */
|
|
33
|
+
attackMs?: number;
|
|
34
|
+
/** ADSR decay in ms (0 => 60). */
|
|
35
|
+
decayMs?: number;
|
|
36
|
+
/** ADSR sustain level [0,1] (0 => 0.7). */
|
|
37
|
+
sustain?: number;
|
|
38
|
+
/** ADSR release in ms (0 => 120). */
|
|
39
|
+
releaseMs?: number;
|
|
40
|
+
/** Max simultaneous voices (0 => 16, clamped to [1, 64]). */
|
|
41
|
+
polyphony?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Cross-binding alias of {@link BuiltinSynthBinding}. The same built-in-synth
|
|
46
|
+
* patch concept is named `BuiltinSynthConfig` in the Python binding; this alias
|
|
47
|
+
* lets portable code use that shared name on the WASM surface too.
|
|
48
|
+
*/
|
|
49
|
+
export type BuiltinSynthConfig = BuiltinSynthBinding;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* SoundFont (SF2) player patch + MIDI routing for
|
|
53
|
+
* {@link Project.bounceWithSf2Instrument}. Every field is optional; a
|
|
54
|
+
* non-positive (or omitted) numeric field falls back to the C-ABI default
|
|
55
|
+
* (gain 0.5, 48 voices), so `{}` is a usable default patch.
|
|
56
|
+
*/
|
|
57
|
+
export interface Sf2InstrumentConfig {
|
|
58
|
+
/** MIDI destination id this player answers to (default 0; see {@link Project.setTrackMidiDestination}). */
|
|
59
|
+
destinationId?: number;
|
|
60
|
+
/** Master output gain, linear (0 => 0.5). */
|
|
61
|
+
gain?: number;
|
|
62
|
+
/** Max simultaneous voices (0 => 48, clamped to [1, 64]). */
|
|
63
|
+
polyphony?: number;
|
|
64
|
+
/** Prefer dedicated physical models for covered melodic GM programs. Defaults to false; drums stay SF2-first. */
|
|
65
|
+
preferModelForModeledFamilies?: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Render the instrument alone, without the stage the bank binds after its
|
|
68
|
+
* voice — the amplifier and cabinet an electric guitar is never heard
|
|
69
|
+
* without. Defaults to false, so a MIDI file that selects a distorted guitar
|
|
70
|
+
* and asks for nothing else still comes out amplified. Set it to get the
|
|
71
|
+
* direct signal, which is what a voice is calibrated against.
|
|
72
|
+
*/
|
|
73
|
+
clearBankRig?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* How the GS insertion effects (EFX) are realised: 'modern' (default) or
|
|
76
|
+
* 'classic'. Any other value is refused by name.
|
|
77
|
+
*/
|
|
78
|
+
gsEfxRealization?: 'modern' | 'classic';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Source backend a resolved MIDI program renders through. */
|
|
82
|
+
export type SourceBackend = 'sf2' | 'synth';
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* One {@link Project.soundFontManifest} entry: a (channel, bank, program)
|
|
86
|
+
* combination the arrangement plays, with the backend it resolves to.
|
|
87
|
+
*/
|
|
88
|
+
export interface Sf2ProgramStatus {
|
|
89
|
+
/** MIDI channel (0-15). */
|
|
90
|
+
channel: number;
|
|
91
|
+
/** Effective SF2 bank (drum channels report 128). */
|
|
92
|
+
bank: number;
|
|
93
|
+
/** Program number (0-127). */
|
|
94
|
+
program: number;
|
|
95
|
+
/** `'sf2'` when the loaded SoundFont covers the program, else `'synth'`. */
|
|
96
|
+
backend: SourceBackend;
|
|
97
|
+
/** Resolved SF2 preset name (GS fallback included); empty for `'synth'`. */
|
|
98
|
+
presetName: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Every NativeSynth engine selector, by ordinal.
|
|
103
|
+
*
|
|
104
|
+
* @remarks
|
|
105
|
+
* Not seventeen interchangeable choices. Selecting a mode blanks every engine
|
|
106
|
+
* section but its own, and four engines have nothing to voice until a section
|
|
107
|
+
* is supplied: `'fm'` needs operators, `'modal'` a mode table, `'percussion'` a
|
|
108
|
+
* kit and `'sample'` a {@link SampleBank}. Each renders exact silence from an
|
|
109
|
+
* otherwise default patch (measured: peak 0), while the other thirteen sound
|
|
110
|
+
* and differ from one another. A host offering this array as a control offers
|
|
111
|
+
* those four as dead entries unless it also supplies the section, which a
|
|
112
|
+
* preset does.
|
|
113
|
+
*/
|
|
114
|
+
export const SYNTH_ENGINE_MODES = [
|
|
115
|
+
'default',
|
|
116
|
+
'subtractive',
|
|
117
|
+
'fm',
|
|
118
|
+
'karplus-strong',
|
|
119
|
+
'modal',
|
|
120
|
+
'additive',
|
|
121
|
+
'percussion',
|
|
122
|
+
'piano',
|
|
123
|
+
'pipe-organ',
|
|
124
|
+
'bowed-string',
|
|
125
|
+
'reed',
|
|
126
|
+
'brass',
|
|
127
|
+
'flute',
|
|
128
|
+
'plucked-string',
|
|
129
|
+
'vocal',
|
|
130
|
+
'free-reed',
|
|
131
|
+
'harpsichord',
|
|
132
|
+
'sample',
|
|
133
|
+
] as const;
|
|
134
|
+
|
|
135
|
+
export const SAMPLE_LOOP_MODES = ['default', 'none', 'continuous', 'key-down'] as const;
|
|
136
|
+
|
|
137
|
+
export const SAMPLE_KEY_TRACKS = ['default', 'on', 'off'] as const;
|
|
138
|
+
|
|
139
|
+
export const SYNTH_RETRIGGERS = ['default', 'free', 'note'] as const;
|
|
140
|
+
|
|
141
|
+
export const SYNTH_OSC_WAVEFORMS = [
|
|
142
|
+
'default',
|
|
143
|
+
'sine',
|
|
144
|
+
'saw',
|
|
145
|
+
'square',
|
|
146
|
+
'triangle',
|
|
147
|
+
'noise',
|
|
148
|
+
] as const;
|
|
149
|
+
|
|
150
|
+
export const SYNTH_FILTER_MODELS = [
|
|
151
|
+
'default',
|
|
152
|
+
'svf',
|
|
153
|
+
'moog-ladder',
|
|
154
|
+
'diode-ladder',
|
|
155
|
+
'sallen-key',
|
|
156
|
+
] as const;
|
|
157
|
+
|
|
158
|
+
export const SYNTH_FILTER_OUTPUTS = ['default', 'lowpass', 'bandpass', 'highpass'] as const;
|
|
159
|
+
|
|
160
|
+
export const SYNTH_BODY_TYPES = [
|
|
161
|
+
'default',
|
|
162
|
+
'none',
|
|
163
|
+
'guitar',
|
|
164
|
+
'violin',
|
|
165
|
+
'wood-tube',
|
|
166
|
+
'brass-bell',
|
|
167
|
+
'vocal',
|
|
168
|
+
] as const;
|
|
169
|
+
|
|
170
|
+
export const SYNTH_MOD_SOURCES = [
|
|
171
|
+
'none',
|
|
172
|
+
'amp-env',
|
|
173
|
+
'filter-env',
|
|
174
|
+
'lfo1',
|
|
175
|
+
'lfo2',
|
|
176
|
+
'velocity',
|
|
177
|
+
'key-track',
|
|
178
|
+
'mod-wheel',
|
|
179
|
+
'random',
|
|
180
|
+
'breath',
|
|
181
|
+
'aftertouch',
|
|
182
|
+
'expression-cc',
|
|
183
|
+
'pitch-bend',
|
|
184
|
+
] as const;
|
|
185
|
+
|
|
186
|
+
export const SYNTH_MOD_DESTINATIONS = [
|
|
187
|
+
'none',
|
|
188
|
+
'pitch-cents',
|
|
189
|
+
'cutoff-cents',
|
|
190
|
+
'amp-gain',
|
|
191
|
+
'pan-units',
|
|
192
|
+
'resonance-q',
|
|
193
|
+
'vibrato-depth-cents',
|
|
194
|
+
'filter-env-depth',
|
|
195
|
+
'lfo1-rate-scale',
|
|
196
|
+
'excitation-force',
|
|
197
|
+
'excitation-position',
|
|
198
|
+
'excitation-brightness',
|
|
199
|
+
'spectrum-morph',
|
|
200
|
+
] as const;
|
|
201
|
+
|
|
202
|
+
/** How a device spells a gesture (see {@link ControllerBinding}). */
|
|
203
|
+
export const CONTROLLER_INPUTS = [
|
|
204
|
+
'control-change',
|
|
205
|
+
'channel-pressure',
|
|
206
|
+
'poly-pressure',
|
|
207
|
+
'pitch-bend',
|
|
208
|
+
'velocity',
|
|
209
|
+
] as const;
|
|
210
|
+
|
|
211
|
+
/** What a gesture means — the expression axis a binding drives. */
|
|
212
|
+
export const CONTROLLER_AXES = [
|
|
213
|
+
'none',
|
|
214
|
+
'excitation',
|
|
215
|
+
'position',
|
|
216
|
+
'brightness',
|
|
217
|
+
'morph',
|
|
218
|
+
'loudness',
|
|
219
|
+
'pitch-cents',
|
|
220
|
+
'vibrato-depth',
|
|
221
|
+
] as const;
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* What a channel does with a note-on while another note on that channel is
|
|
225
|
+
* still held. `'mono-legato'` carries the sounding voice and only moves its
|
|
226
|
+
* pitch — a wind player's slur — and is deliberately out of reach of any MIDI
|
|
227
|
+
* message: CC126 names a monophonic mode but not this one.
|
|
228
|
+
*/
|
|
229
|
+
export const ARTICULATIONS = ['poly', 'mono-retrigger', 'mono-legato'] as const;
|
|
230
|
+
|
|
231
|
+
/** The three dimensions MPE carries per note ({@link RealtimeEngine.setControllerNoteTracking}). */
|
|
232
|
+
export const MPE_DIMENSIONS = ['bend', 'pressure', 'timbre'] as const;
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Which note a value addressed to a whole MIDI channel belongs to when several
|
|
236
|
+
* are sounding on it ({@link RealtimeEngine.setControllerNoteTracking}).
|
|
237
|
+
*
|
|
238
|
+
* MPE poses this question and declines to answer it — how a controller affects
|
|
239
|
+
* the notes when more than one is active on a member channel is left to the
|
|
240
|
+
* device — so this is a choice rather than a rule. A released note is never
|
|
241
|
+
* selected, whatever the rule and however long a pedal keeps it sounding.
|
|
242
|
+
*/
|
|
243
|
+
export const NOTE_TRACKINGS = ['last', 'lowest', 'highest', 'all'] as const;
|
|
244
|
+
|
|
245
|
+
export interface SynthEnumTables {
|
|
246
|
+
engineModes: string[];
|
|
247
|
+
waveforms: string[];
|
|
248
|
+
builtinWaveforms: string[];
|
|
249
|
+
filterModels: string[];
|
|
250
|
+
filterOutputs: string[];
|
|
251
|
+
bodyTypes: string[];
|
|
252
|
+
modSources: string[];
|
|
253
|
+
modDestinations: string[];
|
|
254
|
+
controllerInputs: string[];
|
|
255
|
+
controllerAxes: string[];
|
|
256
|
+
articulations: string[];
|
|
257
|
+
mpeDimensions: string[];
|
|
258
|
+
noteTrackings: string[];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** NativeSynth engine selector ({@link SynthPatch}; `'default'` keeps the base patch's). */
|
|
262
|
+
export type SynthEngineMode = (typeof SYNTH_ENGINE_MODES)[number];
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Per-patch loop override for the sample engine (`'default'` keeps what the
|
|
266
|
+
* bank recorded for the sample the zone names).
|
|
267
|
+
*
|
|
268
|
+
* A different set of values from {@link SampleDesc.loopMode}, which is the
|
|
269
|
+
* SoundFont `sampleModes` number describing the recording itself.
|
|
270
|
+
*/
|
|
271
|
+
export type SampleLoopMode = (typeof SAMPLE_LOOP_MODES)[number];
|
|
272
|
+
|
|
273
|
+
/** Whether a sample follows the played key (`'default'` keeps the base patch's). */
|
|
274
|
+
export type SampleKeyTrack = (typeof SAMPLE_KEY_TRACKS)[number];
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* What a note-on restarts on a {@link SynthPatch} (`'default'` keeps the base
|
|
278
|
+
* patch's). `'free'` seeds each voice's start phases and random streams from
|
|
279
|
+
* the voice slot and a running note count, so two plays of one note differ;
|
|
280
|
+
* `'note'` seeds them from the note number alone.
|
|
281
|
+
*/
|
|
282
|
+
export type SynthRetrigger = (typeof SYNTH_RETRIGGERS)[number];
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Loop behaviour recorded for one sample in a {@link SampleBank}.
|
|
286
|
+
*
|
|
287
|
+
* A number is the raw SoundFont `sampleModes` value the C struct carries
|
|
288
|
+
* (`0` no loop, `1` continuous, `3` while the key is held), so SF2-derived data
|
|
289
|
+
* passes through untranslated; the names are the readable spellings of the same
|
|
290
|
+
* three states. There is no `'default'`: a sample's own loop mode is where the
|
|
291
|
+
* default comes from.
|
|
292
|
+
*/
|
|
293
|
+
export type SampleDescLoopMode = 'none' | 'continuous' | 'key-down';
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Tuning and looping of one sample, in units relative to that sample
|
|
297
|
+
* ({@link SampleBank.addSample}).
|
|
298
|
+
*
|
|
299
|
+
* Every field is optional and the omitted state is meaningful: the empty
|
|
300
|
+
* descriptor is an unlooped sample rooted at middle C and played at the
|
|
301
|
+
* render's own rate.
|
|
302
|
+
*/
|
|
303
|
+
export interface SampleDesc {
|
|
304
|
+
/** MIDI key at which the sample sounds at its recorded pitch. Defaults to 60. */
|
|
305
|
+
rootKey?: number;
|
|
306
|
+
/** Fine tuning applied on top of {@link SampleDesc.rootKey}. */
|
|
307
|
+
fineTuneCents?: number;
|
|
308
|
+
/** Rate the sample was recorded at; omit to play it at the render's rate. */
|
|
309
|
+
sourceRate?: number;
|
|
310
|
+
/** Loop start, as a frame offset inside this sample. */
|
|
311
|
+
loopStart?: number;
|
|
312
|
+
/** Loop end, as a frame offset inside this sample. */
|
|
313
|
+
loopEnd?: number;
|
|
314
|
+
/**
|
|
315
|
+
* The sample's own loop behaviour. A loop that survives clamping empty is
|
|
316
|
+
* dropped, so a malformed loop plays unlooped rather than wrapping over
|
|
317
|
+
* nothing. {@link SynthPatch.sampleLoop} overrides this per patch.
|
|
318
|
+
*/
|
|
319
|
+
loopMode?: SampleDescLoopMode | number;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* One key/velocity rectangle mapped onto a sample ({@link SampleBank.addZone}).
|
|
324
|
+
*
|
|
325
|
+
* Every bound defaults on its own, so narrowing one edge never collapses
|
|
326
|
+
* another: an omitted upper bound is `127`, an omitted `velLo` is `1` (velocity
|
|
327
|
+
* zero is a note-off, not a dynamic), and an omitted `keyLo` is simply the
|
|
328
|
+
* lowest key. `{ keyLo: 48 }` is therefore keys 48-127 at every velocity and
|
|
329
|
+
* `{ velLo: 64 }` its exact mirror, while an empty rectangle is the whole
|
|
330
|
+
* keyboard. The one rectangle this cannot express is the single key `0`.
|
|
331
|
+
*/
|
|
332
|
+
export interface SampleZoneDesc {
|
|
333
|
+
/**
|
|
334
|
+
* Keymap set the zone joins; a {@link SynthPatch} names a set through
|
|
335
|
+
* {@link SynthPatch.sampleSet}. Sets below it are created. Defaults to `0`.
|
|
336
|
+
*/
|
|
337
|
+
setIndex?: number;
|
|
338
|
+
/** Sample the zone plays, as returned by {@link SampleBank.addSample}. */
|
|
339
|
+
sampleIndex?: number;
|
|
340
|
+
/** Lowest key of the rectangle. Defaults to `0`. */
|
|
341
|
+
keyLo?: number;
|
|
342
|
+
/** Highest key of the rectangle. Defaults to `127`. */
|
|
343
|
+
keyHi?: number;
|
|
344
|
+
/** Lowest velocity of the rectangle. Defaults to `1`. */
|
|
345
|
+
velLo?: number;
|
|
346
|
+
/** Highest velocity of the rectangle. Defaults to `127`. */
|
|
347
|
+
velHi?: number;
|
|
348
|
+
/** Added to the sample's own fine tuning. */
|
|
349
|
+
tuneCents?: number;
|
|
350
|
+
/** Linear gain; omit for unity. */
|
|
351
|
+
gain?: number;
|
|
352
|
+
/** Pan in the voice mixer's units, `-500` to `500`. */
|
|
353
|
+
panUnits?: number;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** NativeSynth oscillator waveform (`'default'` keeps the base patch's). */
|
|
357
|
+
export type SynthOscWaveform = (typeof SYNTH_OSC_WAVEFORMS)[number];
|
|
358
|
+
|
|
359
|
+
/** NativeSynth filter model — the character core (`'default'` keeps the base patch's). */
|
|
360
|
+
export type SynthFilterModel = (typeof SYNTH_FILTER_MODELS)[number];
|
|
361
|
+
|
|
362
|
+
/** NativeSynth filter output (SVF only; `'default'` keeps the base patch's). */
|
|
363
|
+
export type SynthFilterOutput = (typeof SYNTH_FILTER_OUTPUTS)[number];
|
|
364
|
+
|
|
365
|
+
/** NativeSynth body/formant resonance voicing (`'default'` keeps the base patch's). */
|
|
366
|
+
export type SynthBodyType = (typeof SYNTH_BODY_TYPES)[number];
|
|
367
|
+
|
|
368
|
+
/** {@link SynthPatch} mod-matrix source. */
|
|
369
|
+
export type SynthModSource = (typeof SYNTH_MOD_SOURCES)[number];
|
|
370
|
+
|
|
371
|
+
/** {@link SynthPatch} mod-matrix destination. */
|
|
372
|
+
export type SynthModDestination = (typeof SYNTH_MOD_DESTINATIONS)[number];
|
|
373
|
+
|
|
374
|
+
/** Input side of a {@link ControllerBinding}: how the device spells the gesture. */
|
|
375
|
+
export type ControllerInput = (typeof CONTROLLER_INPUTS)[number];
|
|
376
|
+
|
|
377
|
+
/** Output side of a {@link ControllerBinding}: which expression axis it means. */
|
|
378
|
+
export type ControllerAxis = (typeof CONTROLLER_AXES)[number];
|
|
379
|
+
|
|
380
|
+
/** Per-channel note-overlap rule ({@link RealtimeEngine.setArticulation}). */
|
|
381
|
+
export type Articulation = (typeof ARTICULATIONS)[number];
|
|
382
|
+
|
|
383
|
+
/** One per-note MPE dimension ({@link MPE_DIMENSIONS}). */
|
|
384
|
+
export type MpeDimension = (typeof MPE_DIMENSIONS)[number];
|
|
385
|
+
|
|
386
|
+
/** One note-attribution rule ({@link NOTE_TRACKINGS}). */
|
|
387
|
+
export type NoteTracking = (typeof NOTE_TRACKINGS)[number];
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* One device gesture bound to one expression axis
|
|
391
|
+
* ({@link RealtimeEngine.bindController}).
|
|
392
|
+
*
|
|
393
|
+
* Binding the same input twice with different axes is how a single gesture
|
|
394
|
+
* reaches two of them, which is what a breath controller driving both
|
|
395
|
+
* excitation and loudness needs. `input` and `axis` are required and are the
|
|
396
|
+
* canonical names (or their C ordinals); an unknown name throws rather than
|
|
397
|
+
* resolving to the first member.
|
|
398
|
+
*
|
|
399
|
+
* @example
|
|
400
|
+
* ```ts
|
|
401
|
+
* engine.bindController(0, { input: 'control-change', index: 2, axis: 'excitation' });
|
|
402
|
+
* ```
|
|
403
|
+
*/
|
|
404
|
+
export interface ControllerBinding {
|
|
405
|
+
/** How the device spells the gesture. */
|
|
406
|
+
input: ControllerInput | number;
|
|
407
|
+
/**
|
|
408
|
+
* CC number 0-127 for `'control-change'`. Every other input is identified by
|
|
409
|
+
* its message status alone and ignores this. Default `0`.
|
|
410
|
+
*/
|
|
411
|
+
index?: number;
|
|
412
|
+
/**
|
|
413
|
+
* Which expression axis the gesture means. `'none'` is refused: a binding
|
|
414
|
+
* that means nothing is a caller mistake, not an empty slot.
|
|
415
|
+
*/
|
|
416
|
+
axis: ControllerAxis | number;
|
|
417
|
+
/**
|
|
418
|
+
* Axis value at zero deflection, in the axis's own unit — normalized `[0,1]`
|
|
419
|
+
* for the excitation axes and loudness, cents for pitch and vibrato depth.
|
|
420
|
+
* Default `0`.
|
|
421
|
+
*/
|
|
422
|
+
lo?: number;
|
|
423
|
+
/** Axis value at full deflection; `lo > hi` inverts the gesture. Default `1`. */
|
|
424
|
+
hi?: number;
|
|
425
|
+
/**
|
|
426
|
+
* Exponent applied to the normalized input before the range maps it. Default
|
|
427
|
+
* `1` (linear) and deliberately so: a wind controller has already applied the
|
|
428
|
+
* curve its player chose, and a second one on this side bends a gesture that
|
|
429
|
+
* was already shaped. Must be finite and positive.
|
|
430
|
+
*/
|
|
431
|
+
curve?: number;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/** One {@link SynthPatch} mod-matrix routing (name or C ordinal per field). */
|
|
435
|
+
export interface SynthModRouting {
|
|
436
|
+
/**
|
|
437
|
+
* Mod-matrix source. `'none'` is refused: a routing driven by nothing is a
|
|
438
|
+
* caller mistake, not an empty slot.
|
|
439
|
+
*/
|
|
440
|
+
source: SynthModSource | number;
|
|
441
|
+
/**
|
|
442
|
+
* Mod-matrix destination. `'none'` is refused, for the same reason as
|
|
443
|
+
* `source`.
|
|
444
|
+
*/
|
|
445
|
+
destination: SynthModDestination | number;
|
|
446
|
+
/**
|
|
447
|
+
* Destination units at full source deflection, summed onto whatever the
|
|
448
|
+
* patch or a CC already set and then handled per destination:
|
|
449
|
+
*
|
|
450
|
+
* - `pan-units`: SF2 pan units, clamped to `[-500, 500]` — `1` moves the
|
|
451
|
+
* image by about 0.2% of a side.
|
|
452
|
+
* - `amp-gain` / `filter-env-depth`: multiplicative. Each routing's amount
|
|
453
|
+
* composes onto a running multiplier that clamps to `[0, 4]`.
|
|
454
|
+
* `filter-env-depth` has nothing to scale while the patch's
|
|
455
|
+
* `envToCutoffCents` is `0` — the filter envelope contributes no cents for
|
|
456
|
+
* it to multiply.
|
|
457
|
+
* - `resonance-q`: added to the patch's own Q, then floored at a fixed
|
|
458
|
+
* `0.5` (not at that patch's own Q).
|
|
459
|
+
* - `lfo1-rate-scale`: multiplicative, clamped to `[1/16, 16]`. Audible only
|
|
460
|
+
* once something else already reads LFO1's output — vibrato depth, the
|
|
461
|
+
* filter's LFO amount, tremolo — since it retunes LFO1 rather than
|
|
462
|
+
* sounding on its own; the bare patch's built-in mod-wheel vibrato is the
|
|
463
|
+
* usual path.
|
|
464
|
+
* - The three `excitation-*` destinations and `spectrum-morph`: an offset in
|
|
465
|
+
* the engine's own normalized `[0, 1]` axis units, the same scale the
|
|
466
|
+
* live-control CCs drive, clamped to `[-1, 1]`. Reach is per engine and not
|
|
467
|
+
* uniform across the three: `excitation-force` reaches pipe organ, bowed
|
|
468
|
+
* string, reed, brass, flute and free reed; `excitation-position` reaches
|
|
469
|
+
* bowed string only; `excitation-brightness` reaches pipe organ, reed,
|
|
470
|
+
* brass, flute, free reed and vocal. An engine outside a destination's list
|
|
471
|
+
* ignores it — its exciter is finished before the second sample renders, or
|
|
472
|
+
* it has none. `spectrum-morph` travels between the two spectral tables an
|
|
473
|
+
* additive patch carries; a patch needs an explicit `drawbars_b` distinct
|
|
474
|
+
* from `drawbars` to have a second table to travel to, and no catalog
|
|
475
|
+
* preset ships one, so the destination is currently unreachable from a
|
|
476
|
+
* preset name alone.
|
|
477
|
+
*
|
|
478
|
+
* Percussion reaches none of the above: a drum channel's voice runs the
|
|
479
|
+
* per-note kit patch instead of this one, so the whole mod matrix is
|
|
480
|
+
* discarded before the voice starts.
|
|
481
|
+
*/
|
|
482
|
+
depth: number;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Versioned NativeSynth patch for {@link Project.bounceWithSynthInstrument}
|
|
487
|
+
* and {@link RealtimeEngine.setSynthInstrument}.
|
|
488
|
+
*
|
|
489
|
+
* The patch starts from a BASE — the named `preset` (see
|
|
490
|
+
* {@link synthPresetNames}; a `"va:"` routing prefix is accepted) or, when
|
|
491
|
+
* `preset` is omitted, the default subtractive patch. Omitting a numeric field
|
|
492
|
+
* keeps the base value; supplying one overrides it (clamped to its audible
|
|
493
|
+
* range), including an explicit `0` such as `stereoSpread: 0`. The enum fields
|
|
494
|
+
* reserve `'default'` as keep. A `modRoutings` array REPLACES the base mod
|
|
495
|
+
* matrix, and an empty array clears it, while omitting the key keeps it.
|
|
496
|
+
*
|
|
497
|
+
* Mode-specific deep parameters (FM operator stacks, modal mode tables,
|
|
498
|
+
* drawbar registrations, kit pieces, piano strings) travel inside the named
|
|
499
|
+
* presets; the patch exposes the wrapper sections most engines share. Two
|
|
500
|
+
* exceptions: `waveform` is read by the subtractive engine only, and on a
|
|
501
|
+
* percussion channel the whole section below is discarded in favor of the
|
|
502
|
+
* per-note drum-kit patch — only `gain`, `busDrive` and `polyphony` still act.
|
|
503
|
+
*/
|
|
504
|
+
export interface SynthPatch {
|
|
505
|
+
/**
|
|
506
|
+
* Optional binding convenience for JS realtime/offline helpers. It is not
|
|
507
|
+
* part of the NativeSynth patch itself; Python uses explicit
|
|
508
|
+
* `(destination_id, patch)` bindings instead. Defaults to `0`.
|
|
509
|
+
*/
|
|
510
|
+
destinationId?: number;
|
|
511
|
+
/** Resolve MIDI channels from incoming GM bank/program changes; defaults to false. */
|
|
512
|
+
useGmPrograms?: boolean;
|
|
513
|
+
/** Base preset name (see {@link synthPresetNames}); omit for the init patch. */
|
|
514
|
+
preset?: string;
|
|
515
|
+
engineMode?: SynthEngineMode | number;
|
|
516
|
+
/** Oscillator waveform. Read by the subtractive engine only; every other engine ignores it. */
|
|
517
|
+
waveform?: SynthOscWaveform | number;
|
|
518
|
+
/** Detuned-stack width [1, 7]. */
|
|
519
|
+
unison?: number;
|
|
520
|
+
detuneCents?: number;
|
|
521
|
+
/** Per-voice slow pitch drift depth (cents). */
|
|
522
|
+
driftCents?: number;
|
|
523
|
+
/** Pre-filter drive [0, 1]. */
|
|
524
|
+
drive?: number;
|
|
525
|
+
filterModel?: SynthFilterModel | number;
|
|
526
|
+
filterOutput?: SynthFilterOutput | number;
|
|
527
|
+
cutoffHz?: number;
|
|
528
|
+
/**
|
|
529
|
+
* Series 12 dB/oct highpass after the main filter, in Hz; 0 disables the
|
|
530
|
+
* stage. The other end of a band the lowpass alone cannot make. It runs at
|
|
531
|
+
* Butterworth Q -- `resonanceQ` belongs to the main filter.
|
|
532
|
+
*/
|
|
533
|
+
hpCutoffHz?: number;
|
|
534
|
+
/**
|
|
535
|
+
* Rate the voice's output is held at, in Hz; 0 disables the stage. The
|
|
536
|
+
* voice's own converter, ahead of its amplitude envelope: the aliased images
|
|
537
|
+
* it folds down are as much of a sound as its samples are. Per voice, so a
|
|
538
|
+
* patch layering several voices can convert some and leave others alone.
|
|
539
|
+
*
|
|
540
|
+
* Percussion never reads this: a drum channel's voice runs the per-note kit
|
|
541
|
+
* patch instead of this one, so the field is discarded before the voice
|
|
542
|
+
* starts.
|
|
543
|
+
*/
|
|
544
|
+
sampleHoldHz?: number;
|
|
545
|
+
/**
|
|
546
|
+
* Word length the held value is quantized to, in bits; 0 disables the
|
|
547
|
+
* quantizer. Fractional values are meaningful — a converter's effective
|
|
548
|
+
* resolution is rarely a whole number of bits.
|
|
549
|
+
*/
|
|
550
|
+
bitDepth?: number;
|
|
551
|
+
resonanceQ?: number;
|
|
552
|
+
/** Cutoff keyboard tracking [0, 1]. */
|
|
553
|
+
keyTrack?: number;
|
|
554
|
+
/**
|
|
555
|
+
* Filter envelope's cutoff depth, in cents at full envelope; 0 leaves the
|
|
556
|
+
* envelope's cutoff contribution off. The cutoff is multiplied by
|
|
557
|
+
* 2^(envelope × envToCutoffCents / 1200), so the response moves by exactly
|
|
558
|
+
* that interval wherever the corner sits well below Nyquist; near the top the
|
|
559
|
+
* `svf` moves slightly less and the ladders slightly more. `cutoffHz` is the
|
|
560
|
+
* −3 dB corner only for `svf` at `resonanceQ` 0.707: with resonance the `svf`
|
|
561
|
+
* corner sits above it, and the ladder and Sallen-Key models put their
|
|
562
|
+
* resonant peak on it with the −3 dB point one to three octaves lower, so
|
|
563
|
+
* measure a depth as a ratio of corners rather than against `cutoffHz`.
|
|
564
|
+
*/
|
|
565
|
+
envToCutoffCents?: number;
|
|
566
|
+
/**
|
|
567
|
+
* Velocity's cutoff depth, in cents. The rendered term is
|
|
568
|
+
* `velToCutoffCents * (velocity / 127 - 1)`, so velocity 127 is the anchor
|
|
569
|
+
* where it is exactly zero: a positive value darkens softer notes, a
|
|
570
|
+
* negative value brightens them, and both effects grow the further the
|
|
571
|
+
* velocity sits below 127.
|
|
572
|
+
*/
|
|
573
|
+
velToCutoffCents?: number;
|
|
574
|
+
// --- pitch offset ---
|
|
575
|
+
/**
|
|
576
|
+
* Constant transposition of the voice's own pitch, in cents, clamped to
|
|
577
|
+
* [-4800, 4800]; 0 leaves the pitch alone. Applied on top of the note, so it
|
|
578
|
+
* shifts a whole patch without rewriting the part — a detuned layer, a sample
|
|
579
|
+
* set mapped a semitone off, an instrument pitched to a reference other than
|
|
580
|
+
* A440. Carried in the per-sample pitch factor every engine's render already
|
|
581
|
+
* takes, so it applies the same amount on all of them — except percussion,
|
|
582
|
+
* whose per-note kit patch replaces this whole section and never reads it.
|
|
583
|
+
*
|
|
584
|
+
* Also automatable under this same name through
|
|
585
|
+
* {@link RealtimeEngine.resolveInstrumentAutomationId}; it is one of the names
|
|
586
|
+
* that reaches a voice that is already sounding, rather than waiting for the
|
|
587
|
+
* next note.
|
|
588
|
+
*/
|
|
589
|
+
pitchOffsetCents?: number;
|
|
590
|
+
ampAttackMs?: number;
|
|
591
|
+
ampDecayMs?: number;
|
|
592
|
+
ampSustain?: number;
|
|
593
|
+
ampReleaseMs?: number;
|
|
594
|
+
filterAttackMs?: number;
|
|
595
|
+
filterDecayMs?: number;
|
|
596
|
+
filterSustain?: number;
|
|
597
|
+
filterReleaseMs?: number;
|
|
598
|
+
lfoRateHz?: number;
|
|
599
|
+
lfoToPitchCents?: number;
|
|
600
|
+
lfo2RateHz?: number;
|
|
601
|
+
glideMs?: number;
|
|
602
|
+
body?: SynthBodyType | number;
|
|
603
|
+
/** Body resonance mix [0, 1]. */
|
|
604
|
+
bodyMix?: number;
|
|
605
|
+
/** Seeded per-voice pan scatter [0, 1]. */
|
|
606
|
+
stereoSpread?: number;
|
|
607
|
+
/**
|
|
608
|
+
* Seed source for each voice's start state. `'note'` derives oscillator start
|
|
609
|
+
* phases, unison jitter, drift and every engine's noise stream from the note
|
|
610
|
+
* number, so the same note played again after its tail has ended renders the
|
|
611
|
+
* same samples; `'free'` (the built-in patches' choice) varies them per note.
|
|
612
|
+
* State outside the voice is reset by neither: controllers, the bus DC
|
|
613
|
+
* blocker, a piano's shared soundboard, a plucked string's sympathetic halo,
|
|
614
|
+
* an organ's wind chest and effect tails. The `'drum-kit'` preset's per-note
|
|
615
|
+
* pieces keep their own mode.
|
|
616
|
+
*/
|
|
617
|
+
retrigger?: SynthRetrigger | number;
|
|
618
|
+
/** Mod matrix (at most 8 routings; REPLACES the base matrix when non-empty). */
|
|
619
|
+
modRoutings?: SynthModRouting[];
|
|
620
|
+
/** Master output gain (linear). */
|
|
621
|
+
gain?: number;
|
|
622
|
+
/** Max simultaneous voices [1, 64]. */
|
|
623
|
+
polyphony?: number;
|
|
624
|
+
/** Gain-neutral bus saturation [0, 1]. */
|
|
625
|
+
busDrive?: number;
|
|
626
|
+
/**
|
|
627
|
+
* Bank the sample engine reads its PCM from. Binding convenience for the JS
|
|
628
|
+
* offline helpers rather than part of the patch itself, like `destinationId`:
|
|
629
|
+
* it is resolved to a native handle before the patch crosses into WASM.
|
|
630
|
+
* A `'sample'` patch bound without a bank renders silence.
|
|
631
|
+
*/
|
|
632
|
+
sampleBank?: SampleBank;
|
|
633
|
+
/**
|
|
634
|
+
* Keymap set in the bound bank (negative selects none). Read only by a
|
|
635
|
+
* `'sample'` patch, which is what lets set `0` stay addressable without a
|
|
636
|
+
* "keep the base value" sentinel of its own.
|
|
637
|
+
*/
|
|
638
|
+
sampleSet?: number;
|
|
639
|
+
/** Linear gain on the sample. */
|
|
640
|
+
sampleLevel?: number;
|
|
641
|
+
sampleLoop?: SampleLoopMode | number;
|
|
642
|
+
/** Attack skip, as a fraction of the mapped region. */
|
|
643
|
+
sampleStartOffset?: number;
|
|
644
|
+
sampleKeyTrack?: SampleKeyTrack | number;
|
|
645
|
+
}
|
package/src/live_audio.ts
CHANGED
|
@@ -1,22 +1,47 @@
|
|
|
1
1
|
import type { SonareRealtimeEngineNode } from './worklet';
|
|
2
2
|
|
|
3
3
|
export interface BindMicrophoneInputOptions extends MediaStreamConstraints {
|
|
4
|
+
/**
|
|
5
|
+
* Stream to bind instead of requesting one. Supplying it also transfers
|
|
6
|
+
* ownership: {@link MicrophoneInputBinding.close} leaves its tracks running,
|
|
7
|
+
* because they may be feeding a recorder, a level meter or a second
|
|
8
|
+
* `AudioContext` that this binding knows nothing about.
|
|
9
|
+
*/
|
|
4
10
|
stream?: MediaStream;
|
|
11
|
+
/**
|
|
12
|
+
* Whether {@link MicrophoneInputBinding.close} stops the stream's audio
|
|
13
|
+
* tracks. Defaults to whether this binding acquired the stream itself: `true`
|
|
14
|
+
* for a stream it requested through `getUserMedia`, `false` for one the
|
|
15
|
+
* caller supplied. Set it explicitly to override in either direction.
|
|
16
|
+
*/
|
|
5
17
|
stopTracksOnClose?: boolean;
|
|
6
18
|
}
|
|
7
19
|
|
|
8
20
|
export interface MicrophoneInputBinding {
|
|
9
21
|
stream: MediaStream;
|
|
10
22
|
source: MediaStreamAudioSourceNode;
|
|
23
|
+
/** Whether this binding acquired {@link stream} rather than being handed one. */
|
|
24
|
+
ownsStream: boolean;
|
|
11
25
|
close(): void;
|
|
12
26
|
}
|
|
13
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Route microphone audio into a realtime engine node.
|
|
30
|
+
*
|
|
31
|
+
* Ownership is decided by provenance, the same rule `ownsWorker` applies in
|
|
32
|
+
* `opfs_clip_pages.ts` and `worker_client.ts`: a resource the caller handed in
|
|
33
|
+
* is the caller's to release. `close()` used to stop the tracks of a supplied
|
|
34
|
+
* stream by default, which killed a microphone the rest of the page was still
|
|
35
|
+
* using.
|
|
36
|
+
*/
|
|
14
37
|
export async function bindMicrophoneInput(
|
|
15
38
|
context: AudioContext,
|
|
16
39
|
engine: SonareRealtimeEngineNode | AudioWorkletNode,
|
|
17
40
|
options: BindMicrophoneInputOptions = {},
|
|
18
41
|
): Promise<MicrophoneInputBinding> {
|
|
19
|
-
const { stream: providedStream, stopTracksOnClose
|
|
42
|
+
const { stream: providedStream, stopTracksOnClose: stopTracksOverride, ...constraints } = options;
|
|
43
|
+
const ownsStream = providedStream === undefined;
|
|
44
|
+
const stopTracksOnClose = stopTracksOverride ?? ownsStream;
|
|
20
45
|
const stream =
|
|
21
46
|
providedStream ??
|
|
22
47
|
(await navigator.mediaDevices.getUserMedia({
|
|
@@ -31,6 +56,7 @@ export async function bindMicrophoneInput(
|
|
|
31
56
|
return {
|
|
32
57
|
stream,
|
|
33
58
|
source,
|
|
59
|
+
ownsStream,
|
|
34
60
|
close() {
|
|
35
61
|
if (closed) {
|
|
36
62
|
return;
|