@drop-ai/core 0.2.0 → 0.3.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 (301) hide show
  1. package/README.md +655 -237
  2. package/dist/actions/ActionRegistry.d.ts.map +1 -1
  3. package/dist/actions/ActionRegistry.js +7 -2
  4. package/dist/actions/ActionRegistry.js.map +1 -1
  5. package/dist/actions/types.d.ts +7 -1
  6. package/dist/actions/types.d.ts.map +1 -1
  7. package/dist/analysis/AudioAnalyzer.d.ts +71 -0
  8. package/dist/analysis/AudioAnalyzer.d.ts.map +1 -0
  9. package/dist/analysis/AudioAnalyzer.js +569 -0
  10. package/dist/analysis/AudioAnalyzer.js.map +1 -0
  11. package/dist/audio/AudioEngine.d.ts +14 -1
  12. package/dist/audio/AudioEngine.d.ts.map +1 -1
  13. package/dist/audio/AudioEngine.js +115 -35
  14. package/dist/audio/AudioEngine.js.map +1 -1
  15. package/dist/audio/Auditioner.d.ts +104 -0
  16. package/dist/audio/Auditioner.d.ts.map +1 -0
  17. package/dist/audio/Auditioner.js +239 -0
  18. package/dist/audio/Auditioner.js.map +1 -0
  19. package/dist/audio/BWFMetadata.d.ts +116 -0
  20. package/dist/audio/BWFMetadata.d.ts.map +1 -0
  21. package/dist/audio/BWFMetadata.js +394 -0
  22. package/dist/audio/BWFMetadata.js.map +1 -0
  23. package/dist/audio/ChannelSplitter.d.ts +37 -0
  24. package/dist/audio/ChannelSplitter.d.ts.map +1 -0
  25. package/dist/audio/ChannelSplitter.js +82 -0
  26. package/dist/audio/ChannelSplitter.js.map +1 -0
  27. package/dist/audio/ExportAnalyzer.d.ts +39 -0
  28. package/dist/audio/ExportAnalyzer.d.ts.map +1 -0
  29. package/dist/audio/ExportAnalyzer.js +76 -0
  30. package/dist/audio/ExportAnalyzer.js.map +1 -0
  31. package/dist/audio/LufsNormalizer.d.ts +37 -0
  32. package/dist/audio/LufsNormalizer.d.ts.map +1 -0
  33. package/dist/audio/LufsNormalizer.js +217 -0
  34. package/dist/audio/LufsNormalizer.js.map +1 -0
  35. package/dist/audio/OfflineExporter.d.ts +1 -6
  36. package/dist/audio/OfflineExporter.d.ts.map +1 -1
  37. package/dist/audio/OfflineExporter.js +31 -10
  38. package/dist/audio/OfflineExporter.js.map +1 -1
  39. package/dist/audio/RoutingGraph.d.ts +134 -0
  40. package/dist/audio/RoutingGraph.d.ts.map +1 -0
  41. package/dist/audio/RoutingGraph.js +547 -0
  42. package/dist/audio/RoutingGraph.js.map +1 -0
  43. package/dist/audio/SampleRateConverter.d.ts +21 -0
  44. package/dist/audio/SampleRateConverter.d.ts.map +1 -0
  45. package/dist/audio/SampleRateConverter.js +84 -0
  46. package/dist/audio/SampleRateConverter.js.map +1 -0
  47. package/dist/audio/SilencePadding.d.ts +29 -0
  48. package/dist/audio/SilencePadding.d.ts.map +1 -0
  49. package/dist/audio/SilencePadding.js +86 -0
  50. package/dist/audio/SilencePadding.js.map +1 -0
  51. package/dist/audio/SourceCache.d.ts +13 -16
  52. package/dist/audio/SourceCache.d.ts.map +1 -1
  53. package/dist/audio/SourceCache.js +21 -33
  54. package/dist/audio/SourceCache.js.map +1 -1
  55. package/dist/audio/engine/Declick.d.ts +98 -0
  56. package/dist/audio/engine/Declick.d.ts.map +1 -0
  57. package/dist/audio/engine/Declick.js +204 -0
  58. package/dist/audio/engine/Declick.js.map +1 -0
  59. package/dist/audio/engine/DiskIO.d.ts +172 -0
  60. package/dist/audio/engine/DiskIO.d.ts.map +1 -0
  61. package/dist/audio/engine/DiskIO.js +384 -0
  62. package/dist/audio/engine/DiskIO.js.map +1 -0
  63. package/dist/audio/engine/LatencyCompensator.d.ts +46 -0
  64. package/dist/audio/engine/LatencyCompensator.d.ts.map +1 -0
  65. package/dist/audio/engine/LatencyCompensator.js +84 -0
  66. package/dist/audio/engine/LatencyCompensator.js.map +1 -0
  67. package/dist/audio/engine/MultiTrackRecorder.d.ts +146 -0
  68. package/dist/audio/engine/MultiTrackRecorder.d.ts.map +1 -0
  69. package/dist/audio/engine/MultiTrackRecorder.js +359 -0
  70. package/dist/audio/engine/MultiTrackRecorder.js.map +1 -0
  71. package/dist/audio/engine/PlaylistEngine.d.ts.map +1 -1
  72. package/dist/audio/engine/PlaylistEngine.js +9 -2
  73. package/dist/audio/engine/PlaylistEngine.js.map +1 -1
  74. package/dist/audio/engine/PunchRecordManager.d.ts +113 -0
  75. package/dist/audio/engine/PunchRecordManager.d.ts.map +1 -0
  76. package/dist/audio/engine/PunchRecordManager.js +191 -0
  77. package/dist/audio/engine/PunchRecordManager.js.map +1 -0
  78. package/dist/audio/engine/RoutingGraph.d.ts +135 -0
  79. package/dist/audio/engine/RoutingGraph.d.ts.map +1 -0
  80. package/dist/audio/engine/RoutingGraph.js +436 -0
  81. package/dist/audio/engine/RoutingGraph.js.map +1 -0
  82. package/dist/audio/engine/SidechainRouter.d.ts +139 -0
  83. package/dist/audio/engine/SidechainRouter.d.ts.map +1 -0
  84. package/dist/audio/engine/SidechainRouter.js +292 -0
  85. package/dist/audio/engine/SidechainRouter.js.map +1 -0
  86. package/dist/audio/engine/XrunTracker.d.ts +71 -0
  87. package/dist/audio/engine/XrunTracker.d.ts.map +1 -0
  88. package/dist/audio/engine/XrunTracker.js +118 -0
  89. package/dist/audio/engine/XrunTracker.js.map +1 -0
  90. package/dist/audio/export/CDMarkerExporter.d.ts +62 -0
  91. package/dist/audio/export/CDMarkerExporter.d.ts.map +1 -0
  92. package/dist/audio/export/CDMarkerExporter.js +164 -0
  93. package/dist/audio/export/CDMarkerExporter.js.map +1 -0
  94. package/dist/audio/export/ExportGraphBuilder.d.ts +121 -0
  95. package/dist/audio/export/ExportGraphBuilder.d.ts.map +1 -0
  96. package/dist/audio/export/ExportGraphBuilder.js +522 -0
  97. package/dist/audio/export/ExportGraphBuilder.js.map +1 -0
  98. package/dist/audio/export/ExportPresetManager.d.ts +87 -0
  99. package/dist/audio/export/ExportPresetManager.d.ts.map +1 -0
  100. package/dist/audio/export/ExportPresetManager.js +306 -0
  101. package/dist/audio/export/ExportPresetManager.js.map +1 -0
  102. package/dist/commands/handlers/RegionHandler.d.ts.map +1 -1
  103. package/dist/commands/handlers/RegionHandler.js +25 -1
  104. package/dist/commands/handlers/RegionHandler.js.map +1 -1
  105. package/dist/commands/handlers/TrackHandler.js +1 -1
  106. package/dist/commands/handlers/TrackHandler.js.map +1 -1
  107. package/dist/commands/impl/FreezeTrackCommand.d.ts.map +1 -1
  108. package/dist/commands/impl/FreezeTrackCommand.js +37 -36
  109. package/dist/commands/impl/FreezeTrackCommand.js.map +1 -1
  110. package/dist/commands/impl/SetRegionTimeDomainCommand.d.ts.map +1 -1
  111. package/dist/commands/impl/SetRegionTimeDomainCommand.js +3 -3
  112. package/dist/commands/impl/SetRegionTimeDomainCommand.js.map +1 -1
  113. package/dist/commands/impl/ToggleLoopCommand.d.ts +1 -0
  114. package/dist/commands/impl/ToggleLoopCommand.d.ts.map +1 -1
  115. package/dist/commands/impl/ToggleLoopCommand.js +10 -0
  116. package/dist/commands/impl/ToggleLoopCommand.js.map +1 -1
  117. package/dist/commands/impl/TrimRegionCommand.d.ts +14 -1
  118. package/dist/commands/impl/TrimRegionCommand.d.ts.map +1 -1
  119. package/dist/commands/impl/TrimRegionCommand.js +138 -33
  120. package/dist/commands/impl/TrimRegionCommand.js.map +1 -1
  121. package/dist/commands/impl/TrimRegionToPlayheadCommand.d.ts +27 -0
  122. package/dist/commands/impl/TrimRegionToPlayheadCommand.d.ts.map +1 -0
  123. package/dist/commands/impl/TrimRegionToPlayheadCommand.js +69 -0
  124. package/dist/commands/impl/TrimRegionToPlayheadCommand.js.map +1 -0
  125. package/dist/commands/impl/TrimRegionToRangeCommand.d.ts +28 -0
  126. package/dist/commands/impl/TrimRegionToRangeCommand.d.ts.map +1 -0
  127. package/dist/commands/impl/TrimRegionToRangeCommand.js +69 -0
  128. package/dist/commands/impl/TrimRegionToRangeCommand.js.map +1 -0
  129. package/dist/commands/impl/TrimToAdjacentRegionCommand.d.ts +27 -0
  130. package/dist/commands/impl/TrimToAdjacentRegionCommand.d.ts.map +1 -0
  131. package/dist/commands/impl/TrimToAdjacentRegionCommand.js +77 -0
  132. package/dist/commands/impl/TrimToAdjacentRegionCommand.js.map +1 -0
  133. package/dist/commands/types.d.ts +19 -0
  134. package/dist/commands/types.d.ts.map +1 -1
  135. package/dist/commands/types.js +21 -0
  136. package/dist/commands/types.js.map +1 -1
  137. package/dist/domain/Crossfade.d.ts +78 -0
  138. package/dist/domain/Crossfade.d.ts.map +1 -0
  139. package/dist/domain/Crossfade.js +216 -0
  140. package/dist/domain/Crossfade.js.map +1 -0
  141. package/dist/domain/ExportConfig.d.ts +98 -1
  142. package/dist/domain/ExportConfig.d.ts.map +1 -1
  143. package/dist/domain/ExportConfig.js +154 -1
  144. package/dist/domain/ExportConfig.js.map +1 -1
  145. package/dist/domain/ExportPreset.d.ts +62 -0
  146. package/dist/domain/ExportPreset.d.ts.map +1 -0
  147. package/dist/domain/ExportPreset.js +79 -0
  148. package/dist/domain/ExportPreset.js.map +1 -0
  149. package/dist/domain/ImportStatus.d.ts +40 -0
  150. package/dist/domain/ImportStatus.d.ts.map +1 -0
  151. package/dist/domain/ImportStatus.js +86 -0
  152. package/dist/domain/ImportStatus.js.map +1 -0
  153. package/dist/domain/Playlist.d.ts +72 -0
  154. package/dist/domain/Playlist.d.ts.map +1 -1
  155. package/dist/domain/Playlist.js +231 -6
  156. package/dist/domain/Playlist.js.map +1 -1
  157. package/dist/domain/Region.d.ts +76 -0
  158. package/dist/domain/Region.d.ts.map +1 -1
  159. package/dist/domain/Region.js +234 -1
  160. package/dist/domain/Region.js.map +1 -1
  161. package/dist/domain/Route.d.ts +43 -3
  162. package/dist/domain/Route.d.ts.map +1 -1
  163. package/dist/domain/Route.js +92 -6
  164. package/dist/domain/Route.js.map +1 -1
  165. package/dist/domain/Session.d.ts +31 -0
  166. package/dist/domain/Session.d.ts.map +1 -1
  167. package/dist/domain/Session.js +70 -0
  168. package/dist/domain/Session.js.map +1 -1
  169. package/dist/domain/Source.d.ts +145 -1
  170. package/dist/domain/Source.d.ts.map +1 -1
  171. package/dist/domain/Source.js +144 -1
  172. package/dist/domain/Source.js.map +1 -1
  173. package/dist/domain/ThawList.d.ts +37 -0
  174. package/dist/domain/ThawList.d.ts.map +1 -0
  175. package/dist/domain/ThawList.js +73 -0
  176. package/dist/domain/ThawList.js.map +1 -0
  177. package/dist/domain/Track.d.ts +93 -1
  178. package/dist/domain/Track.d.ts.map +1 -1
  179. package/dist/domain/Track.js +136 -0
  180. package/dist/domain/Track.js.map +1 -1
  181. package/dist/domain/TriggerBox.d.ts +123 -0
  182. package/dist/domain/TriggerBox.d.ts.map +1 -0
  183. package/dist/domain/TriggerBox.js +430 -0
  184. package/dist/domain/TriggerBox.js.map +1 -0
  185. package/dist/domain/VCATrack.d.ts +64 -1
  186. package/dist/domain/VCATrack.d.ts.map +1 -1
  187. package/dist/domain/VCATrack.js +128 -1
  188. package/dist/domain/VCATrack.js.map +1 -1
  189. package/dist/domain/VideoExportConfig.d.ts +117 -0
  190. package/dist/domain/VideoExportConfig.d.ts.map +1 -0
  191. package/dist/domain/VideoExportConfig.js +244 -0
  192. package/dist/domain/VideoExportConfig.js.map +1 -0
  193. package/dist/domain/VideoExportStatus.d.ts +89 -0
  194. package/dist/domain/VideoExportStatus.d.ts.map +1 -0
  195. package/dist/domain/VideoExportStatus.js +192 -0
  196. package/dist/domain/VideoExportStatus.js.map +1 -0
  197. package/dist/domain/VideoMetadata.d.ts +46 -0
  198. package/dist/domain/VideoMetadata.d.ts.map +1 -0
  199. package/dist/domain/VideoMetadata.js +2 -0
  200. package/dist/domain/VideoMetadata.js.map +1 -0
  201. package/dist/domain/index.d.ts +5 -0
  202. package/dist/domain/index.d.ts.map +1 -1
  203. package/dist/domain/index.js +5 -0
  204. package/dist/domain/index.js.map +1 -1
  205. package/dist/domain/temporal/TempoMap.d.ts +199 -2
  206. package/dist/domain/temporal/TempoMap.d.ts.map +1 -1
  207. package/dist/domain/temporal/TempoMap.js +464 -30
  208. package/dist/domain/temporal/TempoMap.js.map +1 -1
  209. package/dist/index.d.ts +43 -1
  210. package/dist/index.d.ts.map +1 -1
  211. package/dist/index.js +32 -1
  212. package/dist/index.js.map +1 -1
  213. package/dist/lib/ThawList.d.ts +100 -0
  214. package/dist/lib/ThawList.d.ts.map +1 -0
  215. package/dist/lib/ThawList.js +153 -0
  216. package/dist/lib/ThawList.js.map +1 -0
  217. package/dist/midi/MidiFileParser.d.ts +33 -0
  218. package/dist/midi/MidiFileParser.d.ts.map +1 -0
  219. package/dist/midi/MidiFileParser.js +246 -0
  220. package/dist/midi/MidiFileParser.js.map +1 -0
  221. package/dist/midi/MidiFileWriter.d.ts +31 -0
  222. package/dist/midi/MidiFileWriter.d.ts.map +1 -0
  223. package/dist/midi/MidiFileWriter.js +160 -0
  224. package/dist/midi/MidiFileWriter.js.map +1 -0
  225. package/dist/midi/MidiImporter.d.ts +37 -0
  226. package/dist/midi/MidiImporter.d.ts.map +1 -0
  227. package/dist/midi/MidiImporter.js +62 -0
  228. package/dist/midi/MidiImporter.js.map +1 -0
  229. package/dist/plugins/PluginPresetManager.d.ts +86 -0
  230. package/dist/plugins/PluginPresetManager.d.ts.map +1 -0
  231. package/dist/plugins/PluginPresetManager.js +140 -0
  232. package/dist/plugins/PluginPresetManager.js.map +1 -0
  233. package/dist/processing/IO.d.ts +15 -1
  234. package/dist/processing/IO.d.ts.map +1 -1
  235. package/dist/processing/IO.js +29 -1
  236. package/dist/processing/IO.js.map +1 -1
  237. package/dist/processing/InternalSend.d.ts +104 -0
  238. package/dist/processing/InternalSend.d.ts.map +1 -0
  239. package/dist/processing/InternalSend.js +175 -0
  240. package/dist/processing/InternalSend.js.map +1 -0
  241. package/dist/processing/MeterDSP.d.ts +166 -0
  242. package/dist/processing/MeterDSP.d.ts.map +1 -0
  243. package/dist/processing/MeterDSP.js +754 -0
  244. package/dist/processing/MeterDSP.js.map +1 -0
  245. package/dist/processing/Panner.d.ts +145 -0
  246. package/dist/processing/Panner.d.ts.map +1 -0
  247. package/dist/processing/Panner.js +281 -0
  248. package/dist/processing/Panner.js.map +1 -0
  249. package/dist/processing/PluginInsert.d.ts +56 -1
  250. package/dist/processing/PluginInsert.d.ts.map +1 -1
  251. package/dist/processing/PluginInsert.js +180 -2
  252. package/dist/processing/PluginInsert.js.map +1 -1
  253. package/dist/processing/Processor.d.ts +38 -0
  254. package/dist/processing/Processor.d.ts.map +1 -1
  255. package/dist/processing/Processor.js +60 -1
  256. package/dist/processing/Processor.js.map +1 -1
  257. package/dist/processing/SurroundPanner.d.ts +122 -0
  258. package/dist/processing/SurroundPanner.d.ts.map +1 -0
  259. package/dist/processing/SurroundPanner.js +376 -0
  260. package/dist/processing/SurroundPanner.js.map +1 -0
  261. package/dist/processing/TruePeakLimiter.d.ts +34 -0
  262. package/dist/processing/TruePeakLimiter.d.ts.map +1 -0
  263. package/dist/processing/TruePeakLimiter.js +144 -0
  264. package/dist/processing/TruePeakLimiter.js.map +1 -0
  265. package/dist/storage/ExportPresetStorage.d.ts +33 -0
  266. package/dist/storage/ExportPresetStorage.d.ts.map +1 -0
  267. package/dist/storage/ExportPresetStorage.js +121 -0
  268. package/dist/storage/ExportPresetStorage.js.map +1 -0
  269. package/dist/storage/SessionArchive.d.ts +73 -0
  270. package/dist/storage/SessionArchive.d.ts.map +1 -0
  271. package/dist/storage/SessionArchive.js +211 -0
  272. package/dist/storage/SessionArchive.js.map +1 -0
  273. package/dist/storage/SessionTemplate.d.ts +74 -5
  274. package/dist/storage/SessionTemplate.d.ts.map +1 -1
  275. package/dist/storage/SessionTemplate.js +247 -53
  276. package/dist/storage/SessionTemplate.js.map +1 -1
  277. package/dist/utils/BwfMetadataWriter.d.ts +42 -0
  278. package/dist/utils/BwfMetadataWriter.d.ts.map +1 -0
  279. package/dist/utils/BwfMetadataWriter.js +131 -0
  280. package/dist/utils/BwfMetadataWriter.js.map +1 -0
  281. package/dist/utils/FilenameTemplate.d.ts +40 -0
  282. package/dist/utils/FilenameTemplate.d.ts.map +1 -0
  283. package/dist/utils/FilenameTemplate.js +78 -0
  284. package/dist/utils/FilenameTemplate.js.map +1 -0
  285. package/dist/utils/Logger.d.ts +28 -0
  286. package/dist/utils/Logger.d.ts.map +1 -0
  287. package/dist/utils/Logger.js +46 -0
  288. package/dist/utils/Logger.js.map +1 -0
  289. package/dist/utils/Mp4ChapterGenerator.d.ts +17 -0
  290. package/dist/utils/Mp4ChapterGenerator.d.ts.map +1 -0
  291. package/dist/utils/Mp4ChapterGenerator.js +33 -0
  292. package/dist/utils/Mp4ChapterGenerator.js.map +1 -0
  293. package/dist/utils/OggEncoder.d.ts +20 -20
  294. package/dist/utils/OggEncoder.d.ts.map +1 -1
  295. package/dist/utils/OggEncoder.js +114 -50
  296. package/dist/utils/OggEncoder.js.map +1 -1
  297. package/dist/utils/TocGenerator.d.ts +17 -0
  298. package/dist/utils/TocGenerator.d.ts.map +1 -0
  299. package/dist/utils/TocGenerator.js +47 -0
  300. package/dist/utils/TocGenerator.js.map +1 -0
  301. package/package.json +7 -8
