@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.
Files changed (303) hide show
  1. package/NOTICE +178 -0
  2. package/README.md +26 -203
  3. package/dist/_chain_config.d.ts +14 -0
  4. package/dist/_chain_config.d.ts.map +1 -0
  5. package/dist/_effects_common.d.ts +6 -0
  6. package/dist/_effects_common.d.ts.map +1 -0
  7. package/dist/_feature_validation.d.ts +8 -0
  8. package/dist/_feature_validation.d.ts.map +1 -0
  9. package/dist/_fft_options.d.ts +24 -0
  10. package/dist/_fft_options.d.ts.map +1 -0
  11. package/dist/align_take.d.ts +50 -0
  12. package/dist/align_take.d.ts.map +1 -0
  13. package/dist/analysis.d.ts +29 -5840
  14. package/dist/analysis.d.ts.map +1 -0
  15. package/dist/analysis.js +873 -722
  16. package/dist/analysis.js.map +1 -1
  17. package/dist/analysis_helpers.d.ts +9 -0
  18. package/dist/analysis_helpers.d.ts.map +1 -0
  19. package/dist/audio.d.ts +163 -0
  20. package/dist/audio.d.ts.map +1 -0
  21. package/dist/clip_page_streamer.d.ts +133 -0
  22. package/dist/clip_page_streamer.d.ts.map +1 -0
  23. package/dist/codes.d.ts +44 -0
  24. package/dist/codes.d.ts.map +1 -0
  25. package/dist/effects_mastering.d.ts +23 -0
  26. package/dist/effects_mastering.d.ts.map +1 -0
  27. package/dist/effects_note_ops.d.ts +477 -0
  28. package/dist/effects_note_ops.d.ts.map +1 -0
  29. package/dist/effects_percussive.d.ts +185 -0
  30. package/dist/effects_percussive.d.ts.map +1 -0
  31. package/dist/effects_separation.d.ts +65 -0
  32. package/dist/effects_separation.d.ts.map +1 -0
  33. package/dist/effects_spectral.d.ts +28 -0
  34. package/dist/effects_spectral.d.ts.map +1 -0
  35. package/dist/effects_timepitch.d.ts +129 -0
  36. package/dist/effects_timepitch.d.ts.map +1 -0
  37. package/dist/effects_voice_change.d.ts +53 -0
  38. package/dist/effects_voice_change.d.ts.map +1 -0
  39. package/dist/errors.d.ts +51 -0
  40. package/dist/errors.d.ts.map +1 -0
  41. package/dist/feature_core.d.ts +341 -0
  42. package/dist/feature_core.d.ts.map +1 -0
  43. package/dist/feature_decompose.d.ts +278 -0
  44. package/dist/feature_decompose.d.ts.map +1 -0
  45. package/dist/feature_inverse.d.ts +128 -0
  46. package/dist/feature_inverse.d.ts.map +1 -0
  47. package/dist/feature_loudness.d.ts +66 -0
  48. package/dist/feature_loudness.d.ts.map +1 -0
  49. package/dist/feature_music.d.ts +307 -0
  50. package/dist/feature_music.d.ts.map +1 -0
  51. package/dist/feature_pitch.d.ts +108 -0
  52. package/dist/feature_pitch.d.ts.map +1 -0
  53. package/dist/feature_resample.d.ts +16 -0
  54. package/dist/feature_resample.d.ts.map +1 -0
  55. package/dist/feature_spectral.d.ts +137 -0
  56. package/dist/feature_spectral.d.ts.map +1 -0
  57. package/dist/feature_spectrogram.d.ts +198 -0
  58. package/dist/feature_spectrogram.d.ts.map +1 -0
  59. package/dist/features.d.ts +10 -0
  60. package/dist/features.d.ts.map +1 -0
  61. package/dist/hrtf/default.shrf +0 -0
  62. package/dist/index.d.ts +74 -7431
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +3758 -1362
  65. package/dist/index.js.map +1 -1
  66. package/dist/instrument_types.d.ts +517 -0
  67. package/dist/instrument_types.d.ts.map +1 -0
  68. package/dist/live_audio.d.ts +35 -0
  69. package/dist/live_audio.d.ts.map +1 -0
  70. package/dist/mastering_chain.d.ts +213 -0
  71. package/dist/mastering_chain.d.ts.map +1 -0
  72. package/dist/mastering_core.d.ts +457 -0
  73. package/dist/mastering_core.d.ts.map +1 -0
  74. package/dist/mastering_dynamics.d.ts +80 -0
  75. package/dist/mastering_dynamics.d.ts.map +1 -0
  76. package/dist/metering.d.ts +287 -0
  77. package/dist/metering.d.ts.map +1 -0
  78. package/dist/mixer.d.ts +464 -0
  79. package/dist/mixer.d.ts.map +1 -0
  80. package/dist/mixing_assistant.d.ts +62 -0
  81. package/dist/mixing_assistant.d.ts.map +1 -0
  82. package/dist/mixing_oneshot.d.ts +40 -0
  83. package/dist/mixing_oneshot.d.ts.map +1 -0
  84. package/dist/module_state.d.ts +15 -0
  85. package/dist/module_state.d.ts.map +1 -0
  86. package/dist/opfs_clip_pages.d.ts +28 -0
  87. package/dist/opfs_clip_pages.d.ts.map +1 -0
  88. package/dist/playback_renderer.d.ts +128 -0
  89. package/dist/playback_renderer.d.ts.map +1 -0
  90. package/dist/polyphony.d.ts +202 -0
  91. package/dist/polyphony.d.ts.map +1 -0
  92. package/dist/project.d.ts +8 -0
  93. package/dist/project.d.ts.map +1 -0
  94. package/dist/project_class.d.ts +562 -0
  95. package/dist/project_class.d.ts.map +1 -0
  96. package/dist/project_internal.d.ts +194 -0
  97. package/dist/project_internal.d.ts.map +1 -0
  98. package/dist/project_synth.d.ts +74 -0
  99. package/dist/project_synth.d.ts.map +1 -0
  100. package/dist/project_types.d.ts +652 -0
  101. package/dist/project_types.d.ts.map +1 -0
  102. package/dist/public_types.d.ts +185 -0
  103. package/dist/public_types.d.ts.map +1 -0
  104. package/dist/public_types_acoustic.d.ts +215 -0
  105. package/dist/public_types_acoustic.d.ts.map +1 -0
  106. package/dist/public_types_mastering.d.ts +510 -0
  107. package/dist/public_types_mastering.d.ts.map +1 -0
  108. package/dist/public_types_mixing.d.ts +436 -0
  109. package/dist/public_types_mixing.d.ts.map +1 -0
  110. package/dist/public_types_music.d.ts +619 -0
  111. package/dist/public_types_music.d.ts.map +1 -0
  112. package/dist/public_types_playback.d.ts +164 -0
  113. package/dist/public_types_playback.d.ts.map +1 -0
  114. package/dist/public_types_realtime.d.ts +174 -0
  115. package/dist/public_types_realtime.d.ts.map +1 -0
  116. package/dist/public_types_repair.d.ts +424 -0
  117. package/dist/public_types_repair.d.ts.map +1 -0
  118. package/dist/public_types_spectral.d.ts +697 -0
  119. package/dist/public_types_spectral.d.ts.map +1 -0
  120. package/dist/quick_analysis.d.ts +445 -0
  121. package/dist/quick_analysis.d.ts.map +1 -0
  122. package/dist/realtime_engine.d.ts +850 -0
  123. package/dist/realtime_engine.d.ts.map +1 -0
  124. package/dist/realtime_voice_changer.d.ts +158 -0
  125. package/dist/realtime_voice_changer.d.ts.map +1 -0
  126. package/dist/repair_dereverb.d.ts +187 -0
  127. package/dist/repair_dereverb.d.ts.map +1 -0
  128. package/dist/repair_impulsive.d.ts +186 -0
  129. package/dist/repair_impulsive.d.ts.map +1 -0
  130. package/dist/repair_noise.d.ts +239 -0
  131. package/dist/repair_noise.d.ts.map +1 -0
  132. package/dist/repair_trim.d.ts +123 -0
  133. package/dist/repair_trim.d.ts.map +1 -0
  134. package/dist/sample_bank.d.ts +84 -0
  135. package/dist/sample_bank.d.ts.map +1 -0
  136. package/dist/scale.d.ts +10 -0
  137. package/dist/scale.d.ts.map +1 -0
  138. package/dist/schemas/mixer-scene.schema.json +393 -0
  139. package/dist/schemas/playback-renderer-config.schema.json +392 -0
  140. package/dist/sonare-analysis.d.ts +8 -0
  141. package/dist/sonare-analysis.js +2 -2
  142. package/dist/sonare-analysis.wasm +0 -0
  143. package/dist/sonare.d.ts +3909 -0
  144. package/dist/sonare.js +2 -2
  145. package/dist/sonare.wasm +0 -0
  146. package/dist/stream_analyzer.d.ts +163 -0
  147. package/dist/stream_analyzer.d.ts.map +1 -0
  148. package/dist/stream_types.d.ts +214 -0
  149. package/dist/stream_types.d.ts.map +1 -0
  150. package/dist/streaming_mixing.d.ts +6 -0
  151. package/dist/streaming_mixing.d.ts.map +1 -0
  152. package/dist/streaming_processors.d.ts +335 -0
  153. package/dist/streaming_processors.d.ts.map +1 -0
  154. package/dist/transcribe.d.ts +77 -0
  155. package/dist/transcribe.d.ts.map +1 -0
  156. package/dist/validation.d.ts +140 -0
  157. package/dist/validation.d.ts.map +1 -0
  158. package/dist/web_midi.d.ts +77 -0
  159. package/dist/web_midi.d.ts.map +1 -0
  160. package/dist/worker.d.ts +5 -48
  161. package/dist/worker.d.ts.map +1 -0
  162. package/dist/worker.js +94 -41
  163. package/dist/worker.js.map +1 -1
  164. package/dist/worker_client.d.ts +96 -0
  165. package/dist/worker_client.d.ts.map +1 -0
  166. package/dist/worker_protocol.d.ts +43 -0
  167. package/dist/worker_protocol.d.ts.map +1 -0
  168. package/dist/worklet/audio_types.d.ts +21 -0
  169. package/dist/worklet/audio_types.d.ts.map +1 -0
  170. package/dist/worklet/engine-automation.d.ts +29 -0
  171. package/dist/worklet/engine-automation.d.ts.map +1 -0
  172. package/dist/worklet/engine-capture-facade.d.ts +35 -0
  173. package/dist/worklet/engine-capture-facade.d.ts.map +1 -0
  174. package/dist/worklet/engine-clips.d.ts +23 -0
  175. package/dist/worklet/engine-clips.d.ts.map +1 -0
  176. package/dist/worklet/engine-markers.d.ts +40 -0
  177. package/dist/worklet/engine-markers.d.ts.map +1 -0
  178. package/dist/worklet/engine-mixer-facade.d.ts +128 -0
  179. package/dist/worklet/engine-mixer-facade.d.ts.map +1 -0
  180. package/dist/worklet/engine-node.d.ts +81 -0
  181. package/dist/worklet/engine-node.d.ts.map +1 -0
  182. package/dist/worklet/engine-offline.d.ts +81 -0
  183. package/dist/worklet/engine-offline.d.ts.map +1 -0
  184. package/dist/worklet/engine-options.d.ts +12 -0
  185. package/dist/worklet/engine-options.d.ts.map +1 -0
  186. package/dist/worklet/engine-parameter-facade.d.ts +106 -0
  187. package/dist/worklet/engine-parameter-facade.d.ts.map +1 -0
  188. package/dist/worklet/engine-processor.d.ts +68 -0
  189. package/dist/worklet/engine-processor.d.ts.map +1 -0
  190. package/dist/worklet/engine-register.d.ts +2 -0
  191. package/dist/worklet/engine-register.d.ts.map +1 -0
  192. package/dist/worklet/engine-strips.d.ts +73 -0
  193. package/dist/worklet/engine-strips.d.ts.map +1 -0
  194. package/dist/worklet/engine-sync.d.ts +37 -0
  195. package/dist/worklet/engine-sync.d.ts.map +1 -0
  196. package/dist/worklet/engine-tempo-facade.d.ts +48 -0
  197. package/dist/worklet/engine-tempo-facade.d.ts.map +1 -0
  198. package/dist/worklet/engine.d.ts +397 -0
  199. package/dist/worklet/engine.d.ts.map +1 -0
  200. package/dist/worklet/guards.d.ts +47 -0
  201. package/dist/worklet/guards.d.ts.map +1 -0
  202. package/dist/worklet/messages.d.ts +710 -0
  203. package/dist/worklet/messages.d.ts.map +1 -0
  204. package/dist/worklet/mixer-processor.d.ts +46 -0
  205. package/dist/worklet/mixer-processor.d.ts.map +1 -0
  206. package/dist/worklet/playback-processor.d.ts +62 -0
  207. package/dist/worklet/playback-processor.d.ts.map +1 -0
  208. package/dist/worklet/protocol.d.ts +323 -0
  209. package/dist/worklet/protocol.d.ts.map +1 -0
  210. package/dist/worklet/voice-changer-processor.d.ts +41 -0
  211. package/dist/worklet/voice-changer-processor.d.ts.map +1 -0
  212. package/dist/worklet.d.ts +16 -2515
  213. package/dist/worklet.d.ts.map +1 -0
  214. package/dist/worklet.js +2763 -501
  215. package/dist/worklet.js.map +1 -1
  216. package/package.json +23 -12
  217. package/src/_effects_common.ts +17 -0
  218. package/src/_feature_validation.ts +34 -0
  219. package/src/_fft_options.ts +39 -0
  220. package/src/align_take.ts +64 -0
  221. package/src/analysis.ts +56 -3
  222. package/src/analysis_helpers.ts +7 -0
  223. package/src/audio.ts +106 -3
  224. package/src/codes.ts +39 -2
  225. package/src/effects_mastering.ts +97 -22
  226. package/src/effects_note_ops.ts +635 -0
  227. package/src/effects_percussive.ts +217 -0
  228. package/src/effects_separation.ts +150 -0
  229. package/src/effects_spectral.ts +60 -0
  230. package/src/effects_timepitch.ts +377 -0
  231. package/src/errors.ts +23 -1
  232. package/src/feature_core.ts +127 -2
  233. package/src/feature_decompose.ts +633 -0
  234. package/src/feature_inverse.ts +454 -0
  235. package/src/feature_loudness.ts +125 -0
  236. package/src/feature_music.ts +107 -14
  237. package/src/feature_pitch.ts +96 -1
  238. package/src/feature_spectral.ts +16 -611
  239. package/src/feature_spectrogram.ts +63 -450
  240. package/src/features.ts +36 -22
  241. package/src/index.ts +282 -30
  242. package/src/instrument_types.ts +645 -0
  243. package/src/live_audio.ts +27 -1
  244. package/src/mastering_chain.ts +184 -0
  245. package/src/mastering_core.ts +346 -32
  246. package/src/mastering_dynamics.ts +22 -11
  247. package/src/metering.ts +67 -24
  248. package/src/mixer.ts +212 -3
  249. package/src/mixing_assistant.ts +138 -0
  250. package/src/mixing_oneshot.ts +10 -5
  251. package/src/module_state.ts +24 -2
  252. package/src/playback_renderer.ts +252 -0
  253. package/src/polyphony.ts +279 -0
  254. package/src/project.ts +61 -24
  255. package/src/project_class.ts +450 -27
  256. package/src/project_internal.ts +149 -42
  257. package/src/project_synth.ts +67 -1
  258. package/src/project_types.ts +268 -270
  259. package/src/public_types.ts +122 -3
  260. package/src/public_types_acoustic.ts +112 -3
  261. package/src/public_types_mastering.ts +254 -73
  262. package/src/public_types_mixing.ts +363 -1
  263. package/src/public_types_music.ts +312 -2
  264. package/src/public_types_playback.ts +195 -0
  265. package/src/public_types_realtime.ts +39 -7
  266. package/src/public_types_repair.ts +446 -0
  267. package/src/public_types_spectral.ts +487 -1
  268. package/src/quick_analysis.ts +203 -26
  269. package/src/realtime_engine.ts +711 -28
  270. package/src/realtime_voice_changer.ts +55 -1
  271. package/src/repair_dereverb.ts +299 -0
  272. package/src/repair_impulsive.ts +395 -0
  273. package/src/repair_noise.ts +425 -0
  274. package/src/repair_trim.ts +226 -0
  275. package/src/sample_bank.ts +113 -0
  276. package/src/sonare.js.d.ts +1122 -30
  277. package/src/stream_analyzer.ts +36 -4
  278. package/src/stream_types.ts +37 -0
  279. package/src/streaming_mixing.ts +1 -1
  280. package/src/streaming_processors.ts +194 -10
  281. package/src/transcribe.ts +89 -0
  282. package/src/validation.ts +285 -11
  283. package/src/web_midi.ts +1 -6
  284. package/src/worker.ts +18 -2
  285. package/src/worklet/audio_types.ts +37 -0
  286. package/src/worklet/engine-mixer-facade.ts +444 -10
  287. package/src/worklet/engine-node.ts +78 -26
  288. package/src/worklet/engine-offline.ts +14 -8
  289. package/src/worklet/engine-parameter-facade.ts +21 -0
  290. package/src/worklet/engine-processor.ts +272 -83
  291. package/src/worklet/engine-register.ts +32 -18
  292. package/src/worklet/engine-strips.ts +280 -9
  293. package/src/worklet/engine-sync.ts +14 -6
  294. package/src/worklet/engine.ts +365 -31
  295. package/src/worklet/guards.ts +140 -44
  296. package/src/worklet/messages.ts +227 -2
  297. package/src/worklet/mixer-processor.ts +109 -47
  298. package/src/worklet/playback-processor.ts +300 -0
  299. package/src/worklet/protocol.ts +53 -3
  300. package/src/worklet/voice-changer-processor.ts +17 -11
  301. package/src/worklet.ts +17 -0
  302. package/src/effects_transform.ts +0 -718
  303. package/src/mastering_repair.ts +0 -273
