@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
@@ -0,0 +1,425 @@
1
+ /**
2
+ * Broadband and tonal noise repair: denoise and dehum, with their detectors.
3
+ */
4
+
5
+ import { getSonareModule } from './module_state';
6
+ import type {
7
+ HumDetection,
8
+ MasteringRepairDehumStereoResult,
9
+ MasteringRepairDenoiseClassicalLinkedResult,
10
+ MasteringRepairDenoiseClassicalStereoResult,
11
+ NoiseDetection,
12
+ } from './public_types_repair';
13
+
14
+ function requireModule() {
15
+ return getSonareModule();
16
+ }
17
+
18
+ /** Algorithms accepted by `masteringRepairDenoiseClassical`. */
19
+ export type DenoiseClassicalMode = 'logMmse' | 'mmseStsa' | 'spectralSubtraction';
20
+
21
+ /** Noise PSD estimators accepted by `masteringRepairDenoiseClassical`. */
22
+ export type DenoiseClassicalNoiseEstimator = 'quantile' | 'mcra' | 'imcra' | 'spp';
23
+
24
+ /** Options for `masteringRepairDenoiseClassical`. */
25
+ export interface DenoiseClassicalOptions {
26
+ mode?: DenoiseClassicalMode;
27
+ noiseEstimator?: DenoiseClassicalNoiseEstimator;
28
+ nFft?: number;
29
+ hopLength?: number;
30
+ ddAlpha?: number;
31
+ reductionDb?: number;
32
+ overSubtraction?: number;
33
+ spectralFloor?: number;
34
+ noiseEstimationQuantile?: number;
35
+ speechPresenceGain?: boolean;
36
+ gainSmoothing?: boolean;
37
+ }
38
+
39
+ export interface MasteringRepairDenoiseClassicalRequest extends DenoiseClassicalOptions {
40
+ samples: Float32Array;
41
+ sampleRate: number;
42
+ }
43
+
44
+ /** Request form of `masteringRepairDenoiseClassicalStereo`. */
45
+ export interface MasteringRepairDenoiseClassicalStereoRequest extends DenoiseClassicalOptions {
46
+ left: Float32Array;
47
+ right: Float32Array;
48
+ sampleRate?: number;
49
+ }
50
+
51
+ /** Request form of `masteringRepairDenoiseClassicalLinked`. */
52
+ export interface MasteringRepairDenoiseClassicalLinkedRequest extends DenoiseClassicalOptions {
53
+ /** At least one channel; all the same length. */
54
+ channels: Float32Array[];
55
+ sampleRate?: number;
56
+ }
57
+
58
+ /** Offline STFT-domain classical denoiser (LogMMSE / MMSE-STSA / SpectralSubtraction). */
59
+ export function masteringRepairDenoiseClassical(
60
+ request: MasteringRepairDenoiseClassicalRequest,
61
+ ): Float32Array;
62
+ export function masteringRepairDenoiseClassical(
63
+ samples: Float32Array,
64
+ sampleRate: number,
65
+ options?: DenoiseClassicalOptions,
66
+ ): Float32Array;
67
+ export function masteringRepairDenoiseClassical(
68
+ samples: Float32Array | MasteringRepairDenoiseClassicalRequest,
69
+ sampleRate?: number,
70
+ options: DenoiseClassicalOptions = {},
71
+ ): Float32Array {
72
+ const request =
73
+ samples instanceof Float32Array
74
+ ? { samples, sampleRate: sampleRate as number, ...options }
75
+ : samples;
76
+ return requireModule().masteringRepairDenoiseClassical(
77
+ request.samples,
78
+ request.sampleRate,
79
+ request,
80
+ );
81
+ }
82
+
83
+ /**
84
+ * Offline STFT-domain classical denoiser for a stereo pair, driven by one channel-linked
85
+ * gain mask.
86
+ *
87
+ * The mask is built from the channel-summed power and applied unchanged to both channels, so
88
+ * the pass cannot move an interchannel level or phase difference. That is also why the result
89
+ * carries a single `report` rather than one per channel: a pair would be two copies of one
90
+ * measurement and would read as though the two could differ.
91
+ *
92
+ * `report.detected` is therefore a *pair-level* measurement, and the only absolute one in the
93
+ * result. Its levels are dBFS on the channel-summed power, so two identical channels read
94
+ * `10*log10(2)` — about 3.01 dB — above the same material through
95
+ * {@link masteringRepairDenoiseClassical}. A stereo floor is comparable only against another
96
+ * stereo floor, never against a mono one.
97
+ *
98
+ * Needs at least `nFft` samples and REJECTS a shorter input, which is the opposite of
99
+ * {@link masteringRepairDereverbClassicalStereo} — that one pads.
100
+ *
101
+ * Which options are live depends on `mode`: `overSubtraction` and `spectralFloor` are read
102
+ * only by `spectralSubtraction`, and `speechPresenceGain` and `gainSmoothing` only by the
103
+ * other two, so at the default `logMmse` the first pair does nothing.
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * const { left, right, report } = masteringRepairDenoiseClassicalStereo({
108
+ * left: leftSamples,
109
+ * right: rightSamples,
110
+ * sampleRate: 48000,
111
+ * reductionDb: 18,
112
+ * });
113
+ * console.log(report.detected.floorDbfs, report.meanReductionDb);
114
+ * ```
115
+ */
116
+ export function masteringRepairDenoiseClassicalStereo(
117
+ request: MasteringRepairDenoiseClassicalStereoRequest,
118
+ ): MasteringRepairDenoiseClassicalStereoResult;
119
+ export function masteringRepairDenoiseClassicalStereo(
120
+ left: Float32Array,
121
+ right: Float32Array,
122
+ sampleRate: number,
123
+ config?: DenoiseClassicalOptions,
124
+ ): MasteringRepairDenoiseClassicalStereoResult;
125
+ export function masteringRepairDenoiseClassicalStereo(
126
+ left: Float32Array | MasteringRepairDenoiseClassicalStereoRequest,
127
+ right?: Float32Array,
128
+ sampleRate?: number,
129
+ config: DenoiseClassicalOptions = {},
130
+ ): MasteringRepairDenoiseClassicalStereoResult {
131
+ const request: MasteringRepairDenoiseClassicalStereoRequest =
132
+ left instanceof Float32Array
133
+ ? { left, right: right as Float32Array, sampleRate, ...config }
134
+ : left;
135
+ const { left: leftSamples, right: rightSamples, sampleRate: rate, ...options } = request;
136
+ return requireModule().masteringRepairDenoiseClassicalStereo(
137
+ leftSamples,
138
+ rightSamples,
139
+ rate ?? 22050,
140
+ options,
141
+ );
142
+ }
143
+
144
+ /**
145
+ * Offline STFT-domain classical denoiser for any number of channels, driven by one
146
+ * channel-linked gain mask.
147
+ *
148
+ * The N-channel form of {@link masteringRepairDenoiseClassicalStereo}, carrying the same
149
+ * guarantee over the whole set: the mask is built from the channel-summed power and applied
150
+ * unchanged to every channel, so no interchannel level or phase difference moves however many
151
+ * channels there are. One `report` for the set, and one output per input channel in input order.
152
+ *
153
+ * A single channel reproduces {@link masteringRepairDenoiseClassical} bit for bit, and two
154
+ * reproduce {@link masteringRepairDenoiseClassicalStereo} plane for plane — `channels[0]` is the
155
+ * left plane and `channels[1]` the right.
156
+ *
157
+ * `report.detected` carries absolute levels and they are the SET's: the floor is referred to the
158
+ * summed mean square of every channel, so N identical channels read `10*log10(N)` above
159
+ * one of them — about 3.01 dB at two channels and 4.77 dB at three. Compare a floor only against
160
+ * one measured over the same number of channels. Every other field of the report is a fraction
161
+ * and does not move with the channel count.
162
+ *
163
+ * Needs at least `nFft` samples and REJECTS a shorter input, which is the opposite of
164
+ * {@link masteringRepairDereverbClassicalLinked} — that one pads.
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * const { channels, report } = masteringRepairDenoiseClassicalLinked({
169
+ * channels: [frontLeft, frontRight, centre],
170
+ * sampleRate: 48000,
171
+ * reductionDb: 18,
172
+ * });
173
+ * console.log(channels.length, report.detected.floorDbfs);
174
+ * ```
175
+ */
176
+ export function masteringRepairDenoiseClassicalLinked(
177
+ request: MasteringRepairDenoiseClassicalLinkedRequest,
178
+ ): MasteringRepairDenoiseClassicalLinkedResult;
179
+ export function masteringRepairDenoiseClassicalLinked(
180
+ channels: Float32Array[],
181
+ sampleRate: number,
182
+ config?: DenoiseClassicalOptions,
183
+ ): MasteringRepairDenoiseClassicalLinkedResult;
184
+ export function masteringRepairDenoiseClassicalLinked(
185
+ channels: Float32Array[] | MasteringRepairDenoiseClassicalLinkedRequest,
186
+ sampleRate?: number,
187
+ config: DenoiseClassicalOptions = {},
188
+ ): MasteringRepairDenoiseClassicalLinkedResult {
189
+ const request: MasteringRepairDenoiseClassicalLinkedRequest = Array.isArray(channels)
190
+ ? { channels, sampleRate, ...config }
191
+ : channels;
192
+ const { channels: input, sampleRate: rate, ...options } = request;
193
+ return requireModule().masteringRepairDenoiseClassicalLinked(input, rate ?? 22050, options);
194
+ }
195
+
196
+ /**
197
+ * How `masteringRepairDehum` removes the harmonic series.
198
+ *
199
+ * `subtract` tracks each harmonic's amplitude and phase and subtracts the tone they describe,
200
+ * so material sitting at the same frequency but uncorrelated with the tracked series survives.
201
+ * `notch` cascades one RBJ notch per harmonic and removes everything inside each notch's
202
+ * bandwidth, hum or programme alike.
203
+ */
204
+ export type DehumMode = 'subtract' | 'notch';
205
+
206
+ /** Options for `masteringRepairDehum`. */
207
+ export interface DehumOptions {
208
+ fundamentalHz?: number;
209
+ harmonics?: number;
210
+ q?: number;
211
+ adaptive?: boolean;
212
+ searchRangeHz?: number;
213
+ adaptation?: number;
214
+ frameSize?: number;
215
+ pllBandwidth?: number;
216
+ /** Defaults to `'subtract'`. */
217
+ mode?: DehumMode;
218
+ }
219
+
220
+ export interface MasteringRepairDehumRequest extends DehumOptions {
221
+ samples: Float32Array;
222
+ sampleRate: number;
223
+ }
224
+
225
+ /** Request form of `masteringRepairDehumStereo`. */
226
+ export interface MasteringRepairDehumStereoRequest extends DehumOptions {
227
+ left: Float32Array;
228
+ right: Float32Array;
229
+ sampleRate?: number;
230
+ }
231
+
232
+ /** Offline mains-hum remover. */
233
+ export function masteringRepairDehum(request: MasteringRepairDehumRequest): Float32Array;
234
+ export function masteringRepairDehum(
235
+ samples: Float32Array,
236
+ sampleRate: number,
237
+ options?: DehumOptions,
238
+ ): Float32Array;
239
+ export function masteringRepairDehum(
240
+ samples: Float32Array | MasteringRepairDehumRequest,
241
+ sampleRate?: number,
242
+ options: DehumOptions = {},
243
+ ): Float32Array {
244
+ const request =
245
+ samples instanceof Float32Array
246
+ ? { samples, sampleRate: sampleRate as number, ...options }
247
+ : samples;
248
+ return requireModule().masteringRepairDehum(request.samples, request.sampleRate, request);
249
+ }
250
+
251
+ /**
252
+ * Offline mains-hum remover for a stereo pair.
253
+ *
254
+ * With `adaptive` set, mains hum is one physical source, so the tracker reads the channel
255
+ * mean and both cascades follow the one frequency it finds: `appliedFundamentalHz` and
256
+ * `fundamentalDriftHz` come back identical in both reports by construction, while each
257
+ * report's `detected` still measures that channel's own input and each channel keeps its
258
+ * own filter state, so neither channel's transient rings through the other. With `adaptive`
259
+ * clear, which is the default, nothing is shared and the two channels are filtered
260
+ * independently at the configured frequency.
261
+ */
262
+ export function masteringRepairDehumStereo(
263
+ request: MasteringRepairDehumStereoRequest,
264
+ ): MasteringRepairDehumStereoResult;
265
+ export function masteringRepairDehumStereo(
266
+ left: Float32Array,
267
+ right: Float32Array,
268
+ sampleRate: number,
269
+ config?: DehumOptions,
270
+ ): MasteringRepairDehumStereoResult;
271
+ export function masteringRepairDehumStereo(
272
+ left: Float32Array | MasteringRepairDehumStereoRequest,
273
+ right?: Float32Array,
274
+ sampleRate?: number,
275
+ config: DehumOptions = {},
276
+ ): MasteringRepairDehumStereoResult {
277
+ const request: MasteringRepairDehumStereoRequest =
278
+ left instanceof Float32Array
279
+ ? { left, right: right as Float32Array, sampleRate, ...config }
280
+ : left;
281
+ const { left: leftSamples, right: rightSamples, sampleRate: rate, ...options } = request;
282
+ return requireModule().masteringRepairDehumStereo(
283
+ leftSamples,
284
+ rightSamples,
285
+ rate ?? 22050,
286
+ options,
287
+ );
288
+ }
289
+
290
+ /** Request form of `masteringRepairDetectNoiseFloor`. */
291
+ export interface MasteringRepairDetectNoiseFloorRequest extends DenoiseClassicalOptions {
292
+ samples: Float32Array;
293
+ sampleRate: number;
294
+ }
295
+
296
+ /** Request form of `masteringRepairNoiseBandBins`. */
297
+ export interface MasteringRepairNoiseBandBinsRequest {
298
+ nFft?: number;
299
+ sampleRate?: number;
300
+ }
301
+
302
+ /** Request form of `masteringRepairDetectHum`. */
303
+ export interface MasteringRepairDetectHumRequest extends DehumOptions {
304
+ samples: Float32Array;
305
+ sampleRate: number;
306
+ }
307
+
308
+ /**
309
+ * Measures the noise floor without denoising.
310
+ *
311
+ * Runs the STFT and the configured noise estimator — the two stages
312
+ * {@link masteringRepairDenoiseClassical} runs — and stops before the gain mask, which is why
313
+ * no attenuation figure appears here.
314
+ *
315
+ * Needs at least `nFft` samples and THROWS for a shorter buffer, the opposite of
316
+ * {@link masteringRepairDetectReverb}, which pads one.
317
+ *
318
+ * `floorDbfs` is an absolute level, so it is comparable only against another figure measured
319
+ * over the same channel count.
320
+ */
321
+ export function masteringRepairDetectNoiseFloor(
322
+ request: MasteringRepairDetectNoiseFloorRequest,
323
+ ): NoiseDetection;
324
+ export function masteringRepairDetectNoiseFloor(
325
+ samples: Float32Array,
326
+ sampleRate: number,
327
+ options?: DenoiseClassicalOptions,
328
+ ): NoiseDetection;
329
+ export function masteringRepairDetectNoiseFloor(
330
+ samples: Float32Array | MasteringRepairDetectNoiseFloorRequest,
331
+ sampleRate?: number,
332
+ options: DenoiseClassicalOptions = {},
333
+ ): NoiseDetection {
334
+ const request =
335
+ samples instanceof Float32Array
336
+ ? { samples, sampleRate: sampleRate as number, ...options }
337
+ : samples;
338
+ return requireModule().masteringRepairDetectNoiseFloor(
339
+ request.samples,
340
+ request.sampleRate,
341
+ request,
342
+ );
343
+ }
344
+
345
+ /**
346
+ * Bin boundaries of the grid {@link masteringRepairDetectNoiseFloor} reports `bandFloorDbfs` on.
347
+ *
348
+ * Band `k` covers the one-sided STFT bins `[bins[k], bins[k + 1])`, and bin `b` sits at
349
+ * `b * sampleRate / nFft` Hz.
350
+ *
351
+ * The geometric band edges are rounded to bins, so a band narrower than the bin spacing comes
352
+ * out EMPTY — `bins[k] === bins[k + 1]` — and its `bandFloorDbfs[k]` reads as the floor
353
+ * sentinel because no bin landed in it, NOT because that region was quiet. Telling those two
354
+ * apart is what this grid is for, and the rounding cannot be recovered from the band count
355
+ * alone.
356
+ *
357
+ * Nothing but the analysis geometry decides the grid, so no denoise config is taken: one call
358
+ * describes every floor measured at that `nFft` and `sampleRate`, whatever mode or estimator
359
+ * produced it.
360
+ *
361
+ * @param nFft - STFT size the bins belong to; a positive power of two, the same rule
362
+ * {@link masteringRepairDetectNoiseFloor} applies to its config, so every grid returned here
363
+ * is one that entry can report on. Defaults to 1024.
364
+ * @param sampleRate - Sample rate the bins belong to, in Hz; positive. Defaults to 22050.
365
+ * @returns 33 bin indices, low to high — one more than the 32 bands: the first bin of every
366
+ * band plus the one-past-the-end bin of the last, which is `nFft / 2 + 1`. Non-decreasing.
367
+ * @throws If `nFft` is not a positive power of two, or `sampleRate` is not positive.
368
+ *
369
+ * @example
370
+ * ```ts
371
+ * const bins = masteringRepairNoiseBandBins({ nFft: 1024, sampleRate: 48000 });
372
+ * const floor = masteringRepairDetectNoiseFloor({ samples, sampleRate: 48000, nFft: 1024 });
373
+ * floor.bandFloorDbfs.forEach((level, k) => {
374
+ * if (bins[k] === bins[k + 1]) return; // empty band: `level` is the sentinel, not a measurement
375
+ * console.log((bins[k] * 48000) / 1024, level);
376
+ * });
377
+ * ```
378
+ */
379
+ export function masteringRepairNoiseBandBins(
380
+ request?: MasteringRepairNoiseBandBinsRequest,
381
+ ): Int32Array;
382
+ export function masteringRepairNoiseBandBins(nFft?: number, sampleRate?: number): Int32Array;
383
+ export function masteringRepairNoiseBandBins(
384
+ nFft?: number | MasteringRepairNoiseBandBinsRequest,
385
+ sampleRate?: number,
386
+ ): Int32Array {
387
+ // Both positional arguments are optional, so a default on `nFft` would swallow
388
+ // an explicit `undefined` and take the request branch with `sampleRate` in hand
389
+ // but unreachable. Discriminate on the value instead.
390
+ const request = typeof nFft === 'object' && nFft !== null ? nFft : { nFft, sampleRate };
391
+ return requireModule().masteringRepairNoiseBandBins(
392
+ request.nFft ?? 1024,
393
+ request.sampleRate ?? 22050,
394
+ );
395
+ }
396
+
397
+ /**
398
+ * Measures hum without filtering.
399
+ *
400
+ * Always runs the estimation path, whatever `adaptive` says: the fixed path notches the
401
+ * configured frequency without ever looking for hum, so a detector following the flag would
402
+ * hand back its own input. `fundamentalProminence` is the winning candidate's projected energy
403
+ * over the median candidate, so `1.0` means no peak was found at all — it is not a lock flag.
404
+ *
405
+ * `harmonicDbfs` is measured at every `k*f0` the sample rate carries, not only the ones a
406
+ * cascade would notch; a `k*f0` at or past Nyquist reads the dB floor because nothing is there
407
+ * to measure.
408
+ */
409
+ export function masteringRepairDetectHum(request: MasteringRepairDetectHumRequest): HumDetection;
410
+ export function masteringRepairDetectHum(
411
+ samples: Float32Array,
412
+ sampleRate: number,
413
+ options?: DehumOptions,
414
+ ): HumDetection;
415
+ export function masteringRepairDetectHum(
416
+ samples: Float32Array | MasteringRepairDetectHumRequest,
417
+ sampleRate?: number,
418
+ options: DehumOptions = {},
419
+ ): HumDetection {
420
+ const request =
421
+ samples instanceof Float32Array
422
+ ? { samples, sampleRate: sampleRate as number, ...options }
423
+ : samples;
424
+ return requireModule().masteringRepairDetectHum(request.samples, request.sampleRate, request);
425
+ }
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Silence trimming, with the range detector it shares its geometry with.
3
+ */
4
+
5
+ import { getSonareModule } from './module_state';
6
+ import type { MasteringRepairTrimSilenceStereoResult, TrimRange } from './public_types_repair';
7
+
8
+ function requireModule() {
9
+ return getSonareModule();
10
+ }
11
+
12
+ /** Trimming modes accepted by `masteringRepairTrimSilence`. */
13
+ export type TrimSilenceMode = 'peak' | 'lufsGated';
14
+
15
+ /** Options for `masteringRepairTrimSilence`. */
16
+ export interface TrimSilenceOptions {
17
+ threshold?: number;
18
+ paddingSamples?: number;
19
+ mode?: TrimSilenceMode;
20
+ gateLufs?: number;
21
+ windowMs?: number;
22
+ }
23
+
24
+ export interface MasteringRepairTrimSilenceRequest extends TrimSilenceOptions {
25
+ samples: Float32Array;
26
+ sampleRate: number;
27
+ }
28
+
29
+ /** Request form of `masteringRepairTrimSilenceStereo`. */
30
+ export interface MasteringRepairTrimSilenceStereoRequest extends TrimSilenceOptions {
31
+ left: Float32Array;
32
+ right: Float32Array;
33
+ sampleRate?: number;
34
+ }
35
+
36
+ /** Offline silence trimmer (peak threshold or LUFS-gated). */
37
+ export function masteringRepairTrimSilence(
38
+ request: MasteringRepairTrimSilenceRequest,
39
+ ): Float32Array;
40
+ export function masteringRepairTrimSilence(
41
+ samples: Float32Array,
42
+ sampleRate: number,
43
+ options?: TrimSilenceOptions,
44
+ ): Float32Array;
45
+ export function masteringRepairTrimSilence(
46
+ samples: Float32Array | MasteringRepairTrimSilenceRequest,
47
+ sampleRate?: number,
48
+ options: TrimSilenceOptions = {},
49
+ ): Float32Array {
50
+ const request =
51
+ samples instanceof Float32Array
52
+ ? { samples, sampleRate: sampleRate as number, ...options }
53
+ : samples;
54
+ return requireModule().masteringRepairTrimSilence(request.samples, request.sampleRate, request);
55
+ }
56
+
57
+ /**
58
+ * Offline silence trimmer for a stereo pair, cutting both channels to one shared range.
59
+ *
60
+ * Each channel is scanned on its own and the two ranges are UNIONED, so the pair keeps
61
+ * whatever either channel calls signal and both outputs come back the same length. The scan
62
+ * never reads a downmix: `0.5 * (left + right)` halves material carried by one channel alone,
63
+ * which can drop it under the gate, and cancels an antiphase pair to exactly zero, which would
64
+ * read full-level audio in both channels as silence. Trimming is destructive, so the rule errs
65
+ * toward keeping.
66
+ *
67
+ * The only repair stereo entry that SHORTENS its input: `result.left.length` is the output
68
+ * length, and the input's says nothing about it. A pair in which neither channel carries signal
69
+ * returns two EMPTY arrays and succeeds — it does not throw and does not return null.
70
+ *
71
+ * `report.range` is the union that was applied to both channels. `leftRange` and `rightRange`
72
+ * are the per-channel scans it was formed from, so a caller can see which channel decided each
73
+ * edge; a channel carrying nothing reports an empty range and contributes nothing to the union.
74
+ * With nothing kept, `report.range` is `(inputLength, inputLength)`, so `removedHeadSamples` is
75
+ * the whole input and `removedTailSamples` is 0 — the two still sum to the input length and
76
+ * only the split between the ends is arbitrary.
77
+ *
78
+ * Which option is live depends on `mode`: `threshold` is read ONLY by `'peak'`, and `gateLufs`
79
+ * and `windowMs` ONLY by `'lufsGated'`. Changing an option the active mode does not read is
80
+ * silently inert rather than an error.
81
+ *
82
+ * That gated mode compares an UNWEIGHTED RMS over a window centred on each sample against
83
+ * `gateLufs`, so the figure it gates on is dBFS rather than a BS.1770 loudness, and `windowMs`
84
+ * sizes that window and does nothing else. The window is clipped at the buffer ends, so a
85
+ * sample near either edge is judged on a shorter one.
86
+ *
87
+ * `paddingSamples` widens the kept range in both directions and is clamped to the buffer, so it
88
+ * can never reach past either end; a pass that kept nothing is not padded. A NEGATIVE count is
89
+ * refused by name rather than absorbed into 0 or into the default — the underlying field is
90
+ * unsigned, and a negative one would arrive as an enormous count instead.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * const { left, right, report, leftRange, rightRange } = masteringRepairTrimSilenceStereo({
95
+ * left: leftSamples,
96
+ * right: rightSamples,
97
+ * sampleRate: 48000,
98
+ * mode: 'peak',
99
+ * threshold: 0.01,
100
+ * });
101
+ * console.log(left.length, report.removedHeadSamples, leftRange.first, rightRange.first);
102
+ * ```
103
+ */
104
+ export function masteringRepairTrimSilenceStereo(
105
+ request: MasteringRepairTrimSilenceStereoRequest,
106
+ ): MasteringRepairTrimSilenceStereoResult;
107
+ export function masteringRepairTrimSilenceStereo(
108
+ left: Float32Array,
109
+ right: Float32Array,
110
+ sampleRate: number,
111
+ config?: TrimSilenceOptions,
112
+ ): MasteringRepairTrimSilenceStereoResult;
113
+ export function masteringRepairTrimSilenceStereo(
114
+ left: Float32Array | MasteringRepairTrimSilenceStereoRequest,
115
+ right?: Float32Array,
116
+ sampleRate?: number,
117
+ config: TrimSilenceOptions = {},
118
+ ): MasteringRepairTrimSilenceStereoResult {
119
+ const request: MasteringRepairTrimSilenceStereoRequest =
120
+ left instanceof Float32Array
121
+ ? { left, right: right as Float32Array, sampleRate, ...config }
122
+ : left;
123
+ const { left: leftSamples, right: rightSamples, sampleRate: rate, ...options } = request;
124
+ return requireModule().masteringRepairTrimSilenceStereo(
125
+ leftSamples,
126
+ rightSamples,
127
+ rate ?? 22050,
128
+ options,
129
+ );
130
+ }
131
+
132
+ /** Request form of `masteringRepairDetectTrimRange`. */
133
+ export interface MasteringRepairDetectTrimRangeRequest extends TrimSilenceOptions {
134
+ samples: Float32Array;
135
+ sampleRate: number;
136
+ }
137
+
138
+ /** Request form of `masteringRepairDetectTrimRangeStereo`. */
139
+ export interface MasteringRepairDetectTrimRangeStereoRequest extends TrimSilenceOptions {
140
+ left: Float32Array;
141
+ right: Float32Array;
142
+ sampleRate?: number;
143
+ }
144
+
145
+ /**
146
+ * Measures the range {@link masteringRepairTrimSilence} would keep, without trimming.
147
+ *
148
+ * The padding `paddingSamples` asks for is already INSIDE the returned range, so this is the
149
+ * range the repair would cut to rather than the detected extent of the signal. A buffer with
150
+ * nothing above the threshold reports `(length, length)`.
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * const range = masteringRepairDetectTrimRange({ samples, sampleRate: 48000, threshold: 0.01 });
155
+ * const keptSeconds = (range.lastExclusive - range.first) / 48000;
156
+ * ```
157
+ */
158
+ export function masteringRepairDetectTrimRange(
159
+ request: MasteringRepairDetectTrimRangeRequest,
160
+ ): TrimRange;
161
+ export function masteringRepairDetectTrimRange(
162
+ samples: Float32Array,
163
+ sampleRate: number,
164
+ options?: TrimSilenceOptions,
165
+ ): TrimRange;
166
+ export function masteringRepairDetectTrimRange(
167
+ samples: Float32Array | MasteringRepairDetectTrimRangeRequest,
168
+ sampleRate?: number,
169
+ options: TrimSilenceOptions = {},
170
+ ): TrimRange {
171
+ const request =
172
+ samples instanceof Float32Array
173
+ ? { samples, sampleRate: sampleRate as number, ...options }
174
+ : samples;
175
+ return requireModule().masteringRepairDetectTrimRange(
176
+ request.samples,
177
+ request.sampleRate,
178
+ request,
179
+ );
180
+ }
181
+
182
+ /**
183
+ * Measures the one range a stereo trim pass would cut both channels to.
184
+ *
185
+ * Each channel is scanned on its own and the two ranges are UNIONED, so the pair keeps whatever
186
+ * either channel calls signal. A channel with nothing above the threshold contributes NO EDGE
187
+ * rather than an edge at the buffer's end: the union of a silent channel and an active one is
188
+ * the active channel's range exactly, where a naive `min`/`max` would push `lastExclusive` out
189
+ * to the buffer end and keep the whole tail.
190
+ *
191
+ * A downmix is not read: summing to mono halves material carried by one channel alone and
192
+ * cancels an antiphase pair outright, either of which would read full-level audio as silence.
193
+ *
194
+ * @example
195
+ * ```ts
196
+ * const range = masteringRepairDetectTrimRangeStereo({ left, right, sampleRate: 48000 });
197
+ * console.log(range.first, range.lastExclusive);
198
+ * ```
199
+ */
200
+ export function masteringRepairDetectTrimRangeStereo(
201
+ request: MasteringRepairDetectTrimRangeStereoRequest,
202
+ ): TrimRange;
203
+ export function masteringRepairDetectTrimRangeStereo(
204
+ left: Float32Array,
205
+ right: Float32Array,
206
+ sampleRate: number,
207
+ config?: TrimSilenceOptions,
208
+ ): TrimRange;
209
+ export function masteringRepairDetectTrimRangeStereo(
210
+ left: Float32Array | MasteringRepairDetectTrimRangeStereoRequest,
211
+ right?: Float32Array,
212
+ sampleRate?: number,
213
+ config: TrimSilenceOptions = {},
214
+ ): TrimRange {
215
+ const request: MasteringRepairDetectTrimRangeStereoRequest =
216
+ left instanceof Float32Array
217
+ ? { left, right: right as Float32Array, sampleRate, ...config }
218
+ : left;
219
+ const { left: leftSamples, right: rightSamples, sampleRate: rate, ...options } = request;
220
+ return requireModule().masteringRepairDetectTrimRangeStereo(
221
+ leftSamples,
222
+ rightSamples,
223
+ rate ?? 22050,
224
+ options,
225
+ );
226
+ }