@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
@@ -25,12 +25,26 @@ export interface SonareWorkletMeterSnapshot {
25
25
  rmsDbL: number;
26
26
  rmsDbR: number;
27
27
  correlation: number;
28
+ /**
29
+ * Left-channel inter-sample (true) peak in dB, from the ITU-R BS.1770-4
30
+ * polyphase reconstruction at 4x. A streaming measurement, and the render
31
+ * quantum makes that visible here: the centered reconstruction stencil needs a
32
+ * few future samples a realtime path does not have, so each block's last
33
+ * samples read marginally low (about 0.1 dB across 64..8192-sample blocks on a
34
+ * near-Nyquist tone, always under-reading). Use `meteringTruePeakDb` over the
35
+ * whole signal for an exact dBTP number.
36
+ */
28
37
  truePeakDbL: number;
38
+ /** Right-channel inter-sample (true) peak in dB. See {@link truePeakDbL}. */
29
39
  truePeakDbR: number;
30
40
  momentaryLufs: number;
31
41
  shortTermLufs: number;
32
42
  integratedLufs: number;
33
43
  gainReductionDb: number;
44
+ /** Maximum pre-trim input peak for the left channel, in dBFS. */
45
+ inputPeakDbL: number;
46
+ /** Maximum pre-trim input peak for the right channel, in dBFS. */
47
+ inputPeakDbR: number;
34
48
  }
35
49
 