@@ -1,5 +1,13 @@
1
+ import type {
2
+ BuiltinSynthBinding,
3
+ Sf2InstrumentConfig,
4
+ Sf2ProgramStatus,
5
+ SynthPatch,
6
+ } from './instrument_types';
1
7
  import {
2
8
  assertProjectMidiEvents,
9
+ normalizeSynthInstrument,
10
+ projectAutomationPointValue,
3
11
  projectAutomationTargetKindValue,
4
12
  projectLoopModeValue,
5
13
  projectMidi1Event,
@@ -9,7 +17,6 @@ import {
9
17
  type WasmProject,
10
18
  } from './project_internal';
11
19
  import type {
12
- BuiltinSynthBinding,
13
20
  ExternalSeparatedStemImportRequest,
14
21
  ExternalSeparatedStemImportResult,
15
22
  MidiCcLearnOptions,
@@ -43,17 +50,23 @@ import type {
43
50
  ProjectNotePairValidation,
44
51
  ProjectSource,
45
52
  ProjectTempoCandidate,
53
+ ProjectTempoOptions,
46
54
  ProjectTempoSegment,
47
55
  ProjectTimeSignatureSegment,
48
56
  ProjectTrack,
49
57
  ProjectTrackDesc,
50
58
  ProjectTrackKind,
59
+ ProjectTranscribeRequest,
51
60
  ProjectWarpMapDesc,
52
61
  ProjectWarpMode,
53
- Sf2InstrumentConfig,
54
- Sf2ProgramStatus,
55
- SynthPatch,
56
62
  } from './project_types';
63
+ import {
64
+ assertBoundedInteger,
65
+ assertNibble,
66
+ assertSampleRate,
67
+ assertSamples,
68
+ assertU7,
69
+ } from './validation';
57
70
 
58
71
  /**
59
72
  * Folds the positional and request call forms of `bakeMidiFx` into one shape,
@@ -112,6 +125,52 @@ function validateAssistSidecarModuleId(value: unknown): string {
112
125
  return value;
113
126
  }
114
127
 
128
+ const MIDI2_U16_MAX = 0xffff;
129
+ const MIDI2_U32_MAX = 0xffffffff;
130
+
131
+ // UMP MIDI-2.0 channel-voice packet (message type 0x4). Canonical layout is
132
+ // sonare::midi::make_midi2_* (C-ABI sonare_midi2_*); the golden vectors in
133
+ // midi2-ump.test.ts lock this copy against it.
134
+ function midi2Event(
135
+ fnName: string,
136
+ ppq: number,
137
+ group: number,
138
+ opcode: number,
139
+ channel: number,
140
+ byte2: number,
141
+ byte3: number,
142
+ word1: number,
143
+ ): ProjectMidiEvent {
144
+ if (!Number.isFinite(ppq) || ppq < 0) {
145
+ throw new RangeError(`${fnName}: ppq must be a non-negative finite number`);
146
+ }
147
+ const g = assertNibble(fnName, group, 'group');
148
+ const ch = assertNibble(fnName, channel, 'channel');
149
+ const word0 =
150
+ ((0x4 << 28) | (g << 24) | (opcode << 20) | (ch << 16) | (byte2 << 8) | byte3) >>> 0;
151
+ return { ppq, data0: word0, data1: word1 >>> 0 };
152
+ }
153
+
154
+ function midi2U32(fnName: string, value: number, argName: string): number {
155
+ assertBoundedInteger(fnName, value, argName, 0, MIDI2_U32_MAX);
156
+ return value;
157
+ }
158
+
159
+ function midi2U16(fnName: string, value: number, argName: string): number {
160
+ assertBoundedInteger(fnName, value, argName, 0, MIDI2_U16_MAX);
161
+ return value;
162
+ }
163
+
164
+ function midi2I32(fnName: string, value: number, argName: string): number {
165
+ assertBoundedInteger(fnName, value, argName, -0x80000000, 0x7fffffff);
166
+ return value;
167
+ }
168
+
169
+ function midi2Byte(fnName: string, value: number, argName: string): number {
170
+ assertBoundedInteger(fnName, value, argName, 0, 0xff);
171
+ return value;
172
+ }
173
+
115
174
  /**
116
175
  * Headless DAW project (control-thread-only arrangement model).
117
176
  *
@@ -140,6 +199,7 @@ function validateAssistSidecarModuleId(value: unknown): string {
140
199
  */
141
200
  export class Project {
142
201
  private native: WasmProject;
202
+ private released = false;
143
203
 
144
204
  constructor() {
145
205
  this.native = new (projectModule().Project)();
@@ -204,6 +264,233 @@ export class Project {
204
264
  return projectMidi1Event('Project.midiProgram', ppq, group, 0xc, channel, program, 0);
205
265
  }
206
266
 
267
+ /**
268
+ * Pack a MIDI 2.0 note-on event accepted by {@link setMidiEvents}.
269
+ * `velocity16` is the full 16-bit velocity (0 is a sounding note-on);
270
+ * `attributeType` 0 is none, 3 is pitch 7.9.
271
+ */
272
+ static midi2NoteOn(
273
+ ppq: number,
274
+ group: number,
275
+ channel: number,
276
+ note: number,
277
+ velocity16: number,
278
+ attributeType = 0,
279
+ attributeData = 0,
280
+ ): ProjectMidiEvent {
281
+ const fn = 'Project.midi2NoteOn';
282
+ const n = assertU7(fn, note, 'note');
283
+ const vel = midi2U16(fn, velocity16, 'velocity16');
284
+ const type = midi2Byte(fn, attributeType, 'attributeType');
285
+ const data = midi2U16(fn, attributeData, 'attributeData');
286
+ return midi2Event(fn, ppq, group, 0x9, channel, n, type, ((vel << 16) | data) >>> 0);
287
+ }
288
+
289
+ /** Pack a MIDI 2.0 note-off event (`velocity16` is the full 16-bit release velocity). */
290
+ static midi2NoteOff(
291
+ ppq: number,
292
+ group: number,
293
+ channel: number,
294
+ note: number,
295
+ velocity16 = 0,
296
+ ): ProjectMidiEvent {
297
+ const fn = 'Project.midi2NoteOff';
298
+ const n = assertU7(fn, note, 'note');
299
+ const vel = midi2U16(fn, velocity16, 'velocity16');
300
+ return midi2Event(fn, ppq, group, 0x8, channel, n, 0, (vel << 16) >>> 0);
301
+ }
302
+
303
+ /** Pack a MIDI 2.0 control-change event (`value32` is the full 32-bit value). */
304
+ static midi2Cc(
305
+ ppq: number,
306
+ group: number,
307
+ channel: number,
308
+ controller: number,
309
+ value32: number,
310
+ ): ProjectMidiEvent {
311
+ const fn = 'Project.midi2Cc';
312
+ const c = assertU7(fn, controller, 'controller');
313
+ return midi2Event(fn, ppq, group, 0xb, channel, c, 0, midi2U32(fn, value32, 'value32'));
314
+ }
315
+
316
+ /** Pack a MIDI 2.0 poly-pressure event (`pressure32` is the full 32-bit pressure). */
317
+ static midi2PolyPressure(
318
+ ppq: number,
319
+ group: number,
320
+ channel: number,
321
+ note: number,
322
+ pressure32: number,
323
+ ): ProjectMidiEvent {
324
+ const fn = 'Project.midi2PolyPressure';
325
+ const n = assertU7(fn, note, 'note');
326
+ return midi2Event(fn, ppq, group, 0xa, channel, n, 0, midi2U32(fn, pressure32, 'pressure32'));
327
+ }
328
+
329
+ /** Pack a MIDI 2.0 channel-pressure event (`pressure32` is the full 32-bit pressure). */
330
+ static midi2ChannelPressure(
331
+ ppq: number,
332
+ group: number,
333
+ channel: number,
334
+ pressure32: number,
335
+ ): ProjectMidiEvent {
336
+ const fn = 'Project.midi2ChannelPressure';
337
+ return midi2Event(fn, ppq, group, 0xd, channel, 0, 0, midi2U32(fn, pressure32, 'pressure32'));
338
+ }
339
+
340
+ /** Pack a MIDI 2.0 pitch-bend event (`bend32` is unsigned 32-bit, center = 0x80000000). */
341
+ static midi2PitchBend(
342
+ ppq: number,
343
+ group: number,
344
+ channel: number,
345
+ bend32: number,
346
+ ): ProjectMidiEvent {
347
+ const fn = 'Project.midi2PitchBend';
348
+ return midi2Event(fn, ppq, group, 0xe, channel, 0, 0, midi2U32(fn, bend32, 'bend32'));
349
+ }
350
+
351
+ /**
352
+ * Pack a MIDI 2.0 program-change event. The bank travels in the same message
353
+ * and is applied only when `bankValid` is true.
354
+ */
355
+ static midi2Program(
356
+ ppq: number,
357
+ group: number,
358
+ channel: number,
359
+ program: number,
360
+ bankValid = false,
361
+ bankMsb = 0,
362
+ bankLsb = 0,
363
+ ): ProjectMidiEvent {
364
+ const fn = 'Project.midi2Program';
365
+ const p = assertU7(fn, program, 'program');
366
+ const msb = assertU7(fn, bankMsb, 'bankMsb');
367
+ const lsb = assertU7(fn, bankLsb, 'bankLsb');
368
+ const word1 = ((p << 24) | (msb << 8) | lsb) >>> 0;
369
+ return midi2Event(fn, ppq, group, 0xc, channel, 0, bankValid ? 1 : 0, word1);
370
+ }
371
+
372
+ /** Pack a MIDI 2.0 registered controller (RPN) event. */
373
+ static midi2RegisteredController(
374
+ ppq: number,
375
+ group: number,
376
+ channel: number,
377
+ bank: number,
378
+ index: number,
379
+ value32: number,
380
+ ): ProjectMidiEvent {
381
+ const fn = 'Project.midi2RegisteredController';
382
+ const b = assertU7(fn, bank, 'bank');
383
+ const i = assertU7(fn, index, 'index');
384
+ return midi2Event(fn, ppq, group, 0x2, channel, b, i, midi2U32(fn, value32, 'value32'));
385
+ }
386
+
387
+ /** Pack a MIDI 2.0 assignable controller (NRPN) event. */
388
+ static midi2AssignableController(
389
+ ppq: number,
390
+ group: number,
391
+ channel: number,
392
+ bank: number,
393
+ index: number,
394
+ value32: number,
395
+ ): ProjectMidiEvent {
396
+ const fn = 'Project.midi2AssignableController';
397
+ const b = assertU7(fn, bank, 'bank');
398
+ const i = assertU7(fn, index, 'index');
399
+ return midi2Event(fn, ppq, group, 0x3, channel, b, i, midi2U32(fn, value32, 'value32'));
400
+ }
401
+
402
+ /** Pack a MIDI 2.0 relative registered controller event (`delta32` is a signed change). */
403
+ static midi2RelativeRegisteredController(
404
+ ppq: number,
405
+ group: number,
406
+ channel: number,
407
+ bank: number,
408
+ index: number,
409
+ delta32: number,
410
+ ): ProjectMidiEvent {
411
+ const fn = 'Project.midi2RelativeRegisteredController';
412
+ const b = assertU7(fn, bank, 'bank');
413
+ const i = assertU7(fn, index, 'index');
414
+ return midi2Event(fn, ppq, group, 0x4, channel, b, i, midi2I32(fn, delta32, 'delta32'));
415
+ }
416
+
417
+ /** Pack a MIDI 2.0 relative assignable controller event (`delta32` is a signed change). */
418
+ static midi2RelativeAssignableController(
419
+ ppq: number,
420
+ group: number,
421
+ channel: number,
422
+ bank: number,
423
+ index: number,
424
+ delta32: number,
425
+ ): ProjectMidiEvent {
426
+ const fn = 'Project.midi2RelativeAssignableController';
427
+ const b = assertU7(fn, bank, 'bank');
428
+ const i = assertU7(fn, index, 'index');
429
+ return midi2Event(fn, ppq, group, 0x5, channel, b, i, midi2I32(fn, delta32, 'delta32'));
430
+ }
431
+
432
+ /** Pack a MIDI 2.0 registered per-note controller event (`index` is 0..255). */
433
+ static midi2RegisteredPerNoteController(
434
+ ppq: number,
435
+ group: number,
436
+ channel: number,
437
+ note: number,
438
+ index: number,
439
+ value32: number,
440
+ ): ProjectMidiEvent {
441
+ const fn = 'Project.midi2RegisteredPerNoteController';
442
+ const n = assertU7(fn, note, 'note');
443
+ const i = midi2Byte(fn, index, 'index');
444
+ return midi2Event(fn, ppq, group, 0x0, channel, n, i, midi2U32(fn, value32, 'value32'));
445
+ }
446
+
447
+ /** Pack a MIDI 2.0 assignable per-note controller event (`index` is 0..255). */
448
+ static midi2AssignablePerNoteController(
449
+ ppq: number,
450
+ group: number,
451
+ channel: number,
452
+ note: number,
453
+ index: number,
454
+ value32: number,
455
+ ): ProjectMidiEvent {
456
+ const fn = 'Project.midi2AssignablePerNoteController';
457
+ const n = assertU7(fn, note, 'note');
458
+ const i = midi2Byte(fn, index, 'index');
459
+ return midi2Event(fn, ppq, group, 0x1, channel, n, i, midi2U32(fn, value32, 'value32'));
460
+ }
461
+
462
+ /** Pack a MIDI 2.0 per-note pitch-bend event (`bend32` is unsigned 32-bit, center = 0x80000000). */
463
+ static midi2PerNotePitchBend(
464
+ ppq: number,
465
+ group: number,
466
+ channel: number,
467
+ note: number,
468
+ bend32: number,
469
+ ): ProjectMidiEvent {
470
+ const fn = 'Project.midi2PerNotePitchBend';
471
+ const n = assertU7(fn, note, 'note');
472
+ return midi2Event(fn, ppq, group, 0x6, channel, n, 0, midi2U32(fn, bend32, 'bend32'));
473
+ }
474
+
475
+ /**
476
+ * Pack a MIDI 2.0 per-note management event. `detach` sets the D flag
477
+ * (detach per-note controllers from voices already sounding on `note`);
478
+ * `reset` sets the S flag (reset the note's per-note controllers).
479
+ */
480
+ static midi2PerNoteManagement(
481
+ ppq: number,
482
+ group: number,
483
+ channel: number,
484
+ note: number,
485
+ detach = false,
486
+ reset = false,
487
+ ): ProjectMidiEvent {
488
+ const fn = 'Project.midi2PerNoteManagement';
489
+ const n = assertU7(fn, note, 'note');
490
+ const flags = (detach ? 0x02 : 0) | (reset ? 0x01 : 0);
491
+ return midi2Event(fn, ppq, group, 0xf, channel, n, flags, 0);
492
+ }
493
+
207
494
  /** Return the General MIDI instrument name for `program`, or `null` when out of range. */
208
495
  static gmInstrumentName(program: number): string | null {
209
496
  return projectModule().midiGmInstrumentName(program);
@@ -335,9 +622,7 @@ export class Project {
335
622
  channel: number,
336
623
  bend: number,
337
624
  ): ProjectMidiEvent {
338
- if (!Number.isInteger(bend) || bend < 0 || bend > 0x3fff) {
339
- throw new RangeError('Project.midiPitchBend: bend must be an integer in [0, 16383]');
340
- }
625
+ assertBoundedInteger('Project.midiPitchBend', bend, 'bend', 0, 0x3fff);
341
626
  return projectMidi1Event(
342
627
  'Project.midiPitchBend',
343
628
  ppq,
@@ -395,7 +680,11 @@ export class Project {
395
680
  return this.native.toJson();
396
681
  }
397
682
 
398
- /** Set the project sample rate in Hz. Must be > 0. */
683
+ /**
684
+ * Set the project sample rate in Hz. Must be in `[8000, 384000]`; anything
685
+ * outside that range throws. Applied through the edit history, so it is
686
+ * undoable.
687
+ */
399
688
  setSampleRate(sampleRate: number): void {
400
689
  this.native.setSampleRate(sampleRate);
401
690
  }
@@ -487,7 +776,24 @@ export class Project {
487
776
  this.native.setTrackMidiDestination(trackId, destinationId);
488
777
  }
489
778
 
490
- /** Set a track's linear playback gain (1.0 = unity; >= 0) via an undoable edit. */
779
+ /**
780
+ * Set a track's linear playback gain (1.0 = unity; >= 0) via an undoable edit.
781
+ *
782
+ * The value reaches the track's audio and MIDI alike, but the stage it lands
783
+ * on follows the track's channel strip. A strip bound by this track alone
784
+ * (including one synthesized for an unbound track) carries the controls on its
785
+ * own fader and panner. A strip several tracks share processes their sum and
786
+ * carries none of them; each track applies its controls upstream instead — on
787
+ * its own clip schedules for audio, on its track lane for MIDI.
788
+ *
789
+ * A MIDI track's gain/pan on a shared strip ride the track lane, which is fed
790
+ * per source track only by an instrument that preserves source-track identity
791
+ * (see {@link setTrackMidiDestination}). An opaque host-callback instrument, or
792
+ * one reporting non-zero latency, renders one buffer per destination and has no
793
+ * per-track stage on a shared strip, so its gain/pan do not reach the bounce
794
+ * there; bind such an instrument to a track with an exclusive strip. Mute and
795
+ * solo are unaffected: a silenced MIDI track schedules no events at all.
796
+ */
491
797
  setTrackGain(trackId: number, gain: number): void {
492
798
  this.native.setTrackGain(trackId, gain);
493
799
  }
@@ -502,7 +808,17 @@ export class Project {
502
808
  this.native.setTrackSolo(trackId, solo);
503
809
  }
504
810
 
505
- /** Set a track's stereo balance in [-1, +1] (0 = center) via an undoable edit. */
811
+ /**
812
+ * Set a track's stereo balance in [-1, +1] (0 = center) via an undoable edit.
813
+ *
814
+ * See {@link setTrackGain} for which stage a track's controls land on. The pan
815
+ * law that shapes the balance belongs to that stage: the strip's configured law
816
+ * on a channel strip and on the clips of an audio track sharing a strip, and
817
+ * the track lane's law for a MIDI track sharing a strip (the law of whatever
818
+ * strip the host bound to that lane, or a linear balance when none is bound).
819
+ * Every law is normalized so a centered track stays at unity and only the away
820
+ * channel is attenuated, so the difference is a taper, not a level offset.
821
+ */
506
822
  setTrackPan(trackId: number, pan: number): void {
507
823
  this.native.setTrackPan(trackId, pan);
508
824
  }
@@ -543,7 +859,18 @@ export class Project {
543
859
  this.native.setMaxHistoryBytes(bytes);
544
860
  }
545
861
 
546
- /** Replace a MIDI clip's entire event list. */
862
+ /**
863
+ * Replace a MIDI clip's entire event list.
864
+ *
865
+ * @remarks
866
+ * Drops the clip's SysEx, which {@link importSmf} and {@link exportSmf} both
867
+ * keep. A clip's SysEx payloads sit beside the event list and are reached by
868
+ * a handle {@link ProjectMidiEvent} does not carry, so replacing the list
869
+ * leaves nothing referring to them: a GS setup block that survives an import
870
+ * and an export byte for byte is gone after one call here. Nothing reads the
871
+ * handles back either, so a caller that must keep the setup edits the
872
+ * exported file rather than the event list.
873
+ */
547
874
  setMidiEvents(
548
875
  clipId: number,
549
876
  events: ReadonlyArray<ProjectMidiEvent | readonly [number, number, number]>,
@@ -561,8 +888,15 @@ export class Project {
561
888
  return this.native.importSmf(data);
562
889
  }
563
890
 
564
- /** Export the project's tempo map + MIDI clips to an SMF byte buffer. */
565
- exportSmf(): Uint8Array {
891
+ /**
892
+ * Export the project's tempo map + MIDI clips to an SMF byte buffer.
893
+ *
894
+ * @remarks
895
+ * The buffer owns a plain `ArrayBuffer`, which is what the `Blob` / `File`
896
+ * constructors accept — so `new Blob([project.exportSmf()])` compiles without
897
+ * a copy through `new Uint8Array(...)` first.
898
+ */
899
+ exportSmf(): Uint8Array<ArrayBuffer> {
566
900
  return this.native.exportSmf();
567
901
  }
568
902
 
@@ -580,8 +914,12 @@ export class Project {
580
914
  * Export the project's tempo map + MIDI clips to a MIDI 2.0 Clip File
581
915
  * (`SMF2CLIP`) byte buffer. MIDI 2.0-only events are written without loss —
582
916
  * prefer this over {@link exportSmf} when MIDI 2.0 fidelity matters.
917
+ *
918
+ * @remarks
919
+ * As with {@link exportSmf}, the buffer owns a plain `ArrayBuffer` and goes
920
+ * straight into a `Blob`.
583
921
  */
584
- exportClipFile(): Uint8Array {
922
+ exportClipFile(): Uint8Array<ArrayBuffer> {
585
923
  return this.native.exportClipFile();
586
924
  }
587
925
 
@@ -653,19 +991,62 @@ export class Project {
653
991
  return this.native.validateMidiNotes(clipId);
654
992
  }
655
993
 
994
+ /**
995
+ * Transcribe mono audio straight into a MIDI clip's event list, **replacing**
996
+ * whatever it held — exactly as {@link setMidiEvents} does.
997
+ *
998
+ * The PPQ grid is this project's own tempo map, which is why there is no
999
+ * `tempoBpm` field: a project whose tempo was installed by {@link autoTempo}
1000
+ * transcribes onto that map rather than onto a second, separately detected
1001
+ * tempo. Use the standalone `transcribe` when you want events without a
1002
+ * project.
1003
+ *
1004
+ * Quantizing, tempo detection and key/chord annotation are not done here —
1005
+ * see `transcribe` for what each belongs to.
1006
+ *
1007
+ * @returns the number of notes written (half the events)
1008
+ * @throws {RangeError} on empty `samples`, a non-finite sample, or a
1009
+ * `sampleRate` outside `[8000, 384000]`
1010
+ * @throws {SonareError} `InvalidParameter` when `clipId` is unknown or not a
1011
+ * MIDI clip, or on an option outside its domain; `NotSupported` when the
1012
+ * library was built without the pitch editor
1013
+ */
1014
+ transcribeToClip(request: ProjectTranscribeRequest): number {
1015
+ assertSamples('Project.transcribeToClip', request.samples, true);
1016
+ assertSampleRate('Project.transcribeToClip', request.sampleRate);
1017
+ return this.native.transcribeToClip(
1018
+ request.clipId,
1019
+ request.samples,
1020
+ request.sampleRate,
1021
+ request,
1022
+ );
1023
+ }
1024
+
656
1025
  /** Return ranked tempo-octave and detected-meter candidates without editing. */
657
- analyzeTempo(audio: Float32Array, sampleRate: number): ProjectTempoCandidate[] {
658
- return this.native.analyzeTempo(audio, sampleRate);
1026
+ analyzeTempo(
1027
+ audio: Float32Array,
1028
+ sampleRate: number,
1029
+ options?: ProjectTempoOptions,
1030
+ ): ProjectTempoCandidate[] {
1031
+ return this.native.analyzeTempo(audio, sampleRate, options);
659
1032
  }
660
1033
 
661
- /** Detect and install a ranked tempo candidate; optionally apply detected meter. */
1034
+ /**
1035
+ * Detect and install a ranked tempo candidate; optionally apply detected meter.
1036
+ *
1037
+ * @remarks
1038
+ * `candidateIndex` indexes the ranking {@link analyzeTempo} produced, so pair
1039
+ * the two on the same `options`. Read the installed map back with
1040
+ * {@link tempoSegmentCount} and {@link tempoSegmentByIndex}.
1041
+ */
662
1042
  autoTempo(
663
1043
  audio: Float32Array,
664
1044
  sampleRate: number,
665
1045
  candidateIndex = 0,
666
1046
  applyTimeSignatures = false,
1047
+ options?: ProjectTempoOptions,
667
1048
  ): number {
668
- return this.native.autoTempo(audio, sampleRate, candidateIndex, applyTimeSignatures);
1049
+ return this.native.autoTempo(audio, sampleRate, candidateIndex, applyTimeSignatures, options);
669
1050
  }
670
1051
 
671
1052
  /** Snap to a bar (`division=0`), beat (`1`), or beat subdivision (`2+`). */
@@ -727,9 +1108,9 @@ export class Project {
727
1108
 
728
1109
  /**
729
1110
  * Compile + render the project offline, routing MIDI tracks through the
730
- * patch-driven NativeSynth — the full synthesizer (subtractive / FM /
731
- * Karplus-Strong / modal / additive / percussion / extended-waveguide-piano
732
- * engines plus the realism layer). Pass a {@link SynthPatch}, a preset-name
1111
+ * patch-driven NativeSynth — the full synthesizer (every
1112
+ * {@link SynthEngineMode} engine plus the realism layer; the modes are
1113
+ * enumerated by {@link SYNTH_ENGINE_MODES}). Pass a {@link SynthPatch}, a preset-name
733
1114
  * string (`'saw-lead'` / `'va:saw-lead'`; see {@link synthPresetNames}), or
734
1115
  * an array of either; each object entry may carry `destinationId` (default
735
1116
  * 0) and `useGmPrograms` (default `false`) binding conveniences, neither of
@@ -740,12 +1121,19 @@ export class Project {
740
1121
  * both create one default binding. Use an explicitly empty array `[]` (or
741
1122
  * runtime `null`) for zero bindings. Unknown preset names throw.
742
1123
  * Deterministic for a fixed project + options + patch.
1124
+ *
1125
+ * An `engineMode: 'sample'` patch reads its PCM from the {@link SampleBank}
1126
+ * passed as `sampleBank`; the bank must still be alive when the bounce runs,
1127
+ * and one bound without a bank renders silence.
743
1128
  */
744
1129
  bounceWithSynthInstrument(
745
1130
  instrument: SynthPatch | string | ReadonlyArray<SynthPatch | string> = {},
746
1131
  options: ProjectBounceOptions = {},
747
1132
  ): Float32Array {
748
- return this.native.bounceWithSynthInstrument(instrument, options);
1133
+ const normalized = Array.isArray(instrument)
1134
+ ? instrument.map((entry) => normalizeSynthInstrument(entry))
1135
+ : normalizeSynthInstrument(instrument);
1136
+ return this.native.bounceWithSynthInstrument(normalized, options);
749
1137
  }
750
1138
 
751
1139
  /**
@@ -895,7 +1283,10 @@ export class Project {
895
1283
  if (desc.targetParamId === 0) {
896
1284
  throw new RangeError('project automation lane targetParamId must be non-zero');
897
1285
  }
898
- const nativeDesc: ProjectAutomationLaneDesc = { ...desc };
1286
+ const nativeDesc: ProjectAutomationLaneDesc = {
1287
+ ...desc,
1288
+ points: desc.points.map(projectAutomationPointValue),
1289
+ };
899
1290
  if (Object.keys(desc).includes('targetKind')) {
900
1291
  nativeDesc.targetKind = projectAutomationTargetKindValue(
901
1292
  desc.targetKind as ProjectAutomationTargetKind,
@@ -913,7 +1304,10 @@ export class Project {
913
1304
  if (desc.targetParamId === 0) {
914
1305
  throw new RangeError('project automation lane targetParamId must be non-zero');
915
1306
  }
916
- const nativeDesc: ProjectAutomationLaneDesc = { ...desc };
1307
+ const nativeDesc: ProjectAutomationLaneDesc = {
1308
+ ...desc,
1309
+ points: desc.points.map(projectAutomationPointValue),
1310
+ };
917
1311
  if (Object.keys(desc).includes('targetKind')) {
918
1312
  nativeDesc.targetKind = projectAutomationTargetKindValue(
919
1313
  desc.targetKind as ProjectAutomationTargetKind,
@@ -1097,6 +1491,28 @@ export class Project {
1097
1491
  return this.native.tempoSegmentCount();
1098
1492
  }
1099
1493
 
1494
+ /**
1495
+ * Reads a tempo segment by index, in stored order.
1496
+ *
1497
+ * @param index - Zero-based index below {@link tempoSegmentCount}
1498
+ * @returns The segment, in the shape {@link setTempoSegments} accepts
1499
+ * @throws When the index is at or past the count
1500
+ */
1501
+ tempoSegmentByIndex(index: number): ProjectTempoSegment {
1502
+ return this.native.tempoSegmentByIndex(index);
1503
+ }
1504
+
1505
+ /**
1506
+ * Reads a time-signature segment by index, in stored order.
1507
+ *
1508
+ * @param index - Zero-based index below {@link timeSignatureCount}
1509
+ * @returns The segment, in the shape {@link setTimeSignatures} accepts
1510
+ * @throws When the index is at or past the count
1511
+ */
1512
+ timeSignatureByIndex(index: number): ProjectTimeSignatureSegment {
1513
+ return this.native.timeSignatureByIndex(index);
1514
+ }
1515
+
1100
1516
  /** Number of time-signature segments on the project. */
1101
1517
  timeSignatureCount(): number {
1102
1518
  return this.native.timeSignatureCount();
@@ -1114,15 +1530,22 @@ export class Project {
1114
1530
 
1115
1531
  /**
1116
1532
  * Compile diagnostics produced by the most recent bounce on this project
1117
- * (e.g. MIDI clips rendering silently without a bound instrument). When no
1118
- * bounce has run, the result is empty with `hasTimeline` set.
1533
+ * (e.g. MIDI clips rendering silently without a bound instrument). On a
1534
+ * project no bounce has ever run on, the result is empty in full:
1535
+ * `hasTimeline` is `false` and `diagnostics` is empty. A failed bounce is
1536
+ * distinguishable from that state, because a bounce only loses its timeline
1537
+ * through an error diagnostic and so always reports at least one.
1119
1538
  */
1120
1539
  lastBounceCompileResult(): ProjectCompileResult {
1121
1540
  return this.native.lastBounceCompileResult();
1122
1541
  }
1123
1542
 
1124
- /** Release the underlying WASM object. Safe to call only once. */
1543
+ /** Release the underlying WASM object. Idempotent, as the Node facade is. */
1125
1544
  delete(): void {
1545
+ if (this.released) {
1546
+ return;
1547
+ }
1548
+ this.released = true;
1126
1549
  this.native.delete();
1127
1550
  }
1128
1551