@libraz/libsonare 1.7.2 → 1.8.1

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 +7 -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 +874 -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 +502 -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 +134 -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 +4173 -1621
  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 +483 -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 +654 -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 +528 -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 +165 -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 +893 -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 +3945 -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 +340 -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 +164 -0
  179. package/dist/worklet/engine-mixer-facade.d.ts.map +1 -0
  180. package/dist/worklet/engine-node.d.ts +83 -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 +72 -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 +75 -0
  193. package/dist/worklet/engine-strips.d.ts.map +1 -0
  194. package/dist/worklet/engine-sync.d.ts +39 -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 +418 -0
  199. package/dist/worklet/engine.d.ts.map +1 -0
  200. package/dist/worklet/guards.d.ts +53 -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 +331 -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 +3200 -541
  215. package/dist/worklet.js.map +1 -1
  216. package/package.json +23 -12
  217. package/src/_effects_common.ts +47 -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 +101 -22
  226. package/src/effects_note_ops.ts +683 -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 +388 -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 +288 -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 +441 -32
  246. package/src/mastering_dynamics.ts +22 -11
  247. package/src/metering.ts +67 -24
  248. package/src/mixer.ts +251 -22
  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 +271 -271
  259. package/src/public_types.ts +122 -3
  260. package/src/public_types_acoustic.ts +112 -3
  261. package/src/public_types_mastering.ts +275 -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 +196 -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 +491 -5
  268. package/src/quick_analysis.ts +203 -26
  269. package/src/realtime_engine.ts +773 -34
  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 +1158 -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 +202 -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 +800 -32
  287. package/src/worklet/engine-node.ts +99 -29
  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 +332 -93
  291. package/src/worklet/engine-register.ts +32 -18
  292. package/src/worklet/engine-strips.ts +275 -9
  293. package/src/worklet/engine-sync.ts +20 -7
  294. package/src/worklet/engine.ts +394 -48
  295. package/src/worklet/guards.ts +195 -44
  296. package/src/worklet/messages.ts +229 -2
  297. package/src/worklet/mixer-processor.ts +117 -48
  298. package/src/worklet/playback-processor.ts +300 -0
  299. package/src/worklet/protocol.ts +82 -11
  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,7 +1,11 @@
1
+ import { validatePositiveIntegers } from './_feature_validation';
2
+ import { resolveFftOptions } from './_fft_options';
1
3
  import { ErrorCode, SonareError } from './errors';
2
4
  import { getSonareModule } from './module_state';