36
50
  export interface SonareWorkletSpectrumSnapshot {
@@ -42,18 +56,21 @@ export interface SonareWorkletSpectrumSnapshot {
42
56
  export const SONARE_METER_RING_HEADER_INTS = 4;
43
57
  // Record layout: [frameLo, frameHi, targetId, peakDbL, peakDbR, rmsDbL, rmsDbR,
44
58
  // correlation, truePeakDbL, truePeakDbR, momentaryLufs, shortTermLufs,
45
- // integratedLufs, gainReductionDb].
59
+ // integratedLufs, gainReductionDb, inputPeakDbL, inputPeakDbR].
46
60
  // The sample-frame index is monotonically increasing and quickly exceeds the
47
61
  // 2^24 exact-integer range of a single Float32 slot (~349 s at 48 kHz), so it is
48
62
  // stored split across two Float32 lanes (low 24 bits + high bits) for exact
49
63
  // reconstruction. See encodeFrameLo/encodeFrameHi/decodeFrame.
50
- export const SONARE_METER_RING_RECORD_FLOATS = 14;
64
+ export const SONARE_METER_RING_RECORD_FLOATS = 16;
51
65
  export const SONARE_SPECTRUM_RING_HEADER_INTS = 5;
52
66
  // Scope ring header: [writeIndex, capacity, recordFloats, bands, maxPoints,
53
67
  // reserved]. Record layout: [frameLo, frameHi, targetId, bandCount, pointCount,
54
68
  // band0..band(bands-1), l0, r0, l1, r1, ... (maxPoints stereo pairs)].
55
69
  export const SONARE_SCOPE_RING_HEADER_INTS = 6;
56
70
  export const SONARE_SCOPE_RING_RECORD_PREFIX_FLOATS = 5;
71
+ /** Scope band and goniometer point counts used when a caller supplies none. */
72
+ export const SONARE_SCOPE_DEFAULT_BANDS = 48;
73
+ export const SONARE_SCOPE_DEFAULT_MAX_POINTS = 32;
57
74
 
58
75
  /** Base for splitting a frame index into two exactly-representable Float32 lanes. */
59
76
  const SONARE_FRAME_LANE_BASE = 0x1000000; // 2^24
@@ -111,6 +128,10 @@ export enum SonareEngineCommandType {
111
128
  // Queueable lane monitor-mode transition. The worklet command vocabulary
112
129
  // deliberately leaves 18..25 unused; this is the stable ABI-v3 ordinal.
113
130
  SetTrackMonitorMode = 26,
131
+ // Queueable master-meter integrated-loudness reset. Numeric value 28 mirrors
132
+ // rt::CommandType::kResetMasterMeterIntegrated; 27 remains the appended MIDI
133
+ // UMP slot command in the native vocabulary.
134
+ ResetMasterLoudnessMeter = 28,
114
135
  }
115
136
 
116
137
  export enum SonareEngineTelemetryType {
@@ -140,6 +161,7 @@ export enum SonareEngineTelemetryError {
140
161
  MetronomeOverflow = 18,
141
162
  InvalidCommand = 19,
142
163
  MaxChannelsExceeded = 20,
164
+ ParameterBaseOverflow = 21,
143
165
  }
144
166
 
145
167
  export interface SonareMeterRingBuffer {
@@ -307,8 +329,24 @@ export interface SharedSpectrumRingWriter {
307
329
  recordFloats: number;
308
330
  }
309
331
 
332
+ /**
333
+ * Shared finite dB floor (`sonare::constants::kFloorDb`). Every level-in-dB
334
+ * field on a worklet snapshot bottoms out here rather than at `-Infinity`.
335
+ */
336
+ export const SONARE_FLOOR_DB = -120;
337
+
338
+ /**
339
+ * Magnitude to dB with the shared finite floor.
340
+ *
341
+ * `-Infinity` is not a usable value for a meter reading: it propagates NaN
342
+ * through the usual `(db + 60) / 60 * height` bar geometry, serializes to
343
+ * `null` through `JSON.stringify`, and poisons `Math.min`/`Math.max`
344
+ * aggregation. The native meters have always floored at `kFloorDb`, so a
345
+ * producer emitting `-Infinity` also made the two worklets disagree about
346
+ * silence.
347
+ */
310
348
  export function toDb(value: number): number {
311
- return value > 0 ? 20 * Math.log10(value) : Number.NEGATIVE_INFINITY;
349
+ return value > 0 ? Math.max(20 * Math.log10(value), SONARE_FLOOR_DB) : SONARE_FLOOR_DB;
312
350
  }
313
351
 
314
352
  export function isRecord(value: unknown): value is Record<string, unknown> {
@@ -360,6 +398,8 @@ export function readSonareMeterRingBuffer(
360
398
  shortTermLufs: ring.records[offset + 11],
361
399
  integratedLufs: ring.records[offset + 12],
362
400
  gainReductionDb: ring.records[offset + 13],
401
+ inputPeakDbL: ring.records[offset + 14],
402
+ inputPeakDbR: ring.records[offset + 15],
363
403
  });
364
404
  }
365
405
  return { nextReadIndex: writeIndex, meters };
@@ -429,8 +469,8 @@ export function sonareScopeRingRecordFloats(bands: number, maxPoints: number): n
429
469
 
430
470
  export function sonareScopeRingBufferByteLength(
431
471
  capacity: number,
432
- bands = 48,
433
- maxPoints = 32,
472
+ bands = SONARE_SCOPE_DEFAULT_BANDS,
473
+ maxPoints = SONARE_SCOPE_DEFAULT_MAX_POINTS,
434
474
  ): number {
435
475
  const clampedCapacity = Math.max(1, Math.floor(capacity));
436
476
  const clampedBands = Math.max(1, Math.floor(bands));
@@ -445,8 +485,8 @@ export function sonareScopeRingBufferByteLength(
445
485
 
446
486
  export function createSonareScopeRingBuffer(
447
487
  capacity = 64,
448
- bands = 48,
449
- maxPoints = 32,
488
+ bands = SONARE_SCOPE_DEFAULT_BANDS,
489
+ maxPoints = SONARE_SCOPE_DEFAULT_MAX_POINTS,
450
490
  ): SonareScopeRingBuffer {
451
491
  const clampedCapacity = Math.max(1, Math.floor(capacity));
452
492
  const clampedBands = Math.max(1, Math.floor(bands));
@@ -525,8 +565,14 @@ export function scopeRingFromSharedBuffer(
525
565
  const existingBands = Atomics.load(header, 3);
526
566
  const existingMaxPoints = Atomics.load(header, 4);
527
567
  const capacity = Math.max(1, Math.floor(existingCapacity || fallbackCapacity || 1));
528
- const bands = Math.max(1, Math.floor(existingBands || fallbackBands || 48));
529
- const maxPoints = Math.max(0, Math.floor(existingMaxPoints || (fallbackMaxPoints ?? 32)));
568
+ const bands = Math.max(
569
+ 1,
570
+ Math.floor(existingBands || fallbackBands || SONARE_SCOPE_DEFAULT_BANDS),
571
+ );
572
+ const maxPoints = Math.max(
573
+ 0,
574
+ Math.floor(existingMaxPoints || (fallbackMaxPoints ?? SONARE_SCOPE_DEFAULT_MAX_POINTS)),
575
+ );
530
576
  const recordFloats = sonareScopeRingRecordFloats(bands, maxPoints);
531
577
  const minBytes = sonareScopeRingBufferByteLength(capacity, bands, maxPoints);
532
578
  if (sharedBuffer.byteLength < minBytes) {
@@ -948,6 +994,27 @@ function recordOffset(index: number, capacity: number, recordBytes: number): num
948
994
  return (index % capacity) * recordBytes;
949
995
  }
950
996
 
997
+ /**
998
+ * The domain of a uint32 record slot: no fraction, no sign, no overflow.
999
+ *
1000
+ * `setUint32` folds anything outside it into a legal value, so a negative id
1001
+ * becomes a large one and 2**32 becomes 0. An absent value is out of domain;
1002
+ * a slot with a default resolves it before asking.
1003
+ */
1004
+ export function isUint32Slot(value: number | undefined): boolean {
1005
+ return (
1006
+ typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 && value <= 0xffff_ffff
1007
+ );
1008
+ }
1009
+
1010
+ function toUint32Slot(value: number | undefined, fallback: number, name: string): number {
1011
+ const resolved = value ?? fallback;
1012
+ if (!isUint32Slot(resolved)) {
1013
+ throw new RangeError(`${name} must be an integer within [0, 4294967295]`);
1014
+ }
1015
+ return resolved;
1016
+ }
1017
+
951
1018
  function toSafeInteger(value: number | bigint | undefined, fallback: number): number {
952
1019
  const resolved = typeof value === 'bigint' ? Number(value) : value;
953
1020
  if (resolved === undefined) {
@@ -976,8 +1043,10 @@ function writeEngineCommandRecord(
976
1043
  offset: number,
977
1044
  command: SonareEngineCommandRecord,
978
1045
  ): void {
979
- view.setUint32(offset, command.type, true);
980
- view.setUint32(offset + 4, command.targetId ?? 0, true);
1046
+ // Both slots are uint32 and the readers are unsigned, so the writer holds
1047
+ // them to that domain; the command vocabulary itself is the node's check.
1048
+ view.setUint32(offset, toUint32Slot(command.type, 0, 'type'), true);
1049
+ view.setUint32(offset + 4, toUint32Slot(command.targetId, 0, 'targetId'), true);
981
1050
  writeInt64Words(view, offset + 8, toSafeInteger(command.sampleTime, -1));
982
1051
  // argFloat occupies a full 8-byte Float64 slot (replacing the old Float32 +
983
1052
  // 4-byte pad) so PPQ scalars carried here keep full double precision over the
@@ -1052,6 +1121,8 @@ export function meterFromEngine(meter: EngineMeterTelemetry): SonareWorkletMeter
1052
1121
  shortTermLufs: meter.shortTermLufs,
1053
1122
  integratedLufs: meter.integratedLufs,
1054
1123
  gainReductionDb: meter.gainReductionDb,
1124
+ inputPeakDbL: meter.inputPeakDbL,
1125
+ inputPeakDbR: meter.inputPeakDbR,
1055
1126
  };
1056
1127
  }
1057
1128
 
@@ -1,6 +1,6 @@
1
1
  import { RealtimeVoiceChanger } from '../index';
2
- import type { WorkletInput, WorkletOutput } from './audio_types';
3
- import { isRealtimeVoiceChangerMessage } from './guards';
2
+ import { copyPlanesToOutput, type WorkletInput, type WorkletOutput } from './audio_types';
3
+ import { isRealtimeVoiceChangerMessage, requireChannelCount } from './guards';
4
4
  import type {
5
5
  SonareRealtimeVoiceChangerMessage,
6
6
  SonareRealtimeVoiceChangerWorkletProcessorOptions,
@@ -25,12 +25,14 @@ export class SonareRealtimeVoiceChangerWorkletProcessor {
25
25
  // Float32Arrays, so this avoids the per-sample interleave/deinterleave
26
26
  // passes that the older interleaved path needed.
27
27
  private planarChannels: Float32Array[];
28
+ // Reused so the mono fan-out never builds an array on the audio thread.
29
+ private readonly monoPlane: Float32Array[] = [new Float32Array(0)];
28
30
  private destroyed = false;
29
31
 
30
32
  constructor(options: SonareRealtimeVoiceChangerWorkletProcessorOptions = {}) {
31
33
  this.sampleRate = options.sampleRate ?? 48000;
32
34
  this.blockSize = options.blockSize ?? 128;
33
- this.channelCount = Math.max(1, Math.floor(options.channelCount ?? 1));
35
+ this.channelCount = requireChannelCount(options.channelCount, 1);
34
36
  this.changer = new RealtimeVoiceChanger(options.preset ?? 'neutral-monitor');
35
37
  this.changer.prepare(this.sampleRate, this.blockSize, this.channelCount);
36
38
  // Acquire WASM-heap views once, sized to the worst case. These are alive
@@ -100,7 +102,14 @@ export class SonareRealtimeVoiceChangerWorkletProcessor {
100
102
  this.monoInput.fill(0, 0, frames);
101
103
  }
102
104
  this.changer.processPreparedMono(frames);
103
- output[0].set(this.monoOutput.subarray(0, frames));
105
+ // One plane fans out to every output channel and anything it does not
106
+ // fill is zeroed. Writing only output[0] left a stereo host hard-panned
107
+ // left -- the default `channelCount` here is 1 while AudioWorkletNode's
108
+ // default `channelCountMode: 'max'` gives a 2-channel output, so that was
109
+ // the ordinary configuration -- and left the previous block's samples in
110
+ // the channels it skipped.
111
+ this.monoPlane[0] = this.monoOutput;
112
+ copyPlanesToOutput(output, this.monoPlane, frames);
104
113
  return true;
105
114
  }
106
115
 
@@ -122,13 +131,10 @@ export class SonareRealtimeVoiceChangerWorkletProcessor {
122
131
  }
123
132
  }
124
133
  this.changer.processPreparedPlanar(frames);
125
- for (let ch = 0; ch < channels; ch++) {
126
- const src = this.planarChannels[ch];
127
- if (src) {
128
- output[ch].set(src.subarray(0, frames));
129
- }
130
- // No `for frame` inner loop needed; output[ch] is a Float32Array.
131
- }
134
+ // Same rule as the mono branch: an output channel past the last processed
135
+ // plane is silence rather than a duplicate of plane 0, and the tail past
136
+ // `frames` is zeroed.
137
+ copyPlanesToOutput(output, this.planarChannels, frames);
132
138
  return true;
133
139
  }
134
140
 
package/src/worklet.ts CHANGED
@@ -26,6 +26,7 @@ export type {
26
26
  SonareEngineClipPageRequestMessage,
27
27
  SonareEngineSyncAutomationMessage,
28
28
  SonareEngineSyncBuiltinInstrumentMessage,
29
+ SonareEngineSyncBusStripEqBandMessage,
29
30
  SonareEngineSyncCaptureMessage,
30
31
  SonareEngineSyncClipsDeltaMessage,
31
32
  SonareEngineSyncClipsMessage,
@@ -48,6 +49,17 @@ export type {
48
49
  SonareEngineTransportFacade,
49
50
  SonareEngineTransportRequestMessage,
50
51
  SonareEngineTransportResponseMessage,
52
+ SonarePlaybackConfigMessage,
53
+ SonarePlaybackDestroyMessage,
54
+ SonarePlaybackDiagnosticsReplyMessage,
55
+ SonarePlaybackDiagnosticsRequestMessage,
56
+ SonarePlaybackErrorMessage,
57
+ SonarePlaybackMessage,
58
+ SonarePlaybackNodeOptions,
59
+ SonarePlaybackOrientationMessage,
60
+ SonarePlaybackResetMessage,
61
+ SonarePlaybackWorkletDiagnostics,
62
+ SonarePlaybackWorkletProcessorOptions,
51
63
  SonareRealtimeEngineNodeCapabilities,
52
64
  SonareRealtimeEngineNodeOptions,
53
65
  SonareRealtimeEngineWorkletProcessorOptions,
@@ -67,6 +79,11 @@ export {
67
79
  registerSonareWorkletProcessor,
68
80
  SonareWorkletProcessor,
69
81
  } from './worklet/mixer-processor';
82
+ export {
83
+ createSonarePlaybackNode,
84
+ registerSonarePlaybackWorkletProcessor,
85
+ SonarePlaybackWorkletProcessor,
86
+ } from './worklet/playback-processor';
70
87
  export {
71
88
  createSonareClipPageRequestRingBuffer,
72
89
  createSonareEngineCommandRingBuffer,