@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
@@ -7,11 +7,170 @@ import type {
7
7
  MasteringPreset,
8
8
  } from './public_types';
9
9
  import type { ProgressCallback } from './sonare.js';
10
+ import type { ValidateOptions } from './validation';
11
+ import { assertSamples } from './validation';
10
12
 
11
13
  function requireModule() {
12
14
  return getSonareModule();
13
15
  }
14
16
 
17
+ export type NormalizeMode = 'peak' | 'rms';
18
+
19
+ // `context` is the calling entry point's name: the refusal is about that
20
+ // caller's argument, so naming a fixed one would send a normalizeStereo user
21
+ // looking at normalize.
22
+ function resolveNormalizeMode(value: unknown, context = 'normalize'): NormalizeMode {
23
+ if (value === undefined) {
24
+ return 'peak';
25
+ }
26
+ if (typeof value !== 'string') {
27
+ throw new TypeError(`${context}: mode must be the string 'peak' or 'rms'`);
28
+ }
29
+ if (value !== 'peak' && value !== 'rms') {
30
+ throw new RangeError(`${context}: mode must be the string 'peak' or 'rms'`);
31
+ }
32
+ return value;
33
+ }
34
+
35
+ export interface NormalizeRequest extends ValidateOptions {
36
+ samples: Float32Array;
37
+ sampleRate?: number;
38
+ targetDb?: number;
39
+ mode?: NormalizeMode;
40
+ }
41
+
42
+ /**
43
+ * Normalize audio to a target peak or RMS level.
44
+ *
45
+ * @param samples - Audio samples (mono, float32)
46
+ * @param sampleRate - Sample rate in Hz (default: 22050)
47
+ * @param targetDb - Finite target at or below 0 dBFS (default: 0 dB = full scale).
48
+ * For `mode: 'peak'`, this is the peak target; for `mode: 'rms'`, this is the RMS target.
49
+ * @param mode - Normalization mode: `'peak'` (default) or `'rms'`.
50
+ * @returns Normalized audio
51
+ */
52
+ export function normalize(request: NormalizeRequest): Float32Array;
53
+ export function normalize(
54
+ samples: Float32Array,
55
+ sampleRate: number,
56
+ targetDb?: number,
57
+ options?: ValidateOptions,
58
+ ): Float32Array;
59
+ export function normalize(
60
+ samples: Float32Array,
61
+ sampleRate: number,
62
+ targetDb?: number,
63
+ mode?: NormalizeMode,
64
+ options?: ValidateOptions,
65
+ ): Float32Array;
66
+ export function normalize(
67
+ samples: Float32Array | NormalizeRequest,
68
+ sampleRate?: number,
69
+ targetDb = 0.0,
70
+ modeOrOptions: NormalizeMode | ValidateOptions = 'peak',
71
+ options: ValidateOptions = {},
72
+ ): Float32Array {
73
+ if (
74
+ modeOrOptions !== undefined &&
75
+ modeOrOptions !== null &&
76
+ typeof modeOrOptions !== 'string' &&
77
+ typeof modeOrOptions !== 'object'
78
+ ) {
79
+ throw new TypeError("normalize: mode must be the string 'peak' or 'rms'");
80
+ }
81
+ if (modeOrOptions === null) {
82
+ throw new TypeError("normalize: mode must be the string 'peak' or 'rms'");
83
+ }
84
+ const positionalOptions =
85
+ typeof modeOrOptions === 'object' && modeOrOptions !== null ? modeOrOptions : options;
86
+ const positionalMode = typeof modeOrOptions === 'string' ? modeOrOptions : undefined;
87
+ const request: NormalizeRequest =
88
+ samples instanceof Float32Array
89
+ ? { samples, sampleRate, targetDb, mode: positionalMode, ...positionalOptions }
90
+ : samples;
91
+ assertSamples('normalize', request.samples, request.validate !== false);
92
+ const mode = resolveNormalizeMode(request.mode);
93
+ return requireModule().normalizeEx(
94
+ request.samples,
95
+ request.sampleRate ?? 22050,
96
+ request.targetDb ?? 0.0,
97
+ mode,
98
+ );
99
+ }
100
+
101
+ export interface NormalizeStereoRequest extends ValidateOptions {
102
+ left: Float32Array;
103
+ right: Float32Array;
104
+ sampleRate?: number;
105
+ targetDb?: number;
106
+ mode?: NormalizeMode;
107
+ }
108
+
109
+ /** A normalized channel pair and the gain both channels were moved by. */
110
+ export interface NormalizeStereoResult {
111
+ left: Float32Array;
112
+ right: Float32Array;
113
+ /**
114
+ * One figure rather than a pair: the gain is one decision shared by both
115
+ * channels. Silence leaves the pair untouched and reports 0.
116
+ */
117
+ appliedGainDb: number;
118
+ }
119
+
120
+ /**
121
+ * Normalize a stereo pair on a gain measured across both channels.
122
+ *
123
+ * Normalizing the two channels separately lifts the quieter one until the peaks
124
+ * match, which changes the balance rather than the level. The level here is read
125
+ * from the pair and the resulting gain goes to both channels, so the image is
126
+ * preserved: for `mode: 'peak'` the louder channel reaches `targetDb` and the
127
+ * other keeps its distance from it; for `mode: 'rms'` the quantity driven to
128
+ * `targetDb` is the root mean square over both channels' samples together — the
129
+ * quadratic mean of the per-channel figures, not their average — and the output
130
+ * is hard-clipped to [-1, 1].
131
+ *
132
+ * @param request.left - Left channel samples (float32)
133
+ * @param request.right - Right channel samples, same length as `left`
134
+ * @param request.sampleRate - Sample rate in Hz (default: 22050)
135
+ * @param request.targetDb - Finite target at or below 0 dBFS. Defaults to 0 for
136
+ * `mode: 'peak'` and -20 for `mode: 'rms'`, matching the library and the other
137
+ * language surfaces.
138
+ * @param request.mode - `'peak'` (default) or `'rms'`
139
+ * @returns The normalized pair and the shared gain in dB
140
+ * @throws RangeError when the two channels differ in length
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * const { left, right, appliedGainDb } = normalizeStereo({
145
+ * left: leftSamples,
146
+ * right: rightSamples,
147
+ * sampleRate: 44100,
148
+ * targetDb: -1,
149
+ * });
150
+ * ```
151
+ */
152
+ export function normalizeStereo(request: NormalizeStereoRequest): NormalizeStereoResult {
153
+ assertSamples('normalizeStereo', request.left, request.validate !== false);
154
+ assertSamples('normalizeStereo', request.right, request.validate !== false);
155
+ if (request.left.length !== request.right.length) {
156
+ throw new RangeError('Stereo channel lengths must match.');
157
+ }
158
+ const mode = resolveNormalizeMode(request.mode, 'normalizeStereo');
159
+ // Mode-dependent, unlike the mono `normalize` on this surface, which defaults
160
+ // to 0 dB in both modes. 0 dBFS RMS is not a usable default -- the peaks sit
161
+ // well above the RMS, so every one of them clips -- and the library and the
162
+ // other surfaces all default RMS to -20. A new entry point takes the shared
163
+ // default rather than inheriting a surface-local one.
164
+ const targetDb = request.targetDb ?? (mode === 'rms' ? -20.0 : 0.0);
165
+ return requireModule().normalizeStereo(
166
+ request.left,
167
+ request.right,
168
+ request.sampleRate ?? 22050,
169
+ targetDb,
170
+ mode,
171
+ );
172
+ }
173
+
15
174
  /** Internal envelope selecting the core dotted-param parser in the embind layer. */