3
5
  import type {
4
6
  AnalyzeSectionsOptions,
7
+ BoundaryOptions,
8
+ BoundaryResult,
5
9
  CqtResult,
6
10
  LufsResult,
7
11
  MelodyResult,
@@ -16,6 +20,7 @@ import {
16
20
  assertPositiveInteger,
17
21
  assertSampleRate,
18
22
  assertSamples,
23
+ assertVqtGamma,
19
24
  } from './validation';
20
25
 
21
26
  function requireModule() {
@@ -24,6 +29,7 @@ function requireModule() {
24
29
 
25
30
  type GuardedOptions = ValidateOptions;
26
31
  type AnalyzeSectionsGuardedOptions = AnalyzeSectionsOptions & ValidateOptions;
32
+ type BoundaryGuardedOptions = BoundaryOptions & ValidateOptions;
27
33
  type MelodyGuardedOptions = MelodyOptions & ValidateOptions;
28
34
 
29
35
  /** Canonical request form shared by the Constant-Q transform variants. */
@@ -89,6 +95,11 @@ export interface AnalyzeSectionsRequest extends AnalyzeSectionsGuardedOptions {
89
95
  samples: Float32Array;
90
96
  sampleRate?: number;
91
97
  }
98
+ /** Canonical (and only) call form for {@link detectBoundaries}. */
99
+ export interface DetectBoundariesRequest extends BoundaryGuardedOptions {
100
+ samples: Float32Array;
101
+ sampleRate?: number;
102
+ }
92
103
  export interface AnalyzeMelodyRequest extends MelodyGuardedOptions {
93
104
  samples: Float32Array;
94
105
  sampleRate?: number;
@@ -116,12 +127,6 @@ function validateMusicSamples(
116
127
  assertSamples(fnName, samples, options.validate !== false);
117
128
  }
118
129
 
119
- function validatePositiveIntegers(fnName: string, values: Record<string, number>): void {
120
- for (const [name, value] of Object.entries(values)) {
121
- assertPositiveInteger(fnName, value, name);
122
- }
123
- }
124
-
125
130
  function validateFrequencyBounds(fnName: string, fmin: number, fmax?: number): void {
126
131
  assertFiniteScalar(fnName, fmin, 'fmin');
127
132
  if (fmin < 0) {
@@ -325,8 +330,8 @@ export function hybridCqt(
325
330
  * @param fmin - Minimum frequency in Hz (default: 32.70319566257483, C1)
326
331
  * @param nBins - Number of frequency bins (default: 84)
327
332
  * @param binsPerOctave - Bins per octave (default: 12)
328
- * @param gamma - Bandwidth offset; negative selects the automatic ERB-derived
329
- * value, while 0 is equivalent to CQT (default: -1)
333
+ * @param gamma - Bandwidth offset; a negative value or NaN selects the automatic
334
+ * ERB-derived value, while 0 is equivalent to CQT (default: -1)
330
335
  * @returns VQT magnitude result (same shape as CQT)
331
336
  */
332
337
  export function vqt(request: VqtRequest): CqtResult;
@@ -366,7 +371,7 @@ export function vqt(
366
371
  validateMusicSamples('vqt', samples, sampleRate, options);
367
372
  validatePositiveIntegers('vqt', { hopLength, nBins, binsPerOctave });
368
373
  validateFrequencyBounds('vqt', fmin);
369
- assertFiniteScalar('vqt', gamma, 'gamma');
374
+ assertVqtGamma('vqt', gamma);
370
375
  return requireModule().vqt(samples, sampleRate, hopLength, fmin, nBins, binsPerOctave, gamma);
371
376
  }
372
377
 
@@ -512,7 +517,7 @@ export function vqtToAudio(
512
517
  nIter,
513
518
  options,
514
519
  );
515
- assertFiniteScalar('vqtToAudio', gamma, 'gamma');
520
+ assertVqtGamma('vqtToAudio', gamma);
516
521
  return requireModule().vqtToAudio(
517
522
  magnitude,
518
523
  nBins,
@@ -575,6 +580,85 @@ export function analyzeSections(
575
580
  return Array.from(sections, (s) => ({ ...s, type: s.type as SectionType }));
576
581
  }
577
582
 
583
+ /**
584
+ * Detect structural boundaries and return the novelty curve behind them.
585
+ *
586
+ * This is the unlabelled layer {@link analyzeSections} is built on, not a
587
+ * coarser view of its output: sections are labelled spans, these are the
588
+ * transitions plus the continuous curve they were picked from, so a caller
589
+ * applying its own threshold needs this and cannot derive it from a section
590
+ * list.
591
+ *
592
+ * @param request - Samples, sample rate and {@link BoundaryOptions}
593
+ * @returns The boundaries, the novelty curve, and the analysis grid they live on
594
+ * @throws {@link SonareError} with `InvalidParameter` when both `useMfcc` and
595
+ * `useChroma` are `false`: the two feature streams are combined frame-for-frame,
596
+ * so with neither enabled there is nothing to combine.
597
+ *
598
+ * @example
599
+ * ```ts
600
+ * const { boundaries, noveltyCurve, noveltyPeak } = detectBoundaries({
601
+ * samples,
602
+ * sampleRate: 44100,
603
+ * absoluteThreshold: 0.01,
604
+ * });
605
+ * // `noveltyCurve` is scaled by its own maximum; recover the raw response with
606
+ * // `noveltyCurve[i] * noveltyPeak`.
607
+ * ```
608
+ */
609
+ export function detectBoundaries(request: DetectBoundariesRequest): BoundaryResult {
610
+ const { samples, sampleRate = 22050 } = request;
611
+ validateMusicSamples('detectBoundaries', samples, sampleRate, request);
612
+ // The request IS the options bag: every field is read straight off it, so an
613
+ // omitted one reaches the embind wrapper absent and takes BoundaryConfig's own
614
+ // default. Restating the ten defaults here would make them the effective ones
615
+ // on npm and leave the core's unreachable, and nothing compares the two.
616
+ const sizes: Record<string, number> = {};
617
+ for (const name of ['nFft', 'hopLength', 'kernelSize', 'nMfcc', 'nChroma'] as const) {
618
+ const value = request[name];
619
+ if (value != null) {
620
+ sizes[name] = value;
621
+ }
622
+ }
623
+ validatePositiveIntegers('detectBoundaries', sizes);
624
+ for (const name of ['threshold', 'absoluteThreshold', 'peakDistance'] as const) {
625
+ const value = request[name];
626
+ if (value == null) {
627
+ continue;
628
+ }
629
+ assertFiniteScalar('detectBoundaries', value, name);
630
+ if (value < 0) {
631
+ throw new RangeError(`detectBoundaries: ${name} must be non-negative`);
632
+ }
633
+ }
634
+ // Only an explicit pair of falses; either one omitted leaves the other stream
635
+ // on, so there is still something to combine.
636
+ if (request.useMfcc === false && request.useChroma === false) {
637
+ throw new SonareError(
638
+ ErrorCode.InvalidParameter,
639
+ 'InvalidParameter',
640
+ 'detectBoundaries: require useMfcc or useChroma',
641
+ );
642
+ }
643
+ const result = requireModule().detectBoundaries(samples, sampleRate, request);
644
+ // Re-root the embind array as a plain Array, for the reason analyzeSections
645
+ // spells out: chaining onto it propagates a constructor structuredClone
646
+ // rejects, so a result posted from a Worker would fail to clone.
647
+ return {
648
+ boundaries: Array.from(result.boundaries, (b) => ({
649
+ time: b.time,
650
+ frame: b.frame,
651
+ strength: b.strength,
652
+ })),
653
+ noveltyCurve: result.noveltyCurve,
654
+ noveltyPeak: result.noveltyPeak,
655
+ sampleRate: result.sampleRate,
656
+ hopLength: result.hopLength,
657
+ nFrames: result.nFrames,
658
+ frameStride: result.frameStride,
659
+ };
660
+ }
661
+
578
662
  /** Options for {@link analyzeMelody}. All fields are optional. */
579
663
  export interface MelodyOptions {
580
664
  /** Lowest f0 (Hz) the tracker will consider. Default 65 (≈ C2). */
@@ -703,8 +787,9 @@ export function onsetEnvelope(
703
787
  );
704
788
  }
705
789
  validateMusicSamples('onsetEnvelope', samples, sampleRate, options);
706
- validatePositiveIntegers('onsetEnvelope', { nFft, hopLength, nMels });
707
- return requireModule().onsetEnvelope(samples, sampleRate, nFft, hopLength, nMels);
790
+ const fft = resolveFftOptions('onsetEnvelope', nFft, hopLength);
791
+ validatePositiveIntegers('onsetEnvelope', { nMels });
792
+ return requireModule().onsetEnvelope(samples, sampleRate, fft.nFft, fft.hopLength, nMels);
708
793
  }
709
794
 
710
795
  /**
@@ -750,8 +835,16 @@ export function onsetStrengthMulti(
750
835
  );
751
836
  }
752
837
  validateMusicSamples('onsetStrengthMulti', samples, sampleRate, options);
753
- validatePositiveIntegers('onsetStrengthMulti', { nFft, hopLength, nMels, nBands });
754
- return requireModule().onsetStrengthMulti(samples, sampleRate, nFft, hopLength, nMels, nBands);
838
+ const fft = resolveFftOptions('onsetStrengthMulti', nFft, hopLength);
839
+ validatePositiveIntegers('onsetStrengthMulti', { nMels, nBands });
840
+ return requireModule().onsetStrengthMulti(
841
+ samples,
842
+ sampleRate,
843
+ fft.nFft,
844
+ fft.hopLength,
845
+ nMels,
846
+ nBands,
847
+ );
755
848
  }
756
849
 
757
850
  /**
@@ -1,3 +1,4 @@
1
+ import type { SpectralFrameRequest } from './feature_spectral';
1
2
  import { getSonareModule } from './module_state';
2
3
  import type { NoteSegment, PiptrackResult, PitchResult } from './public_types';
3
4
 
@@ -188,23 +189,117 @@ export function pitchPyin(
188
189
  /** Parameters for segmenting an F0 track into stable monophonic notes. */
189
190
  export interface NoteSegmentsRequest {
190
191
  f0Hz: Float32Array;
192
+ /**
193
+ * Per-frame voicing values in `[0, 1]`; anything below `voicedThreshold` is
194
+ * unvoiced.
195
+ *
196
+ * Pass {@link PitchResult.voicedFlag} converted to `0`/`1`. Do **not** pass
197
+ * {@link pitchPyin}'s `voicedProb`: that value is the frame's voiced
198
+ * observation mass and rises with F0 for a fixed `frameLength`, so a fixed
199
+ * threshold silently returns no segments at all for low-register material
200
+ * (a steady tone below roughly C5 never reaches 0.5).
201
+ */
191
202
  voicedProb: Float32Array;
192
203
  frameRate: number;
204
+ /** Cents of pitch change that start a new segment. Default 50. */
193
205
  segmentationThresholdCents?: number;
206
+ /** Shortest segment kept, in ms. Default 30. */
194
207
  minNoteMs?: number;
208
+ /** Reference pitch each segment's cents are measured against. Default 440. */
195
209
  referenceHz?: number;
210
+ /** Voicing threshold applied to `voicedProb`; defaults to `0.5`. */
211
+ voicedThreshold?: number;
196
212
  }
197
213
 
198
214
  /**
199
215
  * Segment a caller-supplied monophonic F0 track into stable note regions.
200
216
  *
201
217
  * `f0Hz` and `voicedProb` must have the same non-zero length. Zero-Hz frames
202
- * and probabilities below 0.5 are treated as unvoiced.
218
+ * and values below `voicedThreshold` (default `0.5`) are treated as unvoiced.
203
219
  */
204
220
  export function noteSegments(request: NoteSegmentsRequest): NoteSegment[] {
205
221
  return requireModule().noteSegments(request.f0Hz, request.voicedProb, request.frameRate, {
206
222
  segmentationThresholdCents: request.segmentationThresholdCents,
207
223
  minNoteMs: request.minNoteMs,
208
224
  referenceHz: request.referenceHz,
225
+ voicedThreshold: request.voicedThreshold,
209
226
  });
210
227
  }
228
+
229
+ // ============================================================================
230
+ // Features - Tuning
231
+ // ============================================================================
232
+
233
+ export interface PitchTuningRequest {
234
+ frequencies: Float32Array;
235
+ resolution?: number;
236
+ binsPerOctave?: number;
237
+ }
238
+
239
+ export interface EstimateTuningRequest extends SpectralFrameRequest {
240
+ resolution?: number;
241
+ binsPerOctave?: number;
242
+ }
243
+
244
+ /**
245
+ * Estimate the global tuning offset from a set of frequencies
246
+ * (librosa.pitch_tuning). Returns a deviation in fractions of a bin.
247
+ */
248
+ export function pitchTuning(request: PitchTuningRequest): number;
249
+ export function pitchTuning(
250
+ frequencies: Float32Array,
251
+ resolution?: number,
252
+ binsPerOctave?: number,
253
+ ): number;
254
+ export function pitchTuning(
255
+ frequencies: Float32Array | PitchTuningRequest,
256
+ resolution = 0.01,
257
+ binsPerOctave = 12,
258
+ ): number {
259
+ if (!(frequencies instanceof Float32Array)) {
260
+ const r = frequencies;
261
+ return pitchTuning(r.frequencies, r.resolution, r.binsPerOctave);
262
+ }
263
+ return requireModule().pitchTuning(frequencies, resolution, binsPerOctave);
264
+ }
265
+
266
+ /**
267
+ * Estimate the tuning offset of an audio signal (librosa.estimate_tuning).
268
+ */
269
+ export function estimateTuning(request: EstimateTuningRequest): number;
270
+ export function estimateTuning(
271
+ samples: Float32Array,
272
+ sampleRate?: number,
273
+ nFft?: number,
274
+ hopLength?: number,
275
+ resolution?: number,
276
+ binsPerOctave?: number,
277
+ ): number;
278
+ export function estimateTuning(
279
+ samples: Float32Array | EstimateTuningRequest,
280
+ sampleRate = 22050,
281
+ nFft = 2048,
282
+ hopLength = 512,
283
+ resolution = 0.01,
284
+ binsPerOctave = 12,
285
+ ): number {
286
+ if (!(samples instanceof Float32Array)) {
287
+ const r = samples;
288
+ return estimateTuning(
289
+ r.samples,
290
+ r.sampleRate,
291
+ r.nFft,
292
+ r.hopLength,
293
+ r.resolution,
294
+ r.binsPerOctave,
295
+ );
296
+ }
297
+ return requireModule().estimateTuning(
298
+ samples,
299
+ sampleRate,
300
+ nFft,
301
+ hopLength,
302
+ resolution,
303
+ binsPerOctave,
304
+ );
305
+ }