@@ -0,0 +1,754 @@
1
+ /**
2
+ * Professional metering DSP algorithms implementing broadcast standards.
3
+ * Inspired by Ardour's meter.h and industry standard implementations.
4
+ *
5
+ * Supported meter types:
6
+ * - Peak (simple sample-peak detection)
7
+ * - IEC 60268-10 Type I (DIN, Nordic)
8
+ * - IEC 60268-10 Type II (BBC PPM, EBU)
9
+ * - K-system (K-12, K-14, K-20)
10
+ * - VU (300ms RMS integration)
11
+ * - LUFS / EBU R128 (momentary, short-term, integrated, loudness range)
12
+ * - ITU-R BS.1770 true peak (4x oversampling)
13
+ */
14
+ // ---------------------------------------------------------------------------
15
+ // Enums & Interfaces
16
+ // ---------------------------------------------------------------------------
17
+ export var MeterType;
18
+ (function (MeterType) {
19
+ MeterType["PEAK"] = "peak";
20
+ MeterType["IEC1_DIN"] = "iec1_din";
21
+ MeterType["IEC1_NORDIC"] = "iec1_nordic";
22
+ MeterType["IEC2_BBC"] = "iec2_bbc";
23
+ MeterType["IEC2_EBU"] = "iec2_ebu";
24
+ MeterType["K_12"] = "k12";
25
+ MeterType["K_14"] = "k14";
26
+ MeterType["K_20"] = "k20";
27
+ MeterType["VU"] = "vu";
28
+ MeterType["LUFS"] = "lufs";
29
+ MeterType["TRUE_PEAK"] = "true_peak";
30
+ })(MeterType || (MeterType = {}));
31
+ // ---------------------------------------------------------------------------
32
+ // Constants
33
+ // ---------------------------------------------------------------------------
34
+ const NEG_INF = -Infinity;
35
+ const LOG10_20 = 20 / Math.LN10; // pre-computed multiplier for 20*log10
36
+ /**
37
+ * 4x oversampling polyphase FIR coefficients for ITU-R BS.1770 true peak.
38
+ * 12 taps per phase, 4 phases = 48 total coefficients.
39
+ * These approximate a half-band windowed-sinc interpolation filter.
40
+ */
41
+ const TP_FIR_PHASES = [
42
+ // Phase 0 (original samples, shifted by filter delay)
43
+ new Float64Array([
44
+ 0.0017089843750,
45
+ 0.0000000000000,
46
+ -0.0189208984375,
47
+ 0.0000000000000,
48
+ 0.3031005859375,
49
+ 0.4736328125000,
50
+ 0.3031005859375,
51
+ 0.0000000000000,
52
+ -0.0189208984375,
53
+ 0.0000000000000,
54
+ 0.0017089843750,
55
+ 0.0000000000000,
56
+ ]),
57
+ // Phase 1
58
+ new Float64Array([
59
+ 0.0013427734375,
60
+ -0.0117797851562,
61
+ -0.0245361328125,
62
+ 0.0527343750000,
63
+ 0.2926025390625,
64
+ 0.4694213867188,
65
+ 0.2926025390625,
66
+ 0.0527343750000,
67
+ -0.0245361328125,
68
+ -0.0117797851562,
69
+ 0.0013427734375,
70
+ 0.0000000000000,
71
+ ]),
72
+ // Phase 2
73
+ new Float64Array([
74
+ 0.0008544921875,
75
+ -0.0200805664062,
76
+ -0.0217285156250,
77
+ 0.0896606445312,
78
+ 0.2593994140625,
79
+ 0.4530029296875,
80
+ 0.3236083984375,
81
+ 0.0896606445312,
82
+ -0.0217285156250,
83
+ -0.0200805664062,
84
+ 0.0008544921875,
85
+ 0.0000000000000,
86
+ ]),
87
+ // Phase 3
88
+ new Float64Array([
89
+ 0.0004272460938,
90
+ -0.0254516601562,
91
+ -0.0128173828125,
92
+ 0.1141357421875,
93
+ 0.2153320312500,
94
+ 0.4309082031250,
95
+ 0.3472900390625,
96
+ 0.1141357421875,
97
+ -0.0128173828125,
98
+ -0.0254516601562,
99
+ 0.0004272460938,
100
+ 0.0000000000000,
101
+ ]),
102
+ ];
103
+ const TP_FIR_LEN = 12; // taps per phase
104
+ /**
105
+ * Design the K-weighting high shelf filter (stage 1).
106
+ * f0 = 1681.97 Hz, gain = +3.999 dB, Q = 0.7071968
107
+ */
108
+ function designKWeightShelf(sr) {
109
+ const f0 = 1681.974450955533;
110
+ const G = 3.999843853973347; // dB
111
+ const Q = 0.7071752369554196;
112
+ const A = Math.pow(10, G / 40); // sqrt of linear gain
113
+ const w0 = 2 * Math.PI * f0 / sr;
114
+ const cosw0 = Math.cos(w0);
115
+ const sinw0 = Math.sin(w0);
116
+ const alpha = sinw0 / (2 * Q);
117
+ const Ap1 = A + 1;
118
+ const Am1 = A - 1;
119
+ const twoSqrtAAlpha = 2 * Math.sqrt(A) * alpha;
120
+ const a0 = Ap1 - Am1 * cosw0 + twoSqrtAAlpha;
121
+ return {
122
+ b0: (A * (Ap1 + Am1 * cosw0 + twoSqrtAAlpha)) / a0,
123
+ b1: (-2 * A * (Am1 + Ap1 * cosw0)) / a0,
124
+ b2: (A * (Ap1 + Am1 * cosw0 - twoSqrtAAlpha)) / a0,
125
+ a1: (2 * (Am1 - Ap1 * cosw0)) / a0,
126
+ a2: (Ap1 - Am1 * cosw0 - twoSqrtAAlpha) / a0,
127
+ };
128
+ }
129
+ /**
130
+ * Design the K-weighting high-pass filter (stage 2).
131
+ * f0 = 38.13547087602444 Hz, Q = 0.5003270373238773
132
+ */
133
+ function designKWeightHP(sr) {
134
+ const f0 = 38.13547087602444;
135
+ const Q = 0.5003270373238773;
136
+ const w0 = 2 * Math.PI * f0 / sr;
137
+ const cosw0 = Math.cos(w0);
138
+ const sinw0 = Math.sin(w0);
139
+ const alpha = sinw0 / (2 * Q);
140
+ const a0 = 1 + alpha;
141
+ return {
142
+ b0: ((1 + cosw0) / 2) / a0,
143
+ b1: (-(1 + cosw0)) / a0,
144
+ b2: ((1 + cosw0) / 2) / a0,
145
+ a1: (-2 * cosw0) / a0,
146
+ a2: (1 - alpha) / a0,
147
+ };
148
+ }
149
+ function makeBiquadState() {
150
+ return { x1: 0, x2: 0, y1: 0, y2: 0 };
151
+ }
152
+ function biquadProcess(c, s, x) {
153
+ const y = c.b0 * x + c.b1 * s.x1 + c.b2 * s.x2
154
+ - c.a1 * s.y1 - c.a2 * s.y2;
155
+ s.x2 = s.x1;
156
+ s.x1 = x;
157
+ s.y2 = s.y1;
158
+ s.y1 = y;
159
+ return y;
160
+ }
161
+ // ---------------------------------------------------------------------------
162
+ // MeterDSP
163
+ // ---------------------------------------------------------------------------
164
+ export class MeterDSP {
165
+ // ── Constructor ─────────────────────────────────────────────────────
166
+ constructor(type, sampleRate, channelCount = 2) {
167
+ this._type = type;
168
+ this._sampleRate = sampleRate;
169
+ this._channelCount = channelCount;
170
+ // Per-channel arrays
171
+ this._peaks = new Float64Array(channelCount);
172
+ this._rms = new Float64Array(channelCount);
173
+ this._peakHold = new Float64Array(channelCount).fill(-Infinity);
174
+ this._peakHoldDecay = 20; // dB/s default
175
+ this._peakHoldTime = Math.round(sampleRate * 2); // 2s hold
176
+ this._peakHoldCounter = new Float64Array(channelCount);
177
+ this._overCount = new Int32Array(channelCount);
178
+ // IEC integrator
179
+ this._iecIntegrator = new Float64Array(channelCount);
180
+ // VU integrator
181
+ this._vuIntegrator = new Float64Array(channelCount);
182
+ // LUFS - K-weighting
183
+ this._kWeightingState = [];
184
+ this._kShelfStates = [];
185
+ this._kHPStates = [];
186
+ for (let ch = 0; ch < channelCount; ch++) {
187
+ this._kWeightingState.push(new Float64Array(4)); // unused legacy slot
188
+ this._kShelfStates.push(makeBiquadState());
189
+ this._kHPStates.push(makeBiquadState());
190
+ }
191
+ this._initKWeighting();
192
+ // LUFS ring buffers (100ms blocks)
193
+ this._blockSize100ms = Math.round(sampleRate * 0.1);
194
+ this._momentaryLen = 4; // 400ms = 4 * 100ms
195
+ this._shortTermLen = 30; // 3s = 30 * 100ms
196
+ this._momentaryBuffer = [];
197
+ this._shortTermBuffer = [];
198
+ this._lufsBlockAccum = new Float64Array(channelCount);
199
+ for (let ch = 0; ch < channelCount; ch++) {
200
+ this._momentaryBuffer.push(new Float64Array(this._momentaryLen));
201
+ this._shortTermBuffer.push(new Float64Array(this._shortTermLen));
202
+ }
203
+ this._lufsBlockSampleCount = 0;
204
+ this._momentaryWriteIdx = 0;
205
+ this._shortTermWriteIdx = 0;
206
+ this._momentaryFilled = 0;
207
+ this._shortTermFilled = 0;
208
+ // Integrated loudness
209
+ this._integratedSum = 0;
210
+ this._integratedCount = 0;
211
+ this._gatingBlocks = [];
212
+ // True peak
213
+ this._truePeakMax = 0;
214
+ this._tpHistory = [];
215
+ for (let ch = 0; ch < channelCount; ch++) {
216
+ this._tpHistory.push(new Float64Array(TP_FIR_LEN));
217
+ }
218
+ }
219
+ // ── K-weighting initialisation ──────────────────────────────────────
220
+ _initKWeighting() {
221
+ this._kShelfCoeffs = designKWeightShelf(this._sampleRate);
222
+ this._kHPCoeffs = designKWeightHP(this._sampleRate);
223
+ }
224
+ // ── IEC coefficient helpers ─────────────────────────────────────────
225
+ /**
226
+ * Compute a single-pole IIR coefficient from an integration time.
227
+ * coeff = exp(-2.2 / (time * sampleRate))
228
+ *
229
+ * A coefficient of ~1 means very slow response (long integration);
230
+ * a coefficient close to 0 means instantaneous tracking.
231
+ */
232
+ _iecCoeff(timeSec) {
233
+ if (timeSec <= 0)
234
+ return 0;
235
+ return Math.exp(-2.2 / (timeSec * this._sampleRate));
236
+ }
237
+ /**
238
+ * Return rise and fall coefficients for the current IEC type.
239
+ */
240
+ _getIecCoeffs() {
241
+ switch (this._type) {
242
+ case MeterType.IEC1_DIN:
243
+ // DIN standard: 1.7s rise, 1.7s fall
244
+ return {
245
+ rise: this._iecCoeff(1.7),
246
+ fall: this._iecCoeff(1.7),
247
+ };
248
+ case MeterType.IEC1_NORDIC:
249
+ // Nordic: ~5ms rise, 1.7s fall
250
+ return {
251
+ rise: this._iecCoeff(0.005),
252
+ fall: this._iecCoeff(1.7),
253
+ };
254
+ case MeterType.IEC2_BBC:
255
+ // BBC PPM: 10ms integration, 2.8s return
256
+ return {
257
+ rise: this._iecCoeff(0.01),
258
+ fall: this._iecCoeff(2.8),
259
+ };
260
+ case MeterType.IEC2_EBU:
261
+ // EBU: 10ms integration, 3.0s return (slightly slower fall)
262
+ return {
263
+ rise: this._iecCoeff(0.01),
264
+ fall: this._iecCoeff(3.0),
265
+ };
266
+ default:
267
+ return { rise: 0, fall: 0 };
268
+ }
269
+ }
270
+ // ── Core Processing ─────────────────────────────────────────────────
271
+ /**
272
+ * Process a single channel of audio samples.
273
+ */
274
+ process(samples, channel) {
275
+ if (channel < 0 || channel >= this._channelCount)
276
+ return;
277
+ const n = samples.length;
278
+ if (n === 0)
279
+ return;
280
+ // ---- Sample peak & RMS & over count ----------------------------
281
+ let peak = 0;
282
+ let sumSq = 0;
283
+ let overs = 0;
284
+ let consecutiveOver = 0;
285
+ for (let i = 0; i < n; i++) {
286
+ const s = samples[i];
287
+ const abs = Math.abs(s);
288
+ if (abs > peak)
289
+ peak = abs;
290
+ sumSq += s * s;
291
+ if (abs >= 1.0) {
292
+ consecutiveOver++;
293
+ }
294
+ else {
295
+ if (consecutiveOver > overs)
296
+ overs = consecutiveOver;
297
+ consecutiveOver = 0;
298
+ }
299
+ }
300
+ if (consecutiveOver > overs)
301
+ overs = consecutiveOver;
302
+ const rmsLinear = Math.sqrt(sumSq / n);
303
+ const peakDb = peak > 0 ? LOG10_20 * Math.log(peak) : NEG_INF;
304
+ const rmsDb = rmsLinear > 0 ? LOG10_20 * Math.log(rmsLinear) : NEG_INF;
305
+ this._peaks[channel] = peakDb;
306
+ this._rms[channel] = rmsDb;
307
+ this._overCount[channel] = overs;
308
+ // ---- Peak hold -------------------------------------------------
309
+ if (peakDb > this._peakHold[channel]) {
310
+ this._peakHold[channel] = peakDb;
311
+ this._peakHoldCounter[channel] = this._peakHoldTime;
312
+ }
313
+ else {
314
+ if (this._peakHoldCounter[channel] > 0) {
315
+ this._peakHoldCounter[channel] -= n;
316
+ }
317
+ else {
318
+ // Decay
319
+ const decayAmount = this._peakHoldDecay * (n / this._sampleRate);
320
+ this._peakHold[channel] -= decayAmount;
321
+ if (this._peakHold[channel] < NEG_INF) {
322
+ this._peakHold[channel] = NEG_INF;
323
+ }
324
+ }
325
+ }
326
+ // ---- IEC integration -------------------------------------------
327
+ if (this._type === MeterType.IEC1_DIN ||
328
+ this._type === MeterType.IEC1_NORDIC ||
329
+ this._type === MeterType.IEC2_BBC ||
330
+ this._type === MeterType.IEC2_EBU) {
331
+ const { rise, fall } = this._getIecCoeffs();
332
+ // IEC meters track the absolute value through a single-pole filter
333
+ let integrator = this._iecIntegrator[channel];
334
+ for (let i = 0; i < n; i++) {
335
+ const abs = Math.abs(samples[i]);
336
+ const coeff = abs > integrator ? rise : fall;
337
+ integrator = (1 - coeff) * abs + coeff * integrator;
338
+ }
339
+ this._iecIntegrator[channel] = integrator;
340
+ // Override peak with IEC-filtered value
341
+ const iecDb = integrator > 0 ? LOG10_20 * Math.log(integrator) : NEG_INF;
342
+ this._peaks[channel] = iecDb;
343
+ }
344
+ // ---- VU integration (300ms RMS) --------------------------------
345
+ if (this._type === MeterType.VU) {
346
+ // Single-pole IIR on the squared signal ≈ exponential RMS
347
+ const vuCoeff = Math.exp(-2.2 / (0.3 * this._sampleRate));
348
+ let vuInt = this._vuIntegrator[channel];
349
+ for (let i = 0; i < n; i++) {
350
+ const sq = samples[i] * samples[i];
351
+ vuInt = (1 - vuCoeff) * sq + vuCoeff * vuInt;
352
+ }
353
+ this._vuIntegrator[channel] = vuInt;
354
+ const vuRms = Math.sqrt(vuInt);
355
+ // VU meter: overshoot on transients (~1.5%)
356
+ // Modelled by letting peak slightly bleed into the RMS reading
357
+ const vuLevel = vuRms + 0.015 * (peak - vuRms);
358
+ const vuDb = vuLevel > 0 ? LOG10_20 * Math.log(vuLevel) : NEG_INF;
359
+ this._rms[channel] = vuDb;
360
+ this._peaks[channel] = vuDb; // VU uses unified reading
361
+ }
362
+ // ---- K-metering ------------------------------------------------
363
+ if (this._type === MeterType.K_12 ||
364
+ this._type === MeterType.K_14 ||
365
+ this._type === MeterType.K_20) {
366
+ const ref = this.getKReference();
367
+ // Offset the RMS reading by the K reference
368
+ if (rmsDb > NEG_INF) {
369
+ this._rms[channel] = rmsDb - ref;
370
+ }
371
+ if (peakDb > NEG_INF) {
372
+ this._peaks[channel] = peakDb - ref;
373
+ }
374
+ }
375
+ // ---- LUFS accumulation -----------------------------------------
376
+ if (this._type === MeterType.LUFS) {
377
+ this._processLufsChannel(samples, channel);
378
+ }
379
+ // ---- True peak -------------------------------------------------
380
+ if (this._type === MeterType.TRUE_PEAK || this._type === MeterType.LUFS) {
381
+ this._processTruePeak(samples, channel);
382
+ }
383
+ }
384
+ /**
385
+ * Process a multi-channel block at once.
386
+ * buffer[ch] must contain at least blockSize samples.
387
+ */
388
+ processBlock(buffer, blockSize) {
389
+ const numCh = Math.min(buffer.length, this._channelCount);
390
+ for (let ch = 0; ch < numCh; ch++) {
391
+ const data = buffer[ch];
392
+ // Slice or use directly depending on length
393
+ if (data.length === blockSize) {
394
+ this.process(data, ch);
395
+ }
396
+ else {
397
+ this.process(data.subarray(0, blockSize), ch);
398
+ }
399
+ }
400
+ }
401
+ // ── LUFS Processing (ITU-R BS.1770-4) ──────────────────────────────
402
+ /**
403
+ * Accumulate K-weighted power for one channel into the 100ms block
404
+ * accumulator. When a full 100ms block is completed, push it into
405
+ * the momentary and short-term ring buffers and perform gating.
406
+ */
407
+ _processLufsChannel(samples, channel) {
408
+ const shelfC = this._kShelfCoeffs;
409
+ const hpC = this._kHPCoeffs;
410
+ const shelfS = this._kShelfStates[channel];
411
+ const hpS = this._kHPStates[channel];
412
+ let remaining = samples.length;
413
+ let offset = 0;
414
+ while (remaining > 0) {
415
+ const spaceInBlock = this._blockSize100ms - this._lufsBlockSampleCount;
416
+ const toProcess = Math.min(remaining, spaceInBlock);
417
+ let sumSq = 0;
418
+ for (let i = 0; i < toProcess; i++) {
419
+ // K-weighting: high shelf then high-pass
420
+ let s = samples[offset + i];
421
+ s = biquadProcess(shelfC, shelfS, s);
422
+ s = biquadProcess(hpC, hpS, s);
423
+ sumSq += s * s;
424
+ }
425
+ this._lufsBlockAccum[channel] += sumSq;
426
+ offset += toProcess;
427
+ remaining -= toProcess;
428
+ // Only advance the block counter on channel 0 to keep channels in sync
429
+ if (channel === 0) {
430
+ this._lufsBlockSampleCount += toProcess;
431
+ }
432
+ // If this is the last channel and the block is full, commit
433
+ if (channel === this._channelCount - 1 &&
434
+ this._lufsBlockSampleCount >= this._blockSize100ms) {
435
+ this._commitLufsBlock();
436
+ }
437
+ }
438
+ }
439
+ /**
440
+ * Commit a completed 100ms block into the ring buffers and perform gating.
441
+ */
442
+ _commitLufsBlock() {
443
+ const numCh = this._channelCount;
444
+ // Write per-channel mean-square into ring buffers
445
+ for (let ch = 0; ch < numCh; ch++) {
446
+ const meanSq = this._lufsBlockAccum[ch] / this._blockSize100ms;
447
+ this._momentaryBuffer[ch][this._momentaryWriteIdx] = meanSq;
448
+ this._shortTermBuffer[ch][this._shortTermWriteIdx] = meanSq;
449
+ }
450
+ this._momentaryWriteIdx = (this._momentaryWriteIdx + 1) % this._momentaryLen;
451
+ this._shortTermWriteIdx = (this._shortTermWriteIdx + 1) % this._shortTermLen;
452
+ if (this._momentaryFilled < this._momentaryLen)
453
+ this._momentaryFilled++;
454
+ if (this._shortTermFilled < this._shortTermLen)
455
+ this._shortTermFilled++;
456
+ // Compute this block's loudness for gating
457
+ let blockPower = 0;
458
+ for (let ch = 0; ch < numCh; ch++) {
459
+ const w = this._channelWeight(ch);
460
+ blockPower += w * (this._lufsBlockAccum[ch] / this._blockSize100ms);
461
+ }
462
+ const blockLoudness = blockPower > 0
463
+ ? -0.691 + 10 * Math.log10(blockPower)
464
+ : -Infinity;
465
+ this._gatingBlocks.push(blockLoudness);
466
+ // Reset accumulators
467
+ this._lufsBlockAccum.fill(0);
468
+ this._lufsBlockSampleCount = 0;
469
+ }
470
+ /**
471
+ * ITU-R BS.1770 channel weighting.
472
+ * Front channels = 1.0, surround channels (index >= 3) = 1.41 (~+1.5 dB).
473
+ * For stereo (2 channels) everything is 1.0.
474
+ */
475
+ _channelWeight(ch) {
476
+ if (this._channelCount <= 3)
477
+ return 1.0;
478
+ // For 5.1 layout: 0=L 1=R 2=C 3=Ls 4=Rs (5=LFE excluded)
479
+ return ch >= 3 ? 1.41 : 1.0;
480
+ }
481
+ // ── True Peak (ITU-R BS.1770, 4x oversampling) ─────────────────────
482
+ _processTruePeak(samples, channel) {
483
+ const history = this._tpHistory[channel];
484
+ let maxPeak = 0;
485
+ for (let i = 0; i < samples.length; i++) {
486
+ // Shift history buffer
487
+ for (let t = TP_FIR_LEN - 1; t > 0; t--) {
488
+ history[t] = history[t - 1];
489
+ }
490
+ history[0] = samples[i];
491
+ // Evaluate 4 polyphase sub-filters
492
+ for (let phase = 0; phase < 4; phase++) {
493
+ const taps = TP_FIR_PHASES[phase];
494
+ let sum = 0;
495
+ for (let t = 0; t < TP_FIR_LEN; t++) {
496
+ sum += history[t] * taps[t];
497
+ }
498
+ const abs = Math.abs(sum);
499
+ if (abs > maxPeak)
500
+ maxPeak = abs;
501
+ }
502
+ }
503
+ if (maxPeak > this._truePeakMax) {
504
+ this._truePeakMax = maxPeak;
505
+ }
506
+ }
507
+ // ── Readings ────────────────────────────────────────────────────────
508
+ getReading(channel) {
509
+ if (channel < 0 || channel >= this._channelCount) {
510
+ return { peak: NEG_INF, rms: NEG_INF, peakHold: NEG_INF, overCount: 0 };
511
+ }
512
+ return {
513
+ peak: this._peaks[channel],
514
+ rms: this._rms[channel],
515
+ peakHold: this._peakHold[channel],
516
+ overCount: this._overCount[channel],
517
+ };
518
+ }
519
+ getLUFSReading() {
520
+ const momentary = this._computeLufsWindow(this._momentaryBuffer, this._momentaryLen, this._momentaryFilled, this._momentaryWriteIdx);
521
+ const shortTerm = this._computeLufsWindow(this._shortTermBuffer, this._shortTermLen, this._shortTermFilled, this._shortTermWriteIdx);
522
+ const integrated = this._computeIntegratedLoudness();
523
+ const range = this._computeLoudnessRange();
524
+ const truePeak = this._truePeakMax > 0
525
+ ? LOG10_20 * Math.log(this._truePeakMax)
526
+ : NEG_INF;
527
+ return { momentary, shortTerm, integrated, range, truePeak };
528
+ }
529
+ getAllChannelReadings() {
530
+ const readings = [];
531
+ for (let ch = 0; ch < this._channelCount; ch++) {
532
+ readings.push(this.getReading(ch));
533
+ }
534
+ return readings;
535
+ }
536
+ // ── LUFS computation helpers ────────────────────────────────────────
537
+ /**
538
+ * Compute the loudness of a sliding window from ring-buffer data.
539
+ */
540
+ _computeLufsWindow(buffers, windowLen, filled, writeIdx) {
541
+ if (filled === 0)
542
+ return NEG_INF;
543
+ const numBlocks = Math.min(filled, windowLen);
544
+ let totalPower = 0;
545
+ for (let ch = 0; ch < this._channelCount; ch++) {
546
+ const w = this._channelWeight(ch);
547
+ const buf = buffers[ch];
548
+ let chSum = 0;
549
+ for (let b = 0; b < numBlocks; b++) {
550
+ const idx = (writeIdx - 1 - b + windowLen) % windowLen;
551
+ chSum += buf[idx];
552
+ }
553
+ totalPower += w * (chSum / numBlocks);
554
+ }
555
+ return totalPower > 0
556
+ ? -0.691 + 10 * Math.log10(totalPower)
557
+ : NEG_INF;
558
+ }
559
+ /**
560
+ * ITU-R BS.1770-4 gated integrated loudness.
561
+ * 1. Absolute gate at -70 LUFS
562
+ * 2. Relative gate at -10 LU below the absolute-gated mean
563
+ */
564
+ _computeIntegratedLoudness() {
565
+ const blocks = this._gatingBlocks;
566
+ if (blocks.length === 0)
567
+ return NEG_INF;
568
+ // Step 1: Absolute gate (-70 LUFS)
569
+ const absThreshold = -70;
570
+ let absGatedSum = 0;
571
+ let absGatedCount = 0;
572
+ for (let i = 0; i < blocks.length; i++) {
573
+ if (blocks[i] > absThreshold) {
574
+ absGatedSum += Math.pow(10, blocks[i] / 10);
575
+ absGatedCount++;
576
+ }
577
+ }
578
+ if (absGatedCount === 0)
579
+ return NEG_INF;
580
+ const absGatedMean = -0.691 + 10 * Math.log10(absGatedSum / absGatedCount);
581
+ // Step 2: Relative gate (-10 LU below absolute-gated mean)
582
+ const relThreshold = absGatedMean - 10;
583
+ let relGatedSum = 0;
584
+ let relGatedCount = 0;
585
+ for (let i = 0; i < blocks.length; i++) {
586
+ if (blocks[i] > relThreshold) {
587
+ relGatedSum += Math.pow(10, blocks[i] / 10);
588
+ relGatedCount++;
589
+ }
590
+ }
591
+ if (relGatedCount === 0)
592
+ return NEG_INF;
593
+ return -0.691 + 10 * Math.log10(relGatedSum / relGatedCount);
594
+ }
595
+ /**
596
+ * EBU R128 Loudness Range (LRA).
597
+ * Computed as the difference between the 95th and 10th percentiles
598
+ * of the short-term loudness distribution (after gating).
599
+ */
600
+ _computeLoudnessRange() {
601
+ if (this._gatingBlocks.length < 2)
602
+ return 0;
603
+ // We need short-term loudness values (3s window).
604
+ // Approximate by using overlapping 30-block windows over the gating blocks.
605
+ const stBlocks = [];
606
+ const stLen = this._shortTermLen; // 30 blocks = 3s
607
+ for (let i = stLen - 1; i < this._gatingBlocks.length; i++) {
608
+ let sum = 0;
609
+ let count = 0;
610
+ for (let j = i - stLen + 1; j <= i; j++) {
611
+ sum += Math.pow(10, this._gatingBlocks[j] / 10);
612
+ count++;
613
+ }
614
+ const loudness = count > 0
615
+ ? -0.691 + 10 * Math.log10(sum / count)
616
+ : NEG_INF;
617
+ stBlocks.push(loudness);
618
+ }
619
+ if (stBlocks.length < 2)
620
+ return 0;
621
+ // Absolute gate at -70 LUFS
622
+ const absGated = stBlocks.filter(l => l > -70);
623
+ if (absGated.length < 2)
624
+ return 0;
625
+ // Relative gate: mean of absolute-gated, then -20 LU
626
+ let absSum = 0;
627
+ for (const l of absGated)
628
+ absSum += Math.pow(10, l / 10);
629
+ const absMean = -0.691 + 10 * Math.log10(absSum / absGated.length);
630
+ const relThreshold = absMean - 20;
631
+ const relGated = absGated.filter(l => l > relThreshold);
632
+ if (relGated.length < 2)
633
+ return 0;
634
+ relGated.sort((a, b) => a - b);
635
+ // 10th and 95th percentile
636
+ const lo = relGated[Math.floor(relGated.length * 0.10)];
637
+ const hi = relGated[Math.floor(relGated.length * 0.95)];
638
+ return hi - lo;
639
+ }
640
+ // ── Reset ───────────────────────────────────────────────────────────
641
+ reset() {
642
+ this._peaks.fill(0);
643
+ this._rms.fill(0);
644
+ this._peakHold.fill(NEG_INF);
645
+ this._peakHoldCounter.fill(0);
646
+ this._overCount.fill(0);
647
+ this._iecIntegrator.fill(0);
648
+ this._vuIntegrator.fill(0);
649
+ this._truePeakMax = 0;
650
+ for (let ch = 0; ch < this._channelCount; ch++) {
651
+ this._kShelfStates[ch] = makeBiquadState();
652
+ this._kHPStates[ch] = makeBiquadState();
653
+ this._momentaryBuffer[ch].fill(0);
654
+ this._shortTermBuffer[ch].fill(0);
655
+ this._lufsBlockAccum[ch] = 0;
656
+ this._tpHistory[ch].fill(0);
657
+ }
658
+ this._lufsBlockSampleCount = 0;
659
+ this._momentaryWriteIdx = 0;
660
+ this._shortTermWriteIdx = 0;
661
+ this._momentaryFilled = 0;
662
+ this._shortTermFilled = 0;
663
+ this._integratedSum = 0;
664
+ this._integratedCount = 0;
665
+ this._gatingBlocks = [];
666
+ }
667
+ resetPeakHold() {
668
+ this._peakHold.fill(NEG_INF);
669
+ this._peakHoldCounter.fill(0);
670
+ }
671
+ // ── Configuration ───────────────────────────────────────────────────
672
+ setType(type) {
673
+ if (this._type !== type) {
674
+ this._type = type;
675
+ this.reset();
676
+ }
677
+ }
678
+ setPeakHoldTime(seconds) {
679
+ this._peakHoldTime = Math.round(this._sampleRate * Math.max(0, seconds));
680
+ }
681
+ setPeakHoldDecay(dbPerSecond) {
682
+ this._peakHoldDecay = Math.max(0, dbPerSecond);
683
+ }
684
+ // ── K-meter helpers ─────────────────────────────────────────────────
685
+ /**
686
+ * Returns the reference level offset (in dB) for the current K-type.
687
+ * K-12 => -12, K-14 => -14, K-20 => -20.
688
+ * Non-K types return 0.
689
+ */
690
+ getKReference() {
691
+ switch (this._type) {
692
+ case MeterType.K_12: return -12;
693
+ case MeterType.K_14: return -14;
694
+ case MeterType.K_20: return -20;
695
+ default: return 0;
696
+ }
697
+ }
698
+ // ── Static Utilities ────────────────────────────────────────────────
699
+ static linearToDb(linear) {
700
+ return linear > 0 ? LOG10_20 * Math.log(linear) : NEG_INF;
701
+ }
702
+ static dbToLinear(db) {
703
+ if (db === NEG_INF)
704
+ return 0;
705
+ return Math.exp(db / LOG10_20);
706
+ }
707
+ /**
708
+ * IEC 60268-10 scale mapping: converts a dB value to a 0-1 range
709
+ * suitable for drawing meter graphics.
710
+ *
711
+ * The piecewise curve is defined by the standard breakpoints:
712
+ * -70 dB = 0.0
713
+ * -60 dB = 0.05
714
+ * -50 dB = 0.075
715
+ * -40 dB = 0.15
716
+ * -30 dB = 0.3
717
+ * -20 dB = 0.5
718
+ * -10 dB = 0.75
719
+ * -5 dB = 0.875
720
+ * 0 dB = 1.0
721
+ *
722
+ * The meterType parameter is accepted for future per-type customisation
723
+ * but currently all IEC types share the same display curve.
724
+ */
725
+ static iecScale(dbValue, _meterType) {
726
+ if (dbValue < -70.0)
727
+ return 0.0;
728
+ if (dbValue > 0.0)
729
+ return 1.0;
730
+ // Breakpoints: [dB, displayValue]
731
+ const bp = [
732
+ [-70, 0.0],
733
+ [-60, 0.05],
734
+ [-50, 0.075],
735
+ [-40, 0.15],
736
+ [-30, 0.3],
737
+ [-20, 0.5],
738
+ [-10, 0.75],
739
+ [-5, 0.875],
740
+ [0, 1.0],
741
+ ];
742
+ // Find the two surrounding breakpoints and linearly interpolate
743
+ for (let i = 1; i < bp.length; i++) {
744
+ if (dbValue <= bp[i][0]) {
745
+ const [db0, v0] = bp[i - 1];
746
+ const [db1, v1] = bp[i];
747
+ const t = (dbValue - db0) / (db1 - db0);
748
+ return v0 + t * (v1 - v0);
749
+ }
750
+ }
751
+ return 1.0;
752
+ }
753
+ }
754
+ //# sourceMappingURL=MeterDSP.js.map