16
175
  function canonicalChainConfig(config: MasteringChainConfig): Record<string, unknown> {
17
176
  return { __flatParams: flattenChainConfig(config) };
@@ -279,6 +438,31 @@ export function masteringPresetNames(): MasteringPreset[] {
279
438
  return Array.from(requireModule().masteringPresetNames()) as MasteringPreset[];
280
439
  }
281
440
 
441
+ /**
442
+ * The flat `{key: number|boolean}` params of preset `preset`'s built-in chain
443
+ * configuration, in the same key space {@link masteringAssistantSuggestChain}
444
+ * returns. Passing this straight through as `overrides` to {@link masterAudio}
445
+ * reproduces the preset unchanged, bit for bit in the C++ core.
446
+ *
447
+ * @param preset - Preset identifier from {@link masteringPresetNames}.
448
+ * @throws For an unknown `preset`.
449
+ */
450
+ export function masteringPresetParams(preset: MasteringPreset): Record<string, number | boolean> {
451
+ return requireModule().masteringPresetParams(preset);
452
+ }
453
+
454
+ /**
455
+ * List the delivery targets the mastering assistant accepts as `targetPlatform`.
456
+ *
457
+ * Read from the library rather than from a list kept here, so a target added in
458
+ * the core is discoverable without a binding change.
459
+ *
460
+ * @returns Target names in index order (e.g. "streaming", "broadcast", "club")
461
+ */
462
+ export function masteringPlatformNames(): string[] {
463
+ return Array.from(requireModule().masteringPlatformNames());
464
+ }
465
+
282
466
  /**
283
467
  * Apply a named mastering preset chain to mono audio.
284
468
  *
@@ -1,5 +1,10 @@
1
+ import { ErrorCode, SonareError } from './errors';
1
2
  import { getSonareModule } from './module_state';
2
3
  import type {
4
+ LoudnessMatchResult,
5
+ MasteringAssistantParams,
6
+ MasteringInsertParamChoice,
7
+ MasteringInsertSlot,
3
8
  MasteringOptions,
4
9
  MasteringProcessorParams,
5
10
  MasteringResult,
@@ -11,6 +16,8 @@ import type {
11
16
  StreamingPlatform,
12
17
  } from './public_types';
13
18
 
19
+ export type { MasteringInsertParamChoice, MasteringInsertSlot };
20
+
14
21
  function requireModule() {
15
22
  return getSonareModule();
16
23
  }
@@ -45,6 +52,15 @@ export interface MasteringPairProcessRequest {
45
52
  params?: MasteringProcessorParams;
46
53
  }
47
54
 
55
+ /** Canonical request form for {@link masteringAbMatchLoudness}. */
56
+ export interface MasteringAbMatchLoudnessRequest {
57
+ /** The take to gain-match. */
58
+ source: Float32Array;
59
+ /** The take whose loudness `source` is matched to; returned untouched. */
60
+ reference: Float32Array;
61
+ sampleRate?: number;
62
+ }
63
+
48
64
  /** Canonical request form for a two-input match analysis. */
49
65
  export interface MasteringPairAnalyzeRequest {
50
66
  analysisName: PairAnalysis;
@@ -70,6 +86,13 @@ export interface MasteringSamplesParamsRequest {
70
86
  params?: MasteringProcessorParams;
71
87
  }
72
88
 
89
+ /** Canonical request form for the assistant, whose params carry a target platform. */
90
+ export interface MasteringAssistantParamsRequest {
91
+ samples: Float32Array;
92
+ sampleRate?: number;
93
+ params?: MasteringAssistantParams;
94
+ }
95
+
73
96
  /** Canonical request form for streaming-platform preview. */
74
97
  export interface MasteringStreamingPreviewRequest {
75
98
  samples: Float32Array;
@@ -85,6 +108,14 @@ export interface MasteringStereoParamsRequest {
85
108
  params?: MasteringProcessorParams;
86
109
  }
87
110
 
111
+ /** Stereo counterpart of {@link MasteringAssistantParamsRequest}. */
112
+ export interface MasteringAssistantStereoParamsRequest {
113
+ left: Float32Array;
114
+ right: Float32Array;
115
+ sampleRate?: number;
116
+ params?: MasteringAssistantParams;
117
+ }
118
+
88
119
  /** Canonical request form for the stereo streaming-platform preview. */
89
120
  export interface MasteringStreamingPreviewStereoRequest {
90
121
  left: Float32Array;
@@ -137,9 +168,7 @@ export function masteringProcessorNames(): SoloProcessor[] {
137
168
  * `sonare_mastering_insert_names` (which joins this list) as a `string[]`.
138
169
  */
139
170
  export function masteringInsertNames(): string[] {
140
- return (
141
- requireModule() as unknown as { masteringInsertNames: () => string[] }
142
- ).masteringInsertNames();
171
+ return requireModule().masteringInsertNames();
143
172
  }
144
173
 
145
174
  /**
@@ -153,43 +182,165 @@ export function masteringInsertNames(): string[] {
153
182
  * @param name - Insert processor name (see {@link masteringInsertNames}).
154
183
  */
155
184
  export function masteringInsertParamNames(name: string): string[] {
156
- return Array.from(
157
- (
158
- requireModule() as unknown as { masteringInsertParamNames: (name: string) => string[] }
159
- ).masteringInsertParamNames(name),
160
- );
185
+ return Array.from(requireModule().masteringInsertParamNames(name));
161
186
  }
162
187
 
163
- /** One realtime-automatable parameter of an insert processor. */
188
+ /**
189
+ * One parameter an insert processor's construction reads, whether or not it
190
+ * is realtime-automatable.
191
+ */
164
192
  export interface MasteringInsertParamInfo {
165
193
  /** JSON-key parameter name, as used in scene insert params. */
166
194
  name: string;
167
- /** Integer param id for realtime automation lanes / MIDI-CC binding. */
168
- id: number;
169
- /** Whether the param can be changed live from the audio thread. */
195
+ /**
196
+ * Integer param id for realtime automation lanes / MIDI-CC binding, or null
197
+ * for a construction-only key with no automation target.
198
+ */
199
+ id: number | null;
200
+ /** Whether the param can be changed live from the audio thread; false when `id` is null. */
170
201
  rtSafe: boolean;
171
- /** Physical unit when the parameter is not unitless. */
172
- unit?: string;
202
+ /**
203
+ * The C++ type the processor's config builder reads the key as. `"enum"` is
204
+ * sent as the number in its `choices` entry; `"string"` / `"array"` (an
205
+ * embedded impulse response, a per-band list) is construction-only and
206
+ * reports null for `min`, `max`, `default` and `choices`.
207
+ */
208
+ type: 'boolean' | 'number' | 'enum' | 'string' | 'array';
209
+ /**
210
+ * Smallest value construction accepts, or null when the catalog states no
211
+ * limit or `choices` is non-null. Measured, so it is a hard constraint
212
+ * rather than a UI range; see {@link CapabilityCatalogParameter} for what a
213
+ * measured bound does and does not promise.
214
+ */
215
+ min: number | null;
216
+ /** Largest value construction accepts, or null when the catalog states no limit or `choices` is non-null. */
217
+ max: number | null;
218
+ /**
219
+ * Value the processor uses when the key is absent — the config struct's own
220
+ * field initializer, an enum as its number. Null for a param id with no
221
+ * construction key, a `"string"` / `"array"` key, or a construction key with
222
+ * no fallback.
223
+ */
224
+ default: boolean | number | null;
225
+ /** Physical unit, or null when the parameter is unitless. */
226
+ unit: string | null;
227
+ /**
228
+ * The closed set of values construction accepts, in value order, or null
229
+ * when the accepted values are not a closed set. Non-null only for `"enum"`
230
+ * (every declared enumerator construction accepts) or a `"number"` key
231
+ * whose accepted integers have holes; `min` / `max` are then both null.
232
+ */
233
+ choices: MasteringInsertParamChoice[] | null;
234
+ /**
235
+ * The {@link MasteringInsertSlot} this key belongs to, or null for a key that
236
+ * always exists.
237
+ */
238
+ slot: string | null;
173
239
  }
174
240
 
175
241
  /**
176
- * Returns the realtime-automatable parameter descriptors for an insert / FX
177
- * processor: each entry maps a JSON-key parameter name to the integer id used by
178
- * realtime automation and reports whether it is realtime-safe. Unlike
179
- * {@link masteringInsertParamNames} (every construction key), this lists only the
180
- * realtime-controllable subset — the keys accepted by
181
- * {@link RealtimeEngine.setTrackStripInsertParamByName}. Returns an empty array
182
- * for an unknown name or a processor with no automatable parameters.
242
+ * Returns every parameter an insert / FX processor's construction reads,
243
+ * including construction-time-only keys with no realtime automation target.
244
+ * Entries come in two runs: first the processor's realtime automation
245
+ * targets in id order (the keys accepted by
246
+ * {@link RealtimeEngine.setTrackStripInsertParamByName}, `id` non-null); then,
247
+ * sorted by name, every other construction key with `id` null and `rtSafe`
248
+ * false. The name set matches {@link masteringInsertParamNames} plus any
249
+ * automation target construction does not read. Returns an empty array for an
250
+ * unknown name.
183
251
  *
184
252
  * @param name - Insert processor name (see {@link masteringInsertNames}).
185
253
  */
186
254
  export function masteringInsertParamInfo(name: string): MasteringInsertParamInfo[] {
187
- const json = (
188
- requireModule() as unknown as { masteringInsertParamInfo: (name: string) => string }
189
- ).masteringInsertParamInfo(name);
255
+ const json = requireModule().masteringInsertParamInfo(name);
190
256
  return JSON.parse(json) as MasteringInsertParamInfo[];
191
257
  }
192
258
 
259
+ /** Latency and tail of one insert instance, in samples. */
260
+ export interface MasteringInsertTiming {
261
+ /** Latency in samples at the queried sample rate. */
262
+ latencySamples: number;
263
+ /** Audible decay tail in samples at the queried sample rate. */
264
+ tailSamples: number;
265
+ }
266
+
267
+ /** One built-in amp-sim rig with its resolved starting configuration. */
268
+ export interface MasteringAmpPresetCatalogEntry {
269
+ /** Stable index used by the amp-sim `presetIndex` parameter. */
270
+ index: number;
271
+ /** Canonical preset identifier accepted by the amp-sim insert. */
272
+ name: string;
273
+ /** Effective values from the core preset, before sparse user overrides. */
274
+ params: Record<string, number | boolean>;
275
+ }
276
+
277
+ /**
278
+ * Returns the built-in amp-sim rigs and their resolved control values.
279
+ *
280
+ * The catalog is read-only metadata for hosts such as Studio. Persist only the
281
+ * preset index and explicit overrides in a project so future core updates can
282
+ * continue to define the canonical DSP configuration.
283
+ */
284
+ export function masteringAmpPresetCatalog(): MasteringAmpPresetCatalogEntry[] {
285
+ const json = requireModule().masteringAmpPresetCatalog();
286
+ return JSON.parse(json) as MasteringAmpPresetCatalogEntry[];
287
+ }
288
+
289
+ /**
290
+ * Reject a `params` value {@link masteringInsertTiming} cannot serialize:
291
+ * anything other than a finite number or a boolean, naming the offending key.
292
+ * Booleans serialize as JSON booleans, not 0/1, since the C++ reader accepts
293
+ * `is_bool` alongside a number.
294
+ */
295
+ function insertTimingParamsToJson(fnName: string, params: Record<string, number | boolean>) {
296
+ const out: Record<string, number | boolean> = {};
297
+ for (const [key, value] of Object.entries(params)) {
298
+ if (typeof value === 'boolean') {
299
+ out[key] = value;
300
+ continue;
301
+ }
302
+ if (typeof value !== 'number' || !Number.isFinite(value)) {
303
+ throw new SonareError(
304
+ ErrorCode.InvalidParameter,
305
+ 'InvalidParameter',
306
+ `${fnName}: params.${key} must be a finite number or boolean`,
307
+ );
308
+ }
309
+ out[key] = value;
310
+ }
311
+ return JSON.stringify(out);
312
+ }
313
+
314
+ /**
315
+ * Latency and tail of insert `name` built from `params` and prepared at
316
+ * `sampleRate` (`mastering::api::insert_timing`). Answers for the exact
317
+ * instance a scene or strip would build — an oversampled saturation path, a
318
+ * linear-phase crossover, a lookahead all change the reported latency. The
319
+ * capability catalog's `latencySamples` / `tailSamples` are this query at
320
+ * default parameters and 48 kHz. `effects.reverb.convolution` answers for its
321
+ * configuration without an impulse response: its latency is its fixed
322
+ * partition size and does not depend on one.
323
+ *
324
+ * A key `name`'s construction does not read is refused rather than ignored,
325
+ * because an ignored key would answer for a configuration the caller did not
326
+ * ask for.
327
+ *
328
+ * @param name - Insert processor name (see {@link masteringInsertNames}).
329
+ * @param params - Flat parameter values, keyed as in {@link masteringInsertParamInfo}.
330
+ * @param sampleRate - Rate the insert is prepared at.
331
+ * @throws For an unknown `name`, a key the insert does not read, a value its
332
+ * construction or `prepare` refuses, or a `params` value that is not a
333
+ * finite number or boolean.
334
+ */
335
+ export function masteringInsertTiming(
336
+ name: string,
337
+ params: Record<string, number | boolean>,
338
+ sampleRate: number,
339
+ ): MasteringInsertTiming {
340
+ const json = insertTimingParamsToJson('masteringInsertTiming', params);
341
+ return requireModule().masteringInsertTiming(name, json, sampleRate);
342
+ }
343
+
193
344
  /**
194
345
  * How a processor handles a buffer with more than two channels (a surround
195
346
  * bed). "multichannel" processes every plane in one call; "stereoPairOnly"
@@ -205,6 +356,25 @@ export type MasteringChannelPolicy =
205
356
  /** Coarse algorithmic work estimate for a realtime insert; not a benchmark. */
206
357
  export type MasteringRealtimeCost = 'low' | 'moderate' | 'high';
207
358
 
359
+ /**
360
+ * Catalog grouping for a processor picker, derived from the id's prefix
361
+ * ("eq.*" -> "eq", "match.*" -> "reference"); anything unprefixed is "other".
362
+ */
363
+ export type MasteringProcessorCategory =
364
+ | 'dynamics'
365
+ | 'effects'
366
+ | 'eq'
367
+ | 'final'
368
+ | 'maximizer'
369
+ | 'multiband'
370
+ | 'other'
371
+ | 'reference'
372
+ | 'repair'
373
+ | 'saturation'
374
+ | 'spectral'
375
+ | 'stereo'
376
+ | 'utility';
377
+
208
378
  /** One processor's realtime/offline/pair classification in the catalog. */
209
379
  export interface MasteringProcessorCatalogEntry {
210
380
  /** Processor id (the name used for scene inserts / named processors). */
@@ -237,6 +407,19 @@ export interface MasteringProcessorCatalogEntry {
237
407
  * surround planes passed through dry).
238
408
  */
239
409
  channelPolicy: MasteringChannelPolicy;
410
+ /** Grouping for a processor picker; see {@link MasteringProcessorCategory}. */
411
+ category: MasteringProcessorCategory;
412
+ /**
413
+ * The processor's construction parameters, the same list
414
+ * {@link masteringInsertParamInfo} returns. Empty for entries that are not
415
+ * realtime-insertable.
416
+ */
417
+ params: MasteringInsertParamInfo[];
418
+ /**
419
+ * The insert's conditional key groups in declaration order, named by each
420
+ * parameter's `slot`. Empty for entries that are not realtime-insertable.
421
+ */
422
+ slots: MasteringInsertSlot[];
240
423
  }
241
424
 
242
425
  /**
@@ -246,9 +429,7 @@ export interface MasteringProcessorCatalogEntry {
246
429
  * instead of offering ids the realtime strip would reject.
247
430
  */
248
431
  export function masteringProcessorCatalog(): MasteringProcessorCatalogEntry[] {
249
- const json = (
250
- requireModule() as unknown as { masteringProcessorCatalog: () => string }
251
- ).masteringProcessorCatalog();
432
+ const json = requireModule().masteringProcessorCatalog();
252
433
  return JSON.parse(json) as MasteringProcessorCatalogEntry[];
253
434
  }
254
435
 
@@ -405,6 +586,36 @@ export function masteringPairAnalyze(
405
586
  );
406
587
  }
407
588
 
589
+ /**
590
+ * Gain-match `source` to `reference`'s integrated loudness, so an A/B between
591
+ * the two is not decided by level. `source` and `reference` may have
592
+ * independent lengths.
593
+ *
594
+ * The gain is applied with no upper bound and `matchedTruePeakDbtp` reports
595
+ * where that left the peak, rather than the call capping it: a headroom clamp
596
+ * would return `source` at its own loudness whenever it started near full
597
+ * scale. Both loudness values are non-finite for a silent or below-gate take,
598
+ * and `appliedGainDb` is then 0.
599
+ *
600
+ * @example
601
+ * ```ts
602
+ * const { samples, appliedGainDb, matchedTruePeakDbtp } = masteringAbMatchLoudness({
603
+ * source: take,
604
+ * reference: master,
605
+ * sampleRate: 48000,
606
+ * });
607
+ * ```
608
+ */
609
+ export function masteringAbMatchLoudness(
610
+ request: MasteringAbMatchLoudnessRequest,
611
+ ): LoudnessMatchResult {
612
+ return requireModule().masteringAbMatchLoudness(
613
+ request.source,
614
+ request.reference,
615
+ request.sampleRate ?? 22050,
616
+ );
617
+ }
618
+
408
619
  export function masteringStereoAnalyze(request: MasteringStereoAnalyzeRequest): string;
409
620
  export function masteringStereoAnalyze(
410
621
  analysisName: StereoAnalysis,
@@ -439,16 +650,16 @@ export function masteringStereoAnalyze(
439
650
  );
440
651
  }
441
652
 
442
- export function masteringAssistantSuggest(request: MasteringSamplesParamsRequest): string;
653
+ export function masteringAssistantSuggest(request: MasteringAssistantParamsRequest): string;
443
654
  export function masteringAssistantSuggest(
444
655
  samples: Float32Array,
445
656
  sampleRate?: number,
446
- params?: MasteringProcessorParams,
657
+ params?: MasteringAssistantParams,
447
658
  ): string;
448
659
  export function masteringAssistantSuggest(
449
- samples: Float32Array | MasteringSamplesParamsRequest,
660
+ samples: Float32Array | MasteringAssistantParamsRequest,
450
661
  sampleRate = 22050,
451
- params: MasteringProcessorParams = {},
662
+ params: MasteringAssistantParams = {},
452
663
  ): string {
453
664
  const request = samples instanceof Float32Array ? { samples, sampleRate, params } : samples;
454
665
  return requireModule().masteringAssistantSuggest(
@@ -458,6 +669,91 @@ export function masteringAssistantSuggest(
458
669
  );
459
670
  }
460
671
 
672
+ /**
673
+ * Suggest a mastering chain, as the flat `{key: number|boolean}` params map
674
+ * {@link masteringAssistantSuggest}'s `chainConfig` carries, without needing to
675
+ * pull it out of the full assistant document. The returned map can be passed
676
+ * straight through as `overrides` to {@link mastering} / {@link masterAudio}.
677
+ */
678
+ export function masteringAssistantSuggestChain(
679
+ request: MasteringAssistantParamsRequest,
680
+ ): Record<string, number | boolean> {
681
+ return requireModule().masteringAssistantSuggestChain(
682
+ request.samples,
683
+ request.sampleRate ?? 22050,
684
+ request.params ?? {},
685
+ );
686
+ }
687
+
688
+ /**
689
+ * The shape {@link masteringAudioProfile}'s JSON parses to.
690
+ *
691
+ * The profile crosses as a string, so nothing type-checks it on arrival; this
692
+ * declaration is what a conformance check compares against the paths the C++
693
+ * writer publishes, so a field added on one side and not the other fails there
694
+ * rather than reaching a caller as `undefined`.
695
+ */
696
+ export interface MasteringAudioProfile {
697
+ durationSec: number;
698
+ bpm: number;
699
+ bpmConfidence: number;
700
+ loudness: {
701
+ integratedLufs: number;
702
+ lraLu: number;
703
+ truePeakDb: number;
704
+ crestFactorDb: number;
705
+ };
706
+ spectral: {
707
+ subRmsDb: number;
708
+ lowRmsDb: number;
709
+ lowMidRmsDb: number;
710
+ midRmsDb: number;
711
+ highMidRmsDb: number;
712
+ highRmsDb: number;
713
+ airRmsDb: number;
714
+ centroidHz: number;
715
+ flatness: number;
716
+ rolloffHz: number;
717
+ };
718
+ dynamics: {
719
+ shortTermLufsStd: number;
720
+ attackDensity: number;
721
+ sustainRatio: number;
722
+ };
723
+ /**
724
+ * What the repair detectors measured. `measured` is false when nothing ran —
725
+ * either `detectDefects` was not asked for or the input was too short — and
726
+ * every other field is then at its default rather than a reading.
727
+ */
728
+ defects: {
729
+ measured: boolean;
730
+ clickCount: number;
731
+ clickRejected: number;
732
+ clickLongestRunSamples: number;
733
+ clickPerSecond: number;
734
+ crackleSampleCount: number;
735
+ crackleSampleFraction: number;
736
+ cracklePerSecond: number;
737
+ clipSampleCount: number;
738
+ clipRunCount: number;
739
+ clipLongestRunSamples: number;
740
+ clipSampleFraction: number;
741
+ clipFlatRunCount: number;
742
+ clipFlatSampleCount: number;
743
+ clipLongestFlatRunSamples: number;
744
+ clipFlatLevel: number;
745
+ noiseFloorDbfs: number;
746
+ noiseBandPeakDbfs: number;
747
+ noiseBandPeakIndex: number;
748
+ humFundamentalHz: number;
749
+ humFundamentalProminence: number;
750
+ humHarmonics: number;
751
+ humFundamentalDbfs: number;
752
+ humPeakHarmonicDbfs: number;
753
+ lateDecayRatioDb: number;
754
+ };
755
+ }
756
+
461
757
  export function masteringAudioProfile(request: MasteringSamplesParamsRequest): string;
462
758
  export function masteringAudioProfile(
463
759
  samples: Float32Array,
@@ -503,7 +799,9 @@ export function masteringStreamingPreview(
503
799
  * of the suggestion is built on the channel-summed program rather than a
504
800
  * downmix that reads roughly 6 dB low.
505
801
  */
506
- export function masteringAssistantSuggestStereo(request: MasteringStereoParamsRequest): string {
802
+ export function masteringAssistantSuggestStereo(
803
+ request: MasteringAssistantStereoParamsRequest,
804
+ ): string {
507
805
  return requireModule().masteringAssistantSuggestStereo(
508
806
  request.left,
509
807
  request.right,
@@ -512,6 +810,22 @@ export function masteringAssistantSuggestStereo(request: MasteringStereoParamsRe
512
810
  );
513
811
  }
514
812
 
813
+ /**
814
+ * Stereo counterpart of {@link masteringAssistantSuggestChain}: the flat
815
+ * `{key: number|boolean}` params map without the surrounding assistant
816
+ * document, ready to pass through as `overrides` to {@link masterAudioStereo}.
817
+ */
818
+ export function masteringAssistantSuggestChainStereo(
819
+ request: MasteringAssistantStereoParamsRequest,
820
+ ): Record<string, number | boolean> {
821
+ return requireModule().masteringAssistantSuggestChainStereo(
822
+ request.left,
823
+ request.right,
824
+ request.sampleRate ?? 22050,
825
+ request.params ?? {},
826
+ );
827
+ }
828
+
515
829
  /**
516
830
  * Mastering assistant profile of a stereo pair, as shared JSON.
517
831
  *