cacophony 0.30.4 → 0.32.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 (451) hide show
  1. package/README.md +158 -13
  2. package/THIRD_PARTY_NOTICES.md +15 -0
  3. package/dist/_virtual/_rolldown/runtime.cjs +1 -0
  4. package/dist/assets/sh_hrir_order_1.wav_url.cjs +2 -0
  5. package/dist/assets/sh_hrir_order_1.wav_url.cjs.map +1 -0
  6. package/dist/assets/sh_hrir_order_1.wav_url.mjs +6 -0
  7. package/dist/assets/sh_hrir_order_1.wav_url.mjs.map +1 -0
  8. package/dist/audioSprite.cjs +2 -0
  9. package/dist/audioSprite.cjs.map +1 -0
  10. package/dist/audioSprite.d.ts +29 -0
  11. package/dist/audioSprite.mjs +26 -0
  12. package/dist/audioSprite.mjs.map +1 -0
  13. package/dist/autoplayUnlock.cjs +2 -0
  14. package/dist/autoplayUnlock.cjs.map +1 -0
  15. package/dist/autoplayUnlock.mjs +58 -0
  16. package/dist/autoplayUnlock.mjs.map +1 -0
  17. package/dist/basePlayback.cjs +2 -0
  18. package/dist/basePlayback.cjs.map +1 -0
  19. package/dist/basePlayback.d.ts +14 -8
  20. package/dist/basePlayback.mjs +164 -0
  21. package/dist/basePlayback.mjs.map +1 -0
  22. package/dist/bundles/barberpole-bundle.js_url.cjs +2 -0
  23. package/dist/bundles/barberpole-bundle.js_url.cjs.map +1 -0
  24. package/dist/bundles/barberpole-bundle.js_url.mjs +6 -0
  25. package/dist/bundles/barberpole-bundle.js_url.mjs.map +1 -0
  26. package/dist/bundles/bcc-encoder-bundle.js_url.cjs +2 -0
  27. package/dist/bundles/bcc-encoder-bundle.js_url.cjs.map +1 -0
  28. package/dist/bundles/bcc-encoder-bundle.js_url.mjs +6 -0
  29. package/dist/bundles/bcc-encoder-bundle.js_url.mjs.map +1 -0
  30. package/dist/bundles/dattorro-reverb-bundle.js_url.cjs +2 -0
  31. package/dist/bundles/dattorro-reverb-bundle.js_url.cjs.map +1 -0
  32. package/dist/bundles/dattorro-reverb-bundle.js_url.mjs +6 -0
  33. package/dist/bundles/dattorro-reverb-bundle.js_url.mjs.map +1 -0
  34. package/dist/bundles/dynamics-bundle.js_url.cjs +2 -0
  35. package/dist/bundles/dynamics-bundle.js_url.cjs.map +1 -0
  36. package/dist/bundles/dynamics-bundle.js_url.mjs +6 -0
  37. package/dist/bundles/dynamics-bundle.js_url.mjs.map +1 -0
  38. package/dist/bundles/fdn-reverb-bundle.js_url.cjs +2 -0
  39. package/dist/bundles/fdn-reverb-bundle.js_url.cjs.map +1 -0
  40. package/dist/bundles/fdn-reverb-bundle.js_url.mjs +6 -0
  41. package/dist/bundles/fdn-reverb-bundle.js_url.mjs.map +1 -0
  42. package/dist/bundles/frequency-shifter-bundle.js_url.cjs +2 -0
  43. package/dist/bundles/frequency-shifter-bundle.js_url.cjs.map +1 -0
  44. package/dist/bundles/frequency-shifter-bundle.js_url.mjs +6 -0
  45. package/dist/bundles/frequency-shifter-bundle.js_url.mjs.map +1 -0
  46. package/dist/bundles/harmonizer-bundle.js_url.cjs +2 -0
  47. package/dist/bundles/harmonizer-bundle.js_url.cjs.map +1 -0
  48. package/dist/bundles/harmonizer-bundle.js_url.mjs +6 -0
  49. package/dist/bundles/harmonizer-bundle.js_url.mjs.map +1 -0
  50. package/dist/bundles/loudness-meter-bundle.js_url.cjs +2 -0
  51. package/dist/bundles/loudness-meter-bundle.js_url.cjs.map +1 -0
  52. package/dist/bundles/loudness-meter-bundle.js_url.mjs +6 -0
  53. package/dist/bundles/loudness-meter-bundle.js_url.mjs.map +1 -0
  54. package/dist/bundles/modulated-delay-bundle.js_url.cjs +2 -0
  55. package/dist/bundles/modulated-delay-bundle.js_url.cjs.map +1 -0
  56. package/dist/bundles/modulated-delay-bundle.js_url.mjs +6 -0
  57. package/dist/bundles/modulated-delay-bundle.js_url.mjs.map +1 -0
  58. package/dist/bundles/pcm-stream-bundle.js_url.cjs +2 -0
  59. package/dist/bundles/pcm-stream-bundle.js_url.cjs.map +1 -0
  60. package/dist/bundles/pcm-stream-bundle.js_url.mjs +6 -0
  61. package/dist/bundles/pcm-stream-bundle.js_url.mjs.map +1 -0
  62. package/dist/bundles/phase-vocoder-bundle.js_url.cjs +2 -0
  63. package/dist/bundles/phase-vocoder-bundle.js_url.cjs.map +1 -0
  64. package/dist/bundles/phase-vocoder-bundle.js_url.mjs +6 -0
  65. package/dist/bundles/phase-vocoder-bundle.js_url.mjs.map +1 -0
  66. package/dist/bundles/phaser-bundle.js_url.cjs +2 -0
  67. package/dist/bundles/phaser-bundle.js_url.cjs.map +1 -0
  68. package/dist/bundles/phaser-bundle.js_url.mjs +6 -0
  69. package/dist/bundles/phaser-bundle.js_url.mjs.map +1 -0
  70. package/dist/bundles/spectral-freeze-bundle.js_url.cjs +2 -0
  71. package/dist/bundles/spectral-freeze-bundle.js_url.cjs.map +1 -0
  72. package/dist/bundles/spectral-freeze-bundle.js_url.mjs +6 -0
  73. package/dist/bundles/spectral-freeze-bundle.js_url.mjs.map +1 -0
  74. package/dist/bundles/stereo-to-bformat-bundle.js_url.cjs +2 -0
  75. package/dist/bundles/stereo-to-bformat-bundle.js_url.cjs.map +1 -0
  76. package/dist/bundles/stereo-to-bformat-bundle.js_url.mjs +6 -0
  77. package/dist/bundles/stereo-to-bformat-bundle.js_url.mjs.map +1 -0
  78. package/dist/bundles/stereo-widener-bundle.js_url.cjs +2 -0
  79. package/dist/bundles/stereo-widener-bundle.js_url.cjs.map +1 -0
  80. package/dist/bundles/stereo-widener-bundle.js_url.mjs +6 -0
  81. package/dist/bundles/stereo-widener-bundle.js_url.mjs.map +1 -0
  82. package/dist/bundles/tremolo-bundle.js_url.cjs +2 -0
  83. package/dist/bundles/tremolo-bundle.js_url.cjs.map +1 -0
  84. package/dist/bundles/tremolo-bundle.js_url.mjs +6 -0
  85. package/dist/bundles/tremolo-bundle.js_url.mjs.map +1 -0
  86. package/dist/bundles/waveshaper-bundle.js_url.cjs +2 -0
  87. package/dist/bundles/waveshaper-bundle.js_url.cjs.map +1 -0
  88. package/dist/bundles/waveshaper-bundle.js_url.mjs +6 -0
  89. package/dist/bundles/waveshaper-bundle.js_url.mjs.map +1 -0
  90. package/dist/bus.cjs +2 -0
  91. package/dist/bus.cjs.map +1 -0
  92. package/dist/bus.mjs +156 -0
  93. package/dist/bus.mjs.map +1 -0
  94. package/dist/cache.cjs +2 -0
  95. package/dist/cache.cjs.map +1 -0
  96. package/dist/cache.d.ts +20 -106
  97. package/dist/cache.mjs +426 -0
  98. package/dist/cache.mjs.map +1 -0
  99. package/dist/cacophony.cjs +2 -0
  100. package/dist/cacophony.cjs.map +1 -0
  101. package/dist/cacophony.d.ts +30 -6
  102. package/dist/cacophony.mjs +791 -0
  103. package/dist/cacophony.mjs.map +1 -0
  104. package/dist/cli/audio-sink.cjs +2 -0
  105. package/dist/cli/audio-sink.cjs.map +1 -0
  106. package/dist/cli/audio-sink.mjs +8 -0
  107. package/dist/cli/audio-sink.mjs.map +1 -0
  108. package/dist/cli/commands.cjs +2 -0
  109. package/dist/cli/commands.cjs.map +1 -0
  110. package/dist/cli/commands.mjs +109 -0
  111. package/dist/cli/commands.mjs.map +1 -0
  112. package/dist/cli/effects-registry.cjs +2 -0
  113. package/dist/cli/effects-registry.cjs.map +1 -0
  114. package/dist/cli/effects-registry.mjs +221 -0
  115. package/dist/cli/effects-registry.mjs.map +1 -0
  116. package/dist/cli/index.cjs +8 -41
  117. package/dist/cli/index.cjs.map +1 -1
  118. package/dist/cli/index.mjs +98 -1053
  119. package/dist/cli/index.mjs.map +1 -1
  120. package/dist/cli/live.cjs +2 -0
  121. package/dist/cli/live.cjs.map +1 -0
  122. package/dist/cli/live.mjs +52 -0
  123. package/dist/cli/live.mjs.map +1 -0
  124. package/dist/cli/logging.cjs +2 -0
  125. package/dist/cli/logging.cjs.map +1 -0
  126. package/dist/cli/logging.mjs +15 -0
  127. package/dist/cli/logging.mjs.map +1 -0
  128. package/dist/cli/meter.cjs +2 -0
  129. package/dist/cli/meter.cjs.map +1 -0
  130. package/dist/cli/meter.mjs +44 -0
  131. package/dist/cli/meter.mjs.map +1 -0
  132. package/dist/cli/render.cjs +2 -0
  133. package/dist/cli/render.cjs.map +1 -0
  134. package/dist/cli/render.mjs +92 -0
  135. package/dist/cli/render.mjs.map +1 -0
  136. package/dist/cli/repl.cjs +41 -0
  137. package/dist/cli/repl.cjs.map +1 -0
  138. package/dist/cli/repl.mjs +317 -0
  139. package/dist/cli/repl.mjs.map +1 -0
  140. package/dist/cli/replay.cjs +2 -0
  141. package/dist/cli/replay.cjs.map +1 -0
  142. package/dist/cli/replay.mjs +86 -0
  143. package/dist/cli/replay.mjs.map +1 -0
  144. package/dist/cli/session.cjs +2 -0
  145. package/dist/cli/session.cjs.map +1 -0
  146. package/dist/cli/session.mjs +54 -0
  147. package/dist/cli/session.mjs.map +1 -0
  148. package/dist/cli/sprite.cjs +2 -0
  149. package/dist/cli/sprite.cjs.map +1 -0
  150. package/dist/cli/sprite.d.ts +21 -0
  151. package/dist/cli/sprite.mjs +68 -0
  152. package/dist/cli/sprite.mjs.map +1 -0
  153. package/dist/cli/wav.cjs +2 -0
  154. package/dist/cli/wav.cjs.map +1 -0
  155. package/dist/cli/wav.mjs +25 -0
  156. package/dist/cli/wav.mjs.map +1 -0
  157. package/dist/container.cjs +2 -0
  158. package/dist/container.cjs.map +1 -0
  159. package/dist/container.d.ts +2 -2
  160. package/dist/container.mjs +121 -0
  161. package/dist/container.mjs.map +1 -0
  162. package/dist/effectChain.cjs +2 -0
  163. package/dist/effectChain.cjs.map +1 -0
  164. package/dist/effectChain.mjs +151 -0
  165. package/dist/effectChain.mjs.map +1 -0
  166. package/dist/effects.cjs +2 -0
  167. package/dist/effects.cjs.map +1 -0
  168. package/dist/effects.mjs +249 -0
  169. package/dist/effects.mjs.map +1 -0
  170. package/dist/eventEmitter.cjs +2 -0
  171. package/dist/eventEmitter.cjs.map +1 -0
  172. package/dist/eventEmitter.mjs +42 -0
  173. package/dist/eventEmitter.mjs.map +1 -0
  174. package/dist/filters.cjs +2 -0
  175. package/dist/filters.cjs.map +1 -0
  176. package/dist/filters.mjs +25 -0
  177. package/dist/filters.mjs.map +1 -0
  178. package/dist/group.cjs +2 -0
  179. package/dist/group.cjs.map +1 -0
  180. package/dist/group.mjs +111 -0
  181. package/dist/group.mjs.map +1 -0
  182. package/dist/hlsAdapter.cjs +2 -0
  183. package/dist/hlsAdapter.cjs.map +1 -0
  184. package/dist/hlsAdapter.mjs +47 -0
  185. package/dist/hlsAdapter.mjs.map +1 -0
  186. package/dist/index.cjs +1 -1
  187. package/dist/index.d.ts +5 -1
  188. package/dist/index.mjs +21 -3
  189. package/dist/logger.cjs +2 -0
  190. package/dist/logger.cjs.map +1 -0
  191. package/dist/logger.mjs +14 -0
  192. package/dist/logger.mjs.map +1 -0
  193. package/dist/mediaStream.cjs +2 -0
  194. package/dist/mediaStream.cjs.map +1 -0
  195. package/dist/mediaStream.d.ts +4 -2
  196. package/dist/mediaStream.mjs +131 -0
  197. package/dist/mediaStream.mjs.map +1 -0
  198. package/dist/meters/loudness-core.cjs +2 -0
  199. package/dist/meters/loudness-core.cjs.map +1 -0
  200. package/dist/meters/loudness-core.mjs +177 -0
  201. package/dist/meters/loudness-core.mjs.map +1 -0
  202. package/dist/meters/loudness-meter.cjs +2 -0
  203. package/dist/meters/loudness-meter.cjs.map +1 -0
  204. package/dist/meters/loudness-meter.mjs +63 -0
  205. package/dist/meters/loudness-meter.mjs.map +1 -0
  206. package/dist/meters/truepeak-core.cjs +2 -0
  207. package/dist/meters/truepeak-core.cjs.map +1 -0
  208. package/dist/meters/truepeak-core.mjs +125 -0
  209. package/dist/meters/truepeak-core.mjs.map +1 -0
  210. package/dist/microphone.cjs +2 -0
  211. package/dist/microphone.cjs.map +1 -0
  212. package/dist/microphone.mjs +20 -0
  213. package/dist/microphone.mjs.map +1 -0
  214. package/dist/node.cjs +2 -2
  215. package/dist/node.cjs.map +1 -1
  216. package/dist/node.mjs +1 -1
  217. package/dist/oscillatorMixin.cjs +2 -0
  218. package/dist/oscillatorMixin.cjs.map +1 -0
  219. package/dist/oscillatorMixin.mjs +39 -0
  220. package/dist/oscillatorMixin.mjs.map +1 -0
  221. package/dist/pannerMixin.cjs +2 -0
  222. package/dist/pannerMixin.cjs.map +1 -0
  223. package/dist/pannerMixin.mjs +81 -0
  224. package/dist/pannerMixin.mjs.map +1 -0
  225. package/dist/pcmStream.cjs +2 -0
  226. package/dist/pcmStream.cjs.map +1 -0
  227. package/dist/pcmStream.d.ts +4 -3
  228. package/dist/pcmStream.mjs +180 -0
  229. package/dist/pcmStream.mjs.map +1 -0
  230. package/dist/playOptions.cjs +2 -0
  231. package/dist/playOptions.cjs.map +1 -0
  232. package/dist/playOptions.d.ts +9 -0
  233. package/dist/playOptions.mjs +56 -0
  234. package/dist/playOptions.mjs.map +1 -0
  235. package/dist/playback.cjs +2 -0
  236. package/dist/playback.cjs.map +1 -0
  237. package/dist/playback.d.ts +11 -4
  238. package/dist/playback.mjs +286 -0
  239. package/dist/playback.mjs.map +1 -0
  240. package/dist/processors/dynamics-core.cjs +2 -0
  241. package/dist/processors/dynamics-core.cjs.map +1 -0
  242. package/dist/processors/dynamics-core.mjs +6 -0
  243. package/dist/processors/dynamics-core.mjs.map +1 -0
  244. package/dist/processors/modulated-delay-core.cjs +2 -0
  245. package/dist/processors/modulated-delay-core.cjs.map +1 -0
  246. package/dist/processors/modulated-delay-core.mjs +6 -0
  247. package/dist/processors/modulated-delay-core.mjs.map +1 -0
  248. package/dist/processors/timestretch-core.cjs +2 -0
  249. package/dist/processors/timestretch-core.cjs.map +1 -0
  250. package/dist/processors/timestretch-core.mjs +145 -0
  251. package/dist/processors/timestretch-core.mjs.map +1 -0
  252. package/dist/routableSource.cjs +2 -0
  253. package/dist/routableSource.cjs.map +1 -0
  254. package/dist/routableSource.mjs +124 -0
  255. package/dist/routableSource.mjs.map +1 -0
  256. package/dist/scheduler.cjs +2 -0
  257. package/dist/scheduler.cjs.map +1 -0
  258. package/dist/scheduler.d.ts +21 -0
  259. package/dist/scheduler.mjs +45 -0
  260. package/dist/scheduler.mjs.map +1 -0
  261. package/dist/sound.cjs +2 -0
  262. package/dist/sound.cjs.map +1 -0
  263. package/dist/sound.d.ts +19 -7
  264. package/dist/sound.mjs +221 -0
  265. package/dist/sound.mjs.map +1 -0
  266. package/dist/spatial/foa-encode.cjs +2 -0
  267. package/dist/spatial/foa-encode.cjs.map +1 -0
  268. package/dist/spatial/foa-encode.mjs +14 -0
  269. package/dist/spatial/foa-encode.mjs.map +1 -0
  270. package/dist/synth.cjs +2 -0
  271. package/dist/synth.cjs.map +1 -0
  272. package/dist/synth.d.ts +2 -2
  273. package/dist/synth.mjs +101 -0
  274. package/dist/synth.mjs.map +1 -0
  275. package/dist/synthGroup.cjs +2 -0
  276. package/dist/synthGroup.cjs.map +1 -0
  277. package/dist/synthGroup.mjs +92 -0
  278. package/dist/synthGroup.mjs.map +1 -0
  279. package/dist/synthPlayback.cjs +2 -0
  280. package/dist/synthPlayback.cjs.map +1 -0
  281. package/dist/synthPlayback.d.ts +2 -2
  282. package/dist/synthPlayback.mjs +56 -0
  283. package/dist/synthPlayback.mjs.map +1 -0
  284. package/dist/volumeMixin.cjs +2 -0
  285. package/dist/volumeMixin.cjs.map +1 -0
  286. package/dist/volumeMixin.d.ts +7 -2
  287. package/dist/volumeMixin.mjs +67 -0
  288. package/dist/volumeMixin.mjs.map +1 -0
  289. package/dist/webCodecsStream.cjs +2 -0
  290. package/dist/webCodecsStream.cjs.map +1 -0
  291. package/dist/webCodecsStream.mjs +220 -0
  292. package/dist/webCodecsStream.mjs.map +1 -0
  293. package/dist/worklets.cjs +2 -0
  294. package/dist/worklets.cjs.map +1 -0
  295. package/dist/worklets.d.ts +24 -18
  296. package/dist/worklets.mjs +75 -0
  297. package/dist/worklets.mjs.map +1 -0
  298. package/docs/assets/hierarchy.js +1 -1
  299. package/docs/assets/highlight.css +16 -2
  300. package/docs/assets/navigation.js +1 -1
  301. package/docs/assets/search.js +1 -1
  302. package/docs/classes/AudioCache.html +20 -51
  303. package/docs/classes/AudioSprite.html +143 -0
  304. package/docs/classes/BarberpoleEffect.html +7 -7
  305. package/docs/classes/BiquadEffect.html +3 -3
  306. package/docs/classes/Bus.html +21 -21
  307. package/docs/classes/Cacophony.html +112 -89
  308. package/docs/classes/DynamicsEffect.html +7 -7
  309. package/docs/classes/FdnReverbEffect.html +7 -7
  310. package/docs/classes/FoaDecoder.html +5 -5
  311. package/docs/classes/FoaDecoderEffect.html +3 -3
  312. package/docs/classes/FrequencyShifterEffect.html +7 -7
  313. package/docs/classes/Group.html +32 -32
  314. package/docs/classes/HarmonizerEffect.html +7 -7
  315. package/docs/classes/ImpulseResponseEffect.html +3 -3
  316. package/docs/classes/KWeightingFilter.html +4 -4
  317. package/docs/classes/LoudnessMeter.html +11 -11
  318. package/docs/classes/MediaStreamPlayback.html +100 -81
  319. package/docs/classes/MediaStreamSound.html +60 -56
  320. package/docs/classes/MicrophoneStream.html +55 -51
  321. package/docs/classes/ModulatedDelayEffect.html +7 -7
  322. package/docs/classes/PcmStreamPlayback.html +100 -81
  323. package/docs/classes/PcmStreamSound.html +63 -59
  324. package/docs/classes/PhaserEffect.html +7 -7
  325. package/docs/classes/Playback.html +124 -106
  326. package/docs/classes/ReverbEffect.html +7 -7
  327. package/docs/classes/Scheduler.html +91 -0
  328. package/docs/classes/ShareEffect.html +3 -3
  329. package/docs/classes/Sound.html +130 -68
  330. package/docs/classes/SpectralFreezeEffect.html +7 -7
  331. package/docs/classes/StereoWidenerEffect.html +3 -3
  332. package/docs/classes/Synth.html +65 -61
  333. package/docs/classes/SynthGroup.html +28 -28
  334. package/docs/classes/TremoloEffect.html +7 -7
  335. package/docs/classes/TruePeakDetector.html +8 -8
  336. package/docs/classes/WaveshaperEffect.html +7 -7
  337. package/docs/functions/encodeMonoToFoaSN3D.html +1 -1
  338. package/docs/functions/integratedLoudness.html +1 -1
  339. package/docs/functions/integratedUngatedLoudness.html +1 -1
  340. package/docs/functions/loudnessRange.html +1 -1
  341. package/docs/functions/timeStretch.html +1 -1
  342. package/docs/functions/timeStretchChannels.html +1 -1
  343. package/docs/functions/truePeakDb.html +1 -1
  344. package/docs/hierarchy.html +29 -29
  345. package/docs/index.html +89 -6
  346. package/docs/interfaces/AudioBuffer.html +8 -8
  347. package/docs/interfaces/AudioBufferSourceNode.html +22 -22
  348. package/docs/interfaces/AudioEventCallbacks.html +2 -2
  349. package/docs/interfaces/AudioListener.html +12 -12
  350. package/docs/interfaces/AudioNode.html +14 -14
  351. package/docs/interfaces/AudioParam.html +6 -6
  352. package/docs/interfaces/AudioWorklet.html +2 -2
  353. package/docs/interfaces/AudioWorkletNode.html +16 -16
  354. package/docs/interfaces/BarberpoleOptions.html +5 -5
  355. package/docs/interfaces/BaseContext.html +25 -25
  356. package/docs/interfaces/BaseSound.html +22 -18
  357. package/docs/interfaces/BiquadCoefficients.html +6 -6
  358. package/docs/interfaces/BiquadFilterNode.html +20 -20
  359. package/docs/interfaces/BuiltEffectGraph.html +6 -6
  360. package/docs/interfaces/CacheErrorEvent.html +5 -5
  361. package/docs/interfaces/CacheHitEvent.html +4 -4
  362. package/docs/interfaces/CacheMissEvent.html +4 -4
  363. package/docs/interfaces/CacophonyEffect.html +2 -2
  364. package/docs/interfaces/CacophonyLogger.html +4 -4
  365. package/docs/interfaces/ChannelMergerNode.html +14 -14
  366. package/docs/interfaces/ChannelSplitterNode.html +14 -14
  367. package/docs/interfaces/ConvolverNode.html +16 -16
  368. package/docs/interfaces/CreateSpriteOptions.html +64 -0
  369. package/docs/interfaces/DynamicsOptions.html +7 -7
  370. package/docs/interfaces/FadeStartEvent.html +4 -4
  371. package/docs/interfaces/FdnReverbOptions.html +6 -6
  372. package/docs/interfaces/FoaDecoderOptions.html +2 -2
  373. package/docs/interfaces/FrequencyShifterOptions.html +3 -3
  374. package/docs/interfaces/GainNode.html +15 -15
  375. package/docs/interfaces/GlobalPlaybackEvent.html +3 -3
  376. package/docs/interfaces/HarmonizerOptions.html +6 -6
  377. package/docs/interfaces/ImpulseResponseOptions.html +5 -5
  378. package/docs/interfaces/LoadingCompleteEvent.html +5 -5
  379. package/docs/interfaces/LoadingErrorEvent.html +5 -5
  380. package/docs/interfaces/LoadingProgressEvent.html +6 -6
  381. package/docs/interfaces/LoadingStartEvent.html +3 -3
  382. package/docs/interfaces/LoudnessChannelInput.html +3 -3
  383. package/docs/interfaces/LoudnessReading.html +5 -5
  384. package/docs/interfaces/MediaElementSourceNode.html +15 -15
  385. package/docs/interfaces/MediaStreamAudioSourceNode.html +15 -15
  386. package/docs/interfaces/MediaStreamSoundOptions.html +5 -5
  387. package/docs/interfaces/MicrophoneStreamOptions.html +8 -8
  388. package/docs/interfaces/ModulatedDelayOptions.html +8 -8
  389. package/docs/interfaces/OfflineOptions.html +5 -5
  390. package/docs/interfaces/OscillatorNode.html +20 -20
  391. package/docs/interfaces/PannerNode.html +28 -28
  392. package/docs/interfaces/PcmStreamBufferEvent.html +2 -2
  393. package/docs/interfaces/PcmStreamSoundOptions.html +6 -6
  394. package/docs/interfaces/PhaserOptions.html +7 -7
  395. package/docs/interfaces/PlayOptions.html +70 -8
  396. package/docs/interfaces/PlaybackErrorEvent.html +7 -7
  397. package/docs/interfaces/ReverbOptions.html +13 -13
  398. package/docs/interfaces/RuntimeOptions.html +7 -7
  399. package/docs/interfaces/ScheduledCallbackHandle.html +59 -0
  400. package/docs/interfaces/SoundCleanupHoldings.html +4 -4
  401. package/docs/interfaces/SoundErrorEvent.html +6 -6
  402. package/docs/interfaces/SpectralFreezeOptions.html +4 -4
  403. package/docs/interfaces/SpriteRegion.html +70 -0
  404. package/docs/interfaces/StereoPannerNode.html +15 -15
  405. package/docs/interfaces/StereoToBFormatOptions.html +60 -0
  406. package/docs/interfaces/StereoWidenerOptions.html +4 -4
  407. package/docs/interfaces/StreamCapabilities.html +5 -5
  408. package/docs/interfaces/TimeStretchOptions.html +5 -5
  409. package/docs/interfaces/TremoloOptions.html +5 -5
  410. package/docs/interfaces/WaveshaperOptions.html +5 -5
  411. package/docs/modules.html +6 -6
  412. package/docs/types/BaseAudioEvents.html +11 -11
  413. package/docs/types/BuiltEffect.html +1 -1
  414. package/docs/types/BusConnectionTarget.html +1 -1
  415. package/docs/types/CacheEventCallback.html +1 -1
  416. package/docs/types/CacophonyEvents.html +17 -17
  417. package/docs/types/ErrorEventCallback.html +1 -1
  418. package/docs/types/FadeType.html +1 -1
  419. package/docs/types/HrtfPannerOptions.html +1 -1
  420. package/docs/types/ImpulseResponseSource.html +1 -1
  421. package/docs/types/LoadingEventCallback.html +1 -1
  422. package/docs/types/LoopCount.html +1 -1
  423. package/docs/types/LoudnessChannel.html +1 -1
  424. package/docs/types/Orientation.html +3 -3
  425. package/docs/types/PanCloneOverrides.html +5 -5
  426. package/docs/types/PanType.html +1 -1
  427. package/docs/types/PcmStreamEvents.html +6 -6
  428. package/docs/types/PcmStreamState.html +1 -1
  429. package/docs/types/PlaybackEvents.html +1 -1
  430. package/docs/types/Position.html +1 -1
  431. package/docs/types/SoundEvents.html +1 -1
  432. package/docs/types/SoundType.html +1 -1
  433. package/docs/types/SourceNode.html +1 -1
  434. package/docs/types/SpriteMap.html +35 -0
  435. package/docs/types/SynthEvents.html +1 -1
  436. package/docs/types/ThreeDOptions.html +1 -1
  437. package/docs/types/WebCodecsStreamSound.html +1 -1
  438. package/docs/variables/CHANNEL_WEIGHTS.html +1 -1
  439. package/docs/variables/K_WEIGHTING_STAGE1_48K.html +1 -1
  440. package/docs/variables/K_WEIGHTING_STAGE2_48K.html +1 -1
  441. package/docs/variables/consoleLogger.html +1 -1
  442. package/docs/variables/noopLogger.html +1 -1
  443. package/package.json +10 -3
  444. package/dist/cacophony-PShY6dPK.cjs +0 -2
  445. package/dist/cacophony-PShY6dPK.cjs.map +0 -1
  446. package/dist/cacophony-o1zE5oDP.js +0 -3857
  447. package/dist/cacophony-o1zE5oDP.js.map +0 -1
  448. package/dist/src-Bss6ztQN.cjs +0 -2
  449. package/dist/src-Bss6ztQN.cjs.map +0 -1
  450. package/dist/src-CCQXsJcG.js +0 -400
  451. package/dist/src-CCQXsJcG.js.map +0 -1
package/README.md CHANGED
@@ -19,10 +19,36 @@ Cacophony is a powerful and intuitive audio library designed for modern web appl
19
19
  ## Installation
20
20
 
21
21
  ```bash
22
- npm install cacophony
23
- ```
24
-
25
- ## Quick Start
22
+ npm install cacophony
23
+ ```
24
+
25
+ ## Bundling and lazy loading
26
+
27
+ Use named ESM imports so your bundler can discard unused modules:
28
+
29
+ ```typescript
30
+ import { AudioCache } from 'cacophony';
31
+ ```
32
+
33
+ The package preserves its module boundaries and declares import side effects.
34
+ Importing `AudioCache` or an individual effect does not pull in the full
35
+ `Cacophony` class or the offline FFT implementation. The `Cacophony` class keeps
36
+ its complete factory API, so importing that class still includes the code its
37
+ methods reference.
38
+
39
+ `createStream()` loads MediaBunny on demand when using the WebCodecs transport.
40
+ HLS streams and browsers without `AudioDecoder` take their existing fallback
41
+ paths without loading it. Each AudioWorklet payload loads when its processor is
42
+ first needed. Call `await audio.loadWorklets()` to preload all processors.
43
+
44
+ Consumer bundlers must support code splitting to keep these imports in separate
45
+ downloadable chunks. Deploy all generated chunks with the application. MediaBunny
46
+ remains an installed dependency; lazy loading defers its runtime load.
47
+
48
+ Run `npm run build` followed by `npm run check:bundles -- --report` to inspect
49
+ initial bundle sizes for cache-only, full-factory, and single-effect consumers.
50
+
51
+ ## Quick Start
26
52
 
27
53
  ```typescript
28
54
  import { Cacophony } from 'cacophony';
@@ -101,7 +127,30 @@ Cacophony uses a two-tier architecture that separates audio assets from their pl
101
127
  - **Sound**: Represents an audio asset (file or buffer). Acts as a container managing multiple playback instances.
102
128
  - **Playback**: Represents a single playback instance of a Sound. Each call to `play()` creates a new Playback.
103
129
 
104
- This design allows the same sound to be played multiple times simultaneously with different settings (volume, position, playback rate, etc.).
130
+ This design allows the same sound to be played multiple times simultaneously with different settings (volume, position, playback rate, etc.).
131
+
132
+ Pass invocation settings to `play(options)` to configure each voice before audio
133
+ starts. Omitted values inherit source defaults; `0` is valid for volume, stereo
134
+ pan, and loop count. Sound defaults and overlapping voices stay independent.
135
+
136
+ ```typescript
137
+ const [voice] = sound.play({ volume: 0.5, playbackRate: 1.2, loopCount: 0,
138
+ panType: 'stereo', stereoPan: 0 });
139
+ const step = group.playOrdered(true, { volume: 0.7, panType: 'HRTF',
140
+ position: [10, 0, 5], threeDOptions: { distanceModel: 'linear', rolloffFactor: 0.1 } });
141
+ ```
142
+
143
+ `stereoPan` requires the effective stereo mode; `position` and `threeDOptions`
144
+ require HRTF. Partial HRTF options preserve unspecified settings. Group all,
145
+ random, and ordered playback and sprite children accept these same options.
146
+ Invalid options throw before voice preparation, and group playback preflights
147
+ every member. Direct `Playback.play(options)` also supports these settings,
148
+ scheduling, and fades. Calling play on an already playing voice remains a no-op
149
+ after validation.
150
+
151
+ Synths and live streams support volume, spatial settings, and fade-in. They
152
+ reject playback rate, loop count, scheduled starts, end fades, and per-loop fade
153
+ settings because these sources do not support those controls.
105
154
 
106
155
  ```typescript
107
156
  const sound = await cacophony.createSound('laser.mp3');
@@ -124,10 +173,63 @@ setTimeout(() => playback1.pause(), 1000);
124
173
  setTimeout(() => playback2.stop(), 2000);
125
174
 
126
175
  // Or control all playbacks through the Sound
127
- sound.stop(); // Stops all three playbacks
128
- ```
129
-
130
- ## Sound Types
176
+ sound.stop(); // Stops all three playbacks
177
+ ```
178
+
179
+ Each playback exposes a read-only `state`: `"unplayed"`, `"playing"`, `"paused"`,
180
+ or `"stopped"`. When `sound.preplay()` or `sound.play()` prepares a new voice,
181
+ it removes stopped entries from `sound.playbacks`. Repeated play/stop calls
182
+ therefore retain at most the most recently stopped voice in that collection.
183
+ Unplayed, paused, and playing voices remain registered.
184
+
185
+ An individual `playback.stop()` preserves the ability to call `playback.play()`
186
+ again. A successfully restarted playback rejoins `sound.playbacks` and receives
187
+ Sound controls again, even if it was previously reaped. Reaping disconnects its
188
+ output and sends; replay restores the Sound's current routing and event forwarding.
189
+ The playback retains its individual volume, rate, and other settings while reaped.
190
+ Reaping does not call `cleanup()`; call `playback.cleanup()` when a retained playback
191
+ is no longer needed.
192
+
193
+ ### Audio Sprites for Games
194
+
195
+ Pack many short effects into one decoded WAV atlas while keeping each named
196
+ region as an ordinary, independently configurable `Sound`:
197
+
198
+ ```bash
199
+ cacophony sprite sfx/laser.wav sfx/engine.wav \
200
+ --out public/sfx-atlas.wav --map public/sfx-atlas.json --gap 10
201
+ ```
202
+
203
+ ```typescript
204
+ import { Cacophony, type SpriteMap } from 'cacophony';
205
+
206
+ const map = {
207
+ laser: { start: 0, duration: 0.35 },
208
+ engine: { start: 0.36, duration: 1.2, loopCount: 'infinite' },
209
+ } as const satisfies SpriteMap;
210
+
211
+ const cacophony = new Cacophony();
212
+ const sprite = await cacophony.createSprite('/sfx-atlas.wav', map);
213
+
214
+ // Literal keys stay typed. Each child uses the normal Sound API.
215
+ sprite.sounds.laser.volume = 0.7;
216
+ sprite.sounds.laser.play();
217
+
218
+ // Dynamic names use the checked lookup.
219
+ const selected: string = chooseEffectName();
220
+ if (sprite.has(selected)) sprite.get(selected).play();
221
+
222
+ // Child configuration is independent; the atlas has no shared play controls.
223
+ sprite.sounds.engine.loop('infinite');
224
+ sprite.sounds.engine.play();
225
+
226
+ sprite.cleanup();
227
+ ```
228
+
229
+ All child Sounds share the atlas `AudioBuffer`. `cleanup()` owns those children
230
+ but not clones created with `sound.clone()`, which remain independently usable.
231
+
232
+ ## Sound Types
131
233
 
132
234
  Cacophony supports three URL-backed sound labels:
133
235
 
@@ -551,6 +653,27 @@ FOA binaural decoding is available in both forms. Use
551
653
  `decoder.input` and `decoder.output`; use `createFoaDecoderEffect()` only on a
552
654
  dedicated 4-channel ACN/SN3D FOA bus, typically as the first and only filter
553
655
  that converts that bus to stereo binaural output.
656
+
657
+ For physically correct mono positioning, use `encodeMonoToFoaSN3D()` and feed
658
+ its ACN/SN3D `[W, Y, Z, X]` output to the decoder. Stereo recordings can instead
659
+ be perceptually upmixed with `createStereoToBFormatNode()`. The default
660
+ `algorithm: 'perceptual'` path preserves the existing three-band front cue;
661
+ `algorithm: 'bcc'` uses frequency-domain coherence and pan analysis:
662
+
663
+ ```typescript
664
+ const upmixer = await cacophony.createStereoToBFormatNode({ algorithm: 'bcc' });
665
+ const decoder = await cacophony.createFoaDecoder();
666
+ stereoSource.connect(upmixer);
667
+ upmixer.connect(decoder.input);
668
+ decoder.output.connect(cacophony.context.destination);
669
+
670
+ // BCC analysis is stateful. Reset it after a seek or other discontinuity.
671
+ upmixer.port.postMessage({ type: 'reset' });
672
+ ```
673
+
674
+ Both stereo upmix algorithms emit ACN `[W, Y, Z, X]` but are perceptual
675
+ approximations, neither N3D nor SN3D. The BCC path produces W/Y only and keeps
676
+ Z/X at zero, trading the default path's front cue for per-band spatial analysis.
554
677
 
555
678
  ### Cleaning up
556
679
 
@@ -1206,7 +1329,29 @@ Cacophony implements intelligent three-layer caching for optimal performance:
1206
1329
 
1207
1330
  **Memory Cache (LRU)** → **Browser Cache API** → **Network**
1208
1331
 
1209
- The cache system is fully automatic and HTTP-compliant, respecting standard cache headers (ETag, Last-Modified). When cache validation tokens are available, Cacophony makes lightweight conditional requests (304 responses have no body). When tokens are unavailable, it falls back to TTL-based caching (24 hours default).
1332
+ HTTP reuse decisions are made by `http-cache-semantics` for both decoded memory entries and persistent responses, using the response headers available to the runtime. Fresh responses can be reused; stale responses are conditionally validated when ETag or Last-Modified is available. `no-cache` requires validation, and `no-store` prevents retention in either layer. A successful matching 304 can reuse the decoded buffer while updating its policy.
1333
+
1334
+ If a 304 cannot be matched to the stored representation, Cacophony retries with an unconditional Fetch reload. This includes a 304 that omits the ETag of an ETag-bearing stored response. Compared with the previous cache implementation, such servers can incur an additional request; return matching validators on 304 responses to allow reuse.
1335
+
1336
+ A matching 304 emits `onCacheHit` with `cacheType: "conditional"`. When it reuses an already decoded buffer, it does not emit `onLoadingComplete`; when it decodes retained bytes, it emits both events. This differs from the previous implementation's completion event on every 304. Await `getAudioBuffer()` to observe completion regardless of the cache path.
1337
+
1338
+ The 24-hour default TTL applies only when the response supplies no Cache-Control, Expires, Pragma, or validators. Explicit expiry never falls back to that TTL. Custom TTL values use milliseconds and are rounded down to whole seconds. Persistence uses a versioned response containing both bytes and policy; older cache entries are ignored and fetched again. On first persistent-cache access, Cacophony attempts to delete its legacy `audio-cache` namespace. Cleanup failure is reported through `onCacheError` and does not prevent loading or use of the current cache.
1339
+
1340
+ `AudioCache.setCacheExpirationTime()` affects newly created response policies. Existing decoded and persisted entries keep the lifetime assigned when their policy was created, including across cache instances. Clearing decoded memory does not rewrite persistent policies. Unlike the previous implementation, negative or non-finite TTL values throw `RangeError` and leave the configured TTL unchanged; zero is valid and disables fallback freshness.
1341
+
1342
+ Compatibility change: a response with only `Cache-Control: public` or `private` and no expiry or validators no longer receives the fallback TTL. These directives permit storage but do not specify how long the response is fresh. Such responses can be stored, but subsequent loads go through Fetch again. Cacophony deliberately does not assign heuristic freshness to responses with explicit cache directives. To enable fresh reuse, serve an appropriate `max-age` (for example, `private, max-age=60`) or `Expires`; increasing the fallback TTL does not override those responses.
1343
+
1344
+ Cross-origin audio uses both decoded memory and persistent caching. CORS exposes Cache-Control, Expires, and Last-Modified without extra server configuration. For cross-origin entries requiring validation, Cacophony asks Fetch to revalidate through the browser HTTP cache, avoiding application-added conditional headers and their CORS preflight requirements. Browser HTTP caching remains enabled. A browser-validated response is decoded again because Fetch delivers the resulting representation rather than its internal 304 exchange.
1345
+
1346
+ In browsers, the guarantee is HTTP policy based on browser-visible headers. Cross-origin caching remains enabled when CORS hides headers. A hidden Age can overestimate remaining freshness, and a hidden Vary cannot participate in application-cache matching. Browser validation applies when Fetch runs; a fresh application-cache hit does not consult the browser HTTP cache. This URL-only API explicitly controls Accept; a visible Vary on other, host-managed headers prevents application-cache retention. Data-URL decoding and caching are unchanged.
1347
+
1348
+ For static audio, use versioned or content-hashed URLs such as `/audio/chime.a1b2c3.ogg` with a long `Cache-Control: max-age`. Publish changed audio at a new URL. For mutable audio requiring validation on every load, use `Cache-Control: no-cache`. Expose relevant response headers when freshness or variants depend on them:
1349
+
1350
+ ```http
1351
+ Access-Control-Expose-Headers: Age, Date, ETag, Vary
1352
+ ```
1353
+
1354
+ This header supplements the server's normal CORS access configuration. Exposing ETag does not cause Cacophony to add conditional request headers to cross-origin requests; Fetch still handles validation.
1210
1355
 
1211
1356
  ```typescript
1212
1357
  const cacophony = new Cacophony();
@@ -1214,7 +1359,7 @@ const cacophony = new Cacophony();
1214
1359
  // First load - fetches from network, stores in cache
1215
1360
  const sound1 = await cacophony.createSound('audio.mp3');
1216
1361
 
1217
- // Second load - instant from memory cache
1362
+ // Second load - from memory while the HTTP policy permits reuse
1218
1363
  const sound2 = await cacophony.createSound('audio.mp3');
1219
1364
 
1220
1365
  // Monitor cache performance via events
@@ -1231,12 +1376,12 @@ cacophony.on('cacheMiss', (event) => {
1231
1376
  // Clear memory cache (browser cache persists)
1232
1377
  cacophony.clearMemoryCache();
1233
1378
 
1234
- // Optional: configure TTL for when no validation tokens exist
1379
+ // Optional: configure TTL for responses without cache directives, expiry, or validators
1235
1380
  import { AudioCache } from 'cacophony';
1236
1381
  AudioCache.setCacheExpirationTime(60 * 60 * 1000); // 1 hour
1237
1382
  ```
1238
1383
 
1239
- The caching system requires no configuration in most cases. It automatically optimizes for performance while respecting HTTP standards.
1384
+ Decoded buffers remain bounded to 64 MiB per audio context. Concurrent callers share fetching and decoding; cancelling one caller does not cancel the others. Storage errors are reported through cacheError while a successfully downloaded response can still be delivered.
1240
1385
 
1241
1386
  ## Cancellation with AbortSignal
1242
1387
 
@@ -0,0 +1,15 @@
1
+ # Third-party notices
2
+
3
+ ## http-cache-semantics 4.1.1
4
+
5
+ https://github.com/kornelski/http-cache-semantics
6
+
7
+ Copyright 2016-2018 Kornel Lesiński
8
+
9
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
10
+
11
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
12
+
13
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1 @@
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));exports.__toESM=s;
@@ -0,0 +1,2 @@
1
+ var e=`data:audio/wav;base64,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`;exports.default=e;
2
+ //# sourceMappingURL=sh_hrir_order_1.wav_url.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sh_hrir_order_1.wav_url.cjs","names":[],"sources":["../../src/assets/sh_hrir_order_1.wav?url"],"sourcesContent":["export default \"data:audio/wav;base64,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\""],"mappings":"AAAA,IAAA,EAAe"}
@@ -0,0 +1,6 @@
1
+ //#region src/assets/sh_hrir_order_1.wav?url
2
+ var e = "data:audio/wav;base64,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";
3
+ //#endregion
4
+ export { e as default };
5
+
6
+ //# sourceMappingURL=sh_hrir_order_1.wav_url.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sh_hrir_order_1.wav_url.mjs","names":[],"sources":["../../src/assets/sh_hrir_order_1.wav?url"],"sourcesContent":["export default \"data:audio/wav;base64,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\""],"mappings":";AAAA,IAAA,IAAe"}
@@ -0,0 +1,2 @@
1
+ var e=class{sounds;names;cleanedUp=!1;constructor(e,t){this.sounds=Object.freeze(e),this.names=Object.freeze([...t])}get(e){if(!Object.hasOwn(this.sounds,e))throw Error(`Unknown audio sprite name: ${e}`);return this.sounds[e]}has(e){return Object.hasOwn(this.sounds,e)}cleanup(){if(!this.cleanedUp){this.cleanedUp=!0;for(let e of this.names)this.sounds[e].cleanup()}}};exports.AudioSprite=e;
2
+ //# sourceMappingURL=audioSprite.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audioSprite.cjs","names":[],"sources":["../src/audioSprite.ts"],"sourcesContent":["import type { LoopCount } from \"./cacophony\";\nimport type { Sound } from \"./sound\";\n\n/** A named, immutable region of an audio atlas, expressed in seconds. */\nexport interface SpriteRegion {\n readonly start: number;\n readonly duration: number;\n readonly loopCount?: LoopCount;\n}\n\n/** Named regions used to construct an {@link AudioSprite}. */\nexport type SpriteMap = Readonly<Record<string, SpriteRegion>>;\n\n/** Options shared by every child Sound created for a sprite. */\nexport interface CreateSpriteOptions {\n panType?: import(\"./cacophony\").PanType;\n signal?: AbortSignal;\n}\n\n/**\n * A typed registry and lifetime owner for the ordinary Sounds in an atlas.\n * Playback and configuration remain child-Sound operations.\n */\nexport class AudioSprite<K extends string = string> {\n readonly sounds: Readonly<Record<K, Sound>>;\n readonly names: readonly K[];\n private cleanedUp = false;\n\n /** @internal Construct sprites through Cacophony.createSprite(). */\n constructor(sounds: Record<K, Sound>, names: readonly K[]) {\n this.sounds = Object.freeze(sounds);\n this.names = Object.freeze([...names]);\n }\n\n get(name: K): Sound {\n if (!Object.hasOwn(this.sounds, name)) {\n throw new Error(`Unknown audio sprite name: ${name}`);\n }\n return this.sounds[name];\n }\n\n has(name: string): name is K {\n return Object.hasOwn(this.sounds, name);\n }\n\n cleanup(): void {\n if (this.cleanedUp) return;\n this.cleanedUp = true;\n for (const name of this.names) {\n this.sounds[name].cleanup();\n }\n }\n}\n"],"mappings":"AAuBA,IAAa,EAAb,KAAoD,CAClD,OACA,MACA,UAAoB,GAGpB,YAAY,EAA0B,EAAqB,CACzD,KAAK,OAAS,OAAO,OAAO,CAAM,EAClC,KAAK,MAAQ,OAAO,OAAO,CAAC,GAAG,CAAK,CAAC,CACvC,CAEA,IAAI,EAAgB,CAClB,GAAI,CAAC,OAAO,OAAO,KAAK,OAAQ,CAAI,EAClC,MAAU,MAAM,8BAA8B,GAAM,EAEtD,OAAO,KAAK,OAAO,EACrB,CAEA,IAAI,EAAyB,CAC3B,OAAO,OAAO,OAAO,KAAK,OAAQ,CAAI,CACxC,CAEA,SAAgB,CACV,SAAK,UACT,MAAK,UAAY,GACjB,IAAK,IAAM,KAAQ,KAAK,MACtB,KAAK,OAAO,GAAM,QAAQ,CAFX,CAInB,CACF"}
@@ -0,0 +1,29 @@
1
+ import { LoopCount } from './cacophony';
2
+ import { Sound } from './sound';
3
+ /** A named, immutable region of an audio atlas, expressed in seconds. */
4
+ export interface SpriteRegion {
5
+ readonly start: number;
6
+ readonly duration: number;
7
+ readonly loopCount?: LoopCount;
8
+ }
9
+ /** Named regions used to construct an {@link AudioSprite}. */
10
+ export type SpriteMap = Readonly<Record<string, SpriteRegion>>;
11
+ /** Options shared by every child Sound created for a sprite. */
12
+ export interface CreateSpriteOptions {
13
+ panType?: import('./cacophony').PanType;
14
+ signal?: AbortSignal;
15
+ }
16
+ /**
17
+ * A typed registry and lifetime owner for the ordinary Sounds in an atlas.
18
+ * Playback and configuration remain child-Sound operations.
19
+ */
20
+ export declare class AudioSprite<K extends string = string> {
21
+ readonly sounds: Readonly<Record<K, Sound>>;
22
+ readonly names: readonly K[];
23
+ private cleanedUp;
24
+ /** @internal Construct sprites through Cacophony.createSprite(). */
25
+ constructor(sounds: Record<K, Sound>, names: readonly K[]);
26
+ get(name: K): Sound;
27
+ has(name: string): name is K;
28
+ cleanup(): void;
29
+ }
@@ -0,0 +1,26 @@
1
+ //#region src/audioSprite.ts
2
+ var e = class {
3
+ sounds;
4
+ names;
5
+ cleanedUp = !1;
6
+ constructor(e, t) {
7
+ this.sounds = Object.freeze(e), this.names = Object.freeze([...t]);
8
+ }
9
+ get(e) {
10
+ if (!Object.hasOwn(this.sounds, e)) throw Error(`Unknown audio sprite name: ${e}`);
11
+ return this.sounds[e];
12
+ }
13
+ has(e) {
14
+ return Object.hasOwn(this.sounds, e);
15
+ }
16
+ cleanup() {
17
+ if (!this.cleanedUp) {
18
+ this.cleanedUp = !0;
19
+ for (let e of this.names) this.sounds[e].cleanup();
20
+ }
21
+ }
22
+ };
23
+ //#endregion
24
+ export { e as AudioSprite };
25
+
26
+ //# sourceMappingURL=audioSprite.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audioSprite.mjs","names":[],"sources":["../src/audioSprite.ts"],"sourcesContent":["import type { LoopCount } from \"./cacophony\";\nimport type { Sound } from \"./sound\";\n\n/** A named, immutable region of an audio atlas, expressed in seconds. */\nexport interface SpriteRegion {\n readonly start: number;\n readonly duration: number;\n readonly loopCount?: LoopCount;\n}\n\n/** Named regions used to construct an {@link AudioSprite}. */\nexport type SpriteMap = Readonly<Record<string, SpriteRegion>>;\n\n/** Options shared by every child Sound created for a sprite. */\nexport interface CreateSpriteOptions {\n panType?: import(\"./cacophony\").PanType;\n signal?: AbortSignal;\n}\n\n/**\n * A typed registry and lifetime owner for the ordinary Sounds in an atlas.\n * Playback and configuration remain child-Sound operations.\n */\nexport class AudioSprite<K extends string = string> {\n readonly sounds: Readonly<Record<K, Sound>>;\n readonly names: readonly K[];\n private cleanedUp = false;\n\n /** @internal Construct sprites through Cacophony.createSprite(). */\n constructor(sounds: Record<K, Sound>, names: readonly K[]) {\n this.sounds = Object.freeze(sounds);\n this.names = Object.freeze([...names]);\n }\n\n get(name: K): Sound {\n if (!Object.hasOwn(this.sounds, name)) {\n throw new Error(`Unknown audio sprite name: ${name}`);\n }\n return this.sounds[name];\n }\n\n has(name: string): name is K {\n return Object.hasOwn(this.sounds, name);\n }\n\n cleanup(): void {\n if (this.cleanedUp) return;\n this.cleanedUp = true;\n for (const name of this.names) {\n this.sounds[name].cleanup();\n }\n }\n}\n"],"mappings":";AAuBA,IAAa,IAAb,MAAoD;CAClD;CACA;CACA,YAAoB;CAGpB,YAAY,GAA0B,GAAqB;EAEzD,AADA,KAAK,SAAS,OAAO,OAAO,CAAM,GAClC,KAAK,QAAQ,OAAO,OAAO,CAAC,GAAG,CAAK,CAAC;CACvC;CAEA,IAAI,GAAgB;EAClB,IAAI,CAAC,OAAO,OAAO,KAAK,QAAQ,CAAI,GAClC,MAAU,MAAM,8BAA8B,GAAM;EAEtD,OAAO,KAAK,OAAO;CACrB;CAEA,IAAI,GAAyB;EAC3B,OAAO,OAAO,OAAO,KAAK,QAAQ,CAAI;CACxC;CAEA,UAAgB;EACV,UAAK,WACT;QAAK,YAAY;GACjB,KAAK,IAAM,KAAQ,KAAK,OACtB,KAAK,OAAO,GAAM,QAAQ;EAFX;CAInB;AACF"}
@@ -0,0 +1,2 @@
1
+ var e=[`touchend`,`click`,`keydown`],t={capture:!0,passive:!0};function n(n){let{context:r,onUnlock:i}=n;if(typeof document>`u`)return()=>{};let a=r.resume;if(typeof a!=`function`)return()=>{};let o=document,s=o.body??o,c=r,l=!1,u=!1,d=!1,f=()=>{if(l){for(let n of e)s.removeEventListener(n,p,t);l=!1}},p=()=>{if(!(d||u)){u=!0,f();try{let e=r.createBuffer(1,1,r.sampleRate),t=r.createBufferSource();t.buffer=e,t.connect(r.destination),t.start(0),t.stop(0)}catch(e){console.warn(`[cacophony/autoplayUnlock] primer failed:`,e)}a.call(r).then(()=>{if(u=!1,!d)try{i()}catch(e){console.error(`[cacophony/autoplayUnlock] onUnlock callback threw:`,e)}},e=>{u=!1,!d&&(console.warn(`[cacophony/autoplayUnlock] resume failed:`,e),h())})}},m=()=>{if(!(d||l||u)){for(let n of e)s.addEventListener(n,p,t);l=!0}};function h(){c.state===`suspended`||c.state===`interrupted`?m():f()}let g=()=>{d||h()};return c.addEventListener?.(`statechange`,g),h(),()=>{d||(d=!0,f(),c.removeEventListener?.(`statechange`,g))}}exports.installAutoplayUnlock=n;
2
+ //# sourceMappingURL=autoplayUnlock.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autoplayUnlock.cjs","names":[],"sources":["../src/autoplayUnlock.ts"],"sourcesContent":["import type { BaseContext } from \"./context\";\n\n/**\n * Autoplay unlock — Howler-parity automatic AudioContext resume on first\n * user gesture.\n *\n * On mobile browsers (especially iOS Safari) an `AudioContext` constructed\n * before any user interaction is created in `suspended` state. It will not\n * produce sound until BOTH:\n *\n * 1. `context.resume()` is called, AND\n * 2. an `AudioBufferSourceNode` is created and started inside the user\n * gesture's call stack (the iOS \"primer\").\n *\n * Many callers do (1) but not (2), and hear silence with no obvious cause.\n * This module watches the context for suspension and arms `touchend` / `click`\n * / `keydown` listeners on `document.body` (or `document` as fallback). A\n * gesture resumes the context, plays a 1-sample silent primer inside the\n * gesture call stack, removes all three listeners, and calls the `onUnlock`\n * callback. If the context suspends again, the listeners are re-armed.\n *\n * Design adapted (not copied) from Howler.js `_unlockAudio` — MIT licensed.\n */\n\nconst UNLOCK_EVENT_TYPES: ReadonlyArray<\"touchend\" | \"click\" | \"keydown\"> = [\"touchend\", \"click\", \"keydown\"];\nconst GESTURE_LISTENER_OPTIONS = { capture: true, passive: true } as const;\n\n/**\n * A `Document`-like type with the methods we need. Avoids a hard dependency\n * on DOM lib types so the package keeps building when `lib: [\"dom\"]` is off.\n */\ninterface DocumentLike {\n body?: EventTargetLike | null;\n addEventListener(type: string, listener: (ev: any) => void, options?: any): void;\n removeEventListener(type: string, listener: (ev: any) => void, options?: any): void;\n}\n\ninterface EventTargetLike {\n addEventListener(type: string, listener: (ev: any) => void, options?: any): void;\n removeEventListener(type: string, listener: (ev: any) => void, options?: any): void;\n}\n\n/**\n * Options for `installAutoplayUnlock`.\n */\nexport interface AutoplayUnlockOptions {\n /**\n * The audio context that should be resumed and primed.\n */\n context: BaseContext;\n\n /**\n * Called after the context has been resumed and the primer buffer has been\n * started. Used by `Cacophony` to emit the public `unlock` event.\n * Errors thrown by the callback are caught and logged so a faulty listener\n * cannot break the unlock flow.\n */\n onUnlock: () => void;\n}\n\n/**\n * Watch for context suspension and install unlock listeners while needed.\n * Returns a cleanup function that removes both the context watcher and any\n * armed gesture listeners (e.g. when a `Cacophony` instance is torn down).\n *\n * If the environment is non-browser (`document` is undefined), this is a\n * no-op and returns a no-op cleanup.\n *\n * @internal\n */\nexport function installAutoplayUnlock(opts: AutoplayUnlockOptions): () => void {\n const { context, onUnlock } = opts;\n\n // Server-side / non-browser: nothing to do.\n if (typeof document === \"undefined\") {\n return () => {};\n }\n\n // `BaseContext.resume` is optional. If this context has no resume() we\n // cannot actually unlock it — installing gesture listeners would be noise,\n // and firing the `unlock` event without a fulfilled resume() would violate\n // the contract (caller would see `unlock` while `Cacophony.locked` stays\n // true, since `locked` is derived from `context.state === \"suspended\"`).\n // Treat the absence as \"this context isn't ours to unlock\" and bail.\n const resume = (context as unknown as { resume?: () => Promise<void> }).resume;\n if (typeof resume !== \"function\") {\n return () => {};\n }\n\n const doc = document as unknown as DocumentLike;\n const target: EventTargetLike = (doc.body as EventTargetLike | null | undefined) ?? doc;\n const observableContext = context as unknown as {\n state?: string;\n addEventListener?: (type: string, listener: () => void) => void;\n removeEventListener?: (type: string, listener: () => void) => void;\n };\n\n let armed = false;\n let resumeInFlight = false;\n let disposed = false;\n\n const removeAll = () => {\n if (!armed) return;\n for (const type of UNLOCK_EVENT_TYPES) {\n target.removeEventListener(type, handler, GESTURE_LISTENER_OPTIONS);\n }\n armed = false;\n };\n\n const handler = () => {\n if (disposed || resumeInFlight) return;\n resumeInFlight = true;\n\n // Remove listeners FIRST so a re-entrant gesture cannot re-trigger us\n // while resume() is in flight.\n removeAll();\n\n // Play the iOS primer INSIDE the gesture call stack. iOS requires that\n // an AudioBufferSourceNode be created and started synchronously inside\n // the gesture handler for the context to truly unlock — calling\n // resume() alone is not sufficient. A 1-sample silent buffer is the\n // minimum that satisfies the requirement and is inaudible.\n try {\n // BaseContext doesn't declare createBuffer because Cacophony's normal\n // code path always feeds an AudioBuffer constructed externally; the\n // primer needs to construct a 1-sample buffer in-place, so we widen\n // the type locally here. Both AudioContext and OfflineAudioContext\n // expose createBuffer.\n const ctxWithCreateBuffer = context as unknown as {\n createBuffer(numberOfChannels: number, length: number, sampleRate: number): unknown;\n };\n const buffer = ctxWithCreateBuffer.createBuffer(1, 1, context.sampleRate);\n const source = context.createBufferSource();\n source.buffer = buffer as any;\n source.connect(context.destination);\n // The load-bearing iOS pattern is start(0); stop(0) on a silent buffer\n // INSIDE the gesture call stack. A 1-sample non-looping buffer would\n // end on its own, but the pair is what the platform contract names.\n source.start(0);\n source.stop(0);\n } catch (err) {\n // Primer failure should not block the rest of the unlock; the\n // context.resume() call below is still useful on platforms that do\n // not require the primer.\n console.warn(\"[cacophony/autoplayUnlock] primer failed:\", err);\n }\n\n // Resume the context, then emit unlock. The `unlock` event signals that\n // audio is actually playable, so we must wait for resume() to fulfill\n // before emitting. On rejection we surface the error and do NOT emit\n // unlock — a failed resume is not an unlock. `resume` is guaranteed to\n // be a function here (guarded at install time above).\n resume.call(context).then(\n () => {\n resumeInFlight = false;\n if (disposed) return;\n try {\n onUnlock();\n } catch (err) {\n console.error(\"[cacophony/autoplayUnlock] onUnlock callback threw:\", err);\n }\n },\n (err: unknown) => {\n resumeInFlight = false;\n if (disposed) return;\n console.warn(\"[cacophony/autoplayUnlock] resume failed:\", err);\n syncGestureListeners();\n },\n );\n };\n\n const armAll = () => {\n if (disposed || armed || resumeInFlight) return;\n for (const type of UNLOCK_EVENT_TYPES) {\n target.addEventListener(type, handler, GESTURE_LISTENER_OPTIONS);\n }\n armed = true;\n };\n\n function syncGestureListeners(): void {\n if (observableContext.state === \"suspended\" || observableContext.state === \"interrupted\") {\n armAll();\n } else {\n removeAll();\n }\n }\n\n const handleStateChange = () => {\n if (!disposed) syncGestureListeners();\n };\n\n observableContext.addEventListener?.(\"statechange\", handleStateChange);\n syncGestureListeners();\n\n return () => {\n if (disposed) return;\n disposed = true;\n removeAll();\n observableContext.removeEventListener?.(\"statechange\", handleStateChange);\n };\n}\n"],"mappings":"AAwBA,IAAM,EAAsE,CAAC,WAAY,QAAS,SAAS,EACrG,EAA2B,CAAE,QAAS,GAAM,QAAS,EAAK,EA6ChE,SAAgB,EAAsB,EAAyC,CAC7E,GAAM,CAAE,UAAS,YAAa,EAG9B,GAAI,OAAO,SAAa,IACtB,UAAa,CAAC,EAShB,IAAM,EAAU,EAAwD,OACxE,GAAI,OAAO,GAAW,WACpB,UAAa,CAAC,EAGhB,IAAM,EAAM,SACN,EAA2B,EAAI,MAA+C,EAC9E,EAAoB,EAMtB,EAAQ,GACR,EAAiB,GACjB,EAAW,GAET,MAAkB,CACjB,KACL,KAAK,IAAM,KAAQ,EACjB,EAAO,oBAAoB,EAAM,EAAS,CAAwB,EAEpE,EAAQ,EAF4D,CAGtE,EAEM,MAAgB,CAChB,QAAY,GAKhB,CAJA,EAAiB,GAIjB,EAAU,EAOV,GAAI,CASF,IAAM,EAAS,EAAoB,aAAa,EAAG,EAAG,EAAQ,UAAU,EAClE,EAAS,EAAQ,mBAAmB,EAC1C,EAAO,OAAS,EAChB,EAAO,QAAQ,EAAQ,WAAW,EAIlC,EAAO,MAAM,CAAC,EACd,EAAO,KAAK,CAAC,CACf,OAAS,EAAK,CAIZ,QAAQ,KAAK,4CAA6C,CAAG,CAC/D,CAOA,EAAO,KAAK,CAAO,EAAE,SACb,CACJ,KAAiB,GACb,GACJ,GAAI,CACF,EAAS,CACX,OAAS,EAAK,CACZ,QAAQ,MAAM,sDAAuD,CAAG,CAC1E,CACF,EACC,GAAiB,CAChB,EAAiB,GACb,KACJ,QAAQ,KAAK,4CAA6C,CAAG,EAC7D,EAAqB,EACvB,CACF,CArDU,CAsDZ,EAEM,MAAe,CACf,QAAY,GAAS,GACzB,KAAK,IAAM,KAAQ,EACjB,EAAO,iBAAiB,EAAM,EAAS,CAAwB,EAEjE,EAAQ,EAFyD,CAGnE,EAEA,SAAS,GAA6B,CAChC,EAAkB,QAAU,aAAe,EAAkB,QAAU,cACzE,EAAO,EAEP,EAAU,CAEd,CAEA,IAAM,MAA0B,CACzB,GAAU,EAAqB,CACtC,EAKA,OAHA,EAAkB,mBAAmB,cAAe,CAAiB,EACrE,EAAqB,MAER,CACP,IACJ,EAAW,GACX,EAAU,EACV,EAAkB,sBAAsB,cAAe,CAAiB,EAC1E,CACF"}
@@ -0,0 +1,58 @@
1
+ //#region src/autoplayUnlock.ts
2
+ var e = [
3
+ "touchend",
4
+ "click",
5
+ "keydown"
6
+ ], t = {
7
+ capture: !0,
8
+ passive: !0
9
+ };
10
+ function n(n) {
11
+ let { context: r, onUnlock: i } = n;
12
+ if (typeof document > "u") return () => {};
13
+ let a = r.resume;
14
+ if (typeof a != "function") return () => {};
15
+ let o = document, s = o.body ?? o, c = r, l = !1, u = !1, d = !1, f = () => {
16
+ if (l) {
17
+ for (let n of e) s.removeEventListener(n, p, t);
18
+ l = !1;
19
+ }
20
+ }, p = () => {
21
+ if (!(d || u)) {
22
+ u = !0, f();
23
+ try {
24
+ let e = r.createBuffer(1, 1, r.sampleRate), t = r.createBufferSource();
25
+ t.buffer = e, t.connect(r.destination), t.start(0), t.stop(0);
26
+ } catch (e) {
27
+ console.warn("[cacophony/autoplayUnlock] primer failed:", e);
28
+ }
29
+ a.call(r).then(() => {
30
+ if (u = !1, !d) try {
31
+ i();
32
+ } catch (e) {
33
+ console.error("[cacophony/autoplayUnlock] onUnlock callback threw:", e);
34
+ }
35
+ }, (e) => {
36
+ u = !1, !d && (console.warn("[cacophony/autoplayUnlock] resume failed:", e), h());
37
+ });
38
+ }
39
+ }, m = () => {
40
+ if (!(d || l || u)) {
41
+ for (let n of e) s.addEventListener(n, p, t);
42
+ l = !0;
43
+ }
44
+ };
45
+ function h() {
46
+ c.state === "suspended" || c.state === "interrupted" ? m() : f();
47
+ }
48
+ let g = () => {
49
+ d || h();
50
+ };
51
+ return c.addEventListener?.("statechange", g), h(), () => {
52
+ d || (d = !0, f(), c.removeEventListener?.("statechange", g));
53
+ };
54
+ }
55
+ //#endregion
56
+ export { n as installAutoplayUnlock };
57
+
58
+ //# sourceMappingURL=autoplayUnlock.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autoplayUnlock.mjs","names":[],"sources":["../src/autoplayUnlock.ts"],"sourcesContent":["import type { BaseContext } from \"./context\";\n\n/**\n * Autoplay unlock — Howler-parity automatic AudioContext resume on first\n * user gesture.\n *\n * On mobile browsers (especially iOS Safari) an `AudioContext` constructed\n * before any user interaction is created in `suspended` state. It will not\n * produce sound until BOTH:\n *\n * 1. `context.resume()` is called, AND\n * 2. an `AudioBufferSourceNode` is created and started inside the user\n * gesture's call stack (the iOS \"primer\").\n *\n * Many callers do (1) but not (2), and hear silence with no obvious cause.\n * This module watches the context for suspension and arms `touchend` / `click`\n * / `keydown` listeners on `document.body` (or `document` as fallback). A\n * gesture resumes the context, plays a 1-sample silent primer inside the\n * gesture call stack, removes all three listeners, and calls the `onUnlock`\n * callback. If the context suspends again, the listeners are re-armed.\n *\n * Design adapted (not copied) from Howler.js `_unlockAudio` — MIT licensed.\n */\n\nconst UNLOCK_EVENT_TYPES: ReadonlyArray<\"touchend\" | \"click\" | \"keydown\"> = [\"touchend\", \"click\", \"keydown\"];\nconst GESTURE_LISTENER_OPTIONS = { capture: true, passive: true } as const;\n\n/**\n * A `Document`-like type with the methods we need. Avoids a hard dependency\n * on DOM lib types so the package keeps building when `lib: [\"dom\"]` is off.\n */\ninterface DocumentLike {\n body?: EventTargetLike | null;\n addEventListener(type: string, listener: (ev: any) => void, options?: any): void;\n removeEventListener(type: string, listener: (ev: any) => void, options?: any): void;\n}\n\ninterface EventTargetLike {\n addEventListener(type: string, listener: (ev: any) => void, options?: any): void;\n removeEventListener(type: string, listener: (ev: any) => void, options?: any): void;\n}\n\n/**\n * Options for `installAutoplayUnlock`.\n */\nexport interface AutoplayUnlockOptions {\n /**\n * The audio context that should be resumed and primed.\n */\n context: BaseContext;\n\n /**\n * Called after the context has been resumed and the primer buffer has been\n * started. Used by `Cacophony` to emit the public `unlock` event.\n * Errors thrown by the callback are caught and logged so a faulty listener\n * cannot break the unlock flow.\n */\n onUnlock: () => void;\n}\n\n/**\n * Watch for context suspension and install unlock listeners while needed.\n * Returns a cleanup function that removes both the context watcher and any\n * armed gesture listeners (e.g. when a `Cacophony` instance is torn down).\n *\n * If the environment is non-browser (`document` is undefined), this is a\n * no-op and returns a no-op cleanup.\n *\n * @internal\n */\nexport function installAutoplayUnlock(opts: AutoplayUnlockOptions): () => void {\n const { context, onUnlock } = opts;\n\n // Server-side / non-browser: nothing to do.\n if (typeof document === \"undefined\") {\n return () => {};\n }\n\n // `BaseContext.resume` is optional. If this context has no resume() we\n // cannot actually unlock it — installing gesture listeners would be noise,\n // and firing the `unlock` event without a fulfilled resume() would violate\n // the contract (caller would see `unlock` while `Cacophony.locked` stays\n // true, since `locked` is derived from `context.state === \"suspended\"`).\n // Treat the absence as \"this context isn't ours to unlock\" and bail.\n const resume = (context as unknown as { resume?: () => Promise<void> }).resume;\n if (typeof resume !== \"function\") {\n return () => {};\n }\n\n const doc = document as unknown as DocumentLike;\n const target: EventTargetLike = (doc.body as EventTargetLike | null | undefined) ?? doc;\n const observableContext = context as unknown as {\n state?: string;\n addEventListener?: (type: string, listener: () => void) => void;\n removeEventListener?: (type: string, listener: () => void) => void;\n };\n\n let armed = false;\n let resumeInFlight = false;\n let disposed = false;\n\n const removeAll = () => {\n if (!armed) return;\n for (const type of UNLOCK_EVENT_TYPES) {\n target.removeEventListener(type, handler, GESTURE_LISTENER_OPTIONS);\n }\n armed = false;\n };\n\n const handler = () => {\n if (disposed || resumeInFlight) return;\n resumeInFlight = true;\n\n // Remove listeners FIRST so a re-entrant gesture cannot re-trigger us\n // while resume() is in flight.\n removeAll();\n\n // Play the iOS primer INSIDE the gesture call stack. iOS requires that\n // an AudioBufferSourceNode be created and started synchronously inside\n // the gesture handler for the context to truly unlock — calling\n // resume() alone is not sufficient. A 1-sample silent buffer is the\n // minimum that satisfies the requirement and is inaudible.\n try {\n // BaseContext doesn't declare createBuffer because Cacophony's normal\n // code path always feeds an AudioBuffer constructed externally; the\n // primer needs to construct a 1-sample buffer in-place, so we widen\n // the type locally here. Both AudioContext and OfflineAudioContext\n // expose createBuffer.\n const ctxWithCreateBuffer = context as unknown as {\n createBuffer(numberOfChannels: number, length: number, sampleRate: number): unknown;\n };\n const buffer = ctxWithCreateBuffer.createBuffer(1, 1, context.sampleRate);\n const source = context.createBufferSource();\n source.buffer = buffer as any;\n source.connect(context.destination);\n // The load-bearing iOS pattern is start(0); stop(0) on a silent buffer\n // INSIDE the gesture call stack. A 1-sample non-looping buffer would\n // end on its own, but the pair is what the platform contract names.\n source.start(0);\n source.stop(0);\n } catch (err) {\n // Primer failure should not block the rest of the unlock; the\n // context.resume() call below is still useful on platforms that do\n // not require the primer.\n console.warn(\"[cacophony/autoplayUnlock] primer failed:\", err);\n }\n\n // Resume the context, then emit unlock. The `unlock` event signals that\n // audio is actually playable, so we must wait for resume() to fulfill\n // before emitting. On rejection we surface the error and do NOT emit\n // unlock — a failed resume is not an unlock. `resume` is guaranteed to\n // be a function here (guarded at install time above).\n resume.call(context).then(\n () => {\n resumeInFlight = false;\n if (disposed) return;\n try {\n onUnlock();\n } catch (err) {\n console.error(\"[cacophony/autoplayUnlock] onUnlock callback threw:\", err);\n }\n },\n (err: unknown) => {\n resumeInFlight = false;\n if (disposed) return;\n console.warn(\"[cacophony/autoplayUnlock] resume failed:\", err);\n syncGestureListeners();\n },\n );\n };\n\n const armAll = () => {\n if (disposed || armed || resumeInFlight) return;\n for (const type of UNLOCK_EVENT_TYPES) {\n target.addEventListener(type, handler, GESTURE_LISTENER_OPTIONS);\n }\n armed = true;\n };\n\n function syncGestureListeners(): void {\n if (observableContext.state === \"suspended\" || observableContext.state === \"interrupted\") {\n armAll();\n } else {\n removeAll();\n }\n }\n\n const handleStateChange = () => {\n if (!disposed) syncGestureListeners();\n };\n\n observableContext.addEventListener?.(\"statechange\", handleStateChange);\n syncGestureListeners();\n\n return () => {\n if (disposed) return;\n disposed = true;\n removeAll();\n observableContext.removeEventListener?.(\"statechange\", handleStateChange);\n };\n}\n"],"mappings":";AAwBA,IAAM,IAAsE;CAAC;CAAY;CAAS;AAAS,GACrG,IAA2B;CAAE,SAAS;CAAM,SAAS;AAAK;AA6ChE,SAAgB,EAAsB,GAAyC;CAC7E,IAAM,EAAE,YAAS,gBAAa;CAG9B,IAAI,OAAO,WAAa,KACtB,aAAa,CAAC;CAShB,IAAM,IAAU,EAAwD;CACxE,IAAI,OAAO,KAAW,YACpB,aAAa,CAAC;CAGhB,IAAM,IAAM,UACN,IAA2B,EAAI,QAA+C,GAC9E,IAAoB,GAMtB,IAAQ,IACR,IAAiB,IACjB,IAAW,IAET,UAAkB;EACjB,OACL;QAAK,IAAM,KAAQ,GACjB,EAAO,oBAAoB,GAAM,GAAS,CAAwB;GAEpE,IAAQ;EAF4D;CAGtE,GAEM,UAAgB;EAChB,WAAY,IAKhB;GAJA,IAAiB,IAIjB,EAAU;GAOV,IAAI;IASF,IAAM,IAAS,EAAoB,aAAa,GAAG,GAAG,EAAQ,UAAU,GAClE,IAAS,EAAQ,mBAAmB;IAO1C,AANA,EAAO,SAAS,GAChB,EAAO,QAAQ,EAAQ,WAAW,GAIlC,EAAO,MAAM,CAAC,GACd,EAAO,KAAK,CAAC;GACf,SAAS,GAAK;IAIZ,QAAQ,KAAK,6CAA6C,CAAG;GAC/D;GAOA,EAAO,KAAK,CAAO,EAAE,WACb;IACJ,QAAiB,IACb,IACJ,IAAI;KACF,EAAS;IACX,SAAS,GAAK;KACZ,QAAQ,MAAM,uDAAuD,CAAG;IAC1E;GACF,IACC,MAAiB;IAChB,IAAiB,IACb,OACJ,QAAQ,KAAK,6CAA6C,CAAG,GAC7D,EAAqB;GACvB,CACF;EArDU;CAsDZ,GAEM,UAAe;EACf,WAAY,KAAS,IACzB;QAAK,IAAM,KAAQ,GACjB,EAAO,iBAAiB,GAAM,GAAS,CAAwB;GAEjE,IAAQ;EAFyD;CAGnE;CAEA,SAAS,IAA6B;EACpC,AAAI,EAAkB,UAAU,eAAe,EAAkB,UAAU,gBACzE,EAAO,IAEP,EAAU;CAEd;CAEA,IAAM,UAA0B;EAC9B,AAAK,KAAU,EAAqB;CACtC;CAKA,OAHA,EAAkB,mBAAmB,eAAe,CAAiB,GACrE,EAAqB,SAER;EACP,MACJ,IAAW,IACX,EAAU,GACV,EAAkB,sBAAsB,eAAe,CAAiB;CAC1E;AACF"}
@@ -0,0 +1,2 @@
1
+ const e=require("./effectChain.cjs"),t=require("./eventEmitter.cjs"),n=require("./filters.cjs"),r=require("./pannerMixin.cjs"),i=require("./volumeMixin.cjs");var a=class extends r.PannerMixin(i.VolumeMixin(n.FilterManager)){source;_effectChain;_effectContext;_sendGains=new Map;_state=`unplayed`;origin;eventEmitter=new t.TypedEventEmitter;constructor(e){super(),this.origin=e}setEffectChainEndpoints(t,n){if(this._effectContext=t.context??this.origin.context,this._effectChain){this._effectChain.setEndpoints(t,n);return}this._effectChain=new e.EffectChain(t,n,this.constructor.name)}addEffect(e){try{return this.materializeEffect(e)}catch(e){return Promise.reject(e)}}_addSourceEffect(e){return this.materializeEffect(e)}_addFilterEffect(e){return this.materializeEffect(e,e=>{this._filters.push(e)})}materializeEffect(e,t){if(!this._effectChain||!this._effectContext)throw Error(`Cannot add an effect before the playback effect chain is initialized`);let n=this._effectChain,r=n.reserve(),i;try{i=e.build(this._effectContext)}catch(e){throw n.cancel(r),e}if(i instanceof Promise||typeof i==`object`&&i&&`then`in i)return Promise.resolve(i).then(e=>{let i=n.resolve(r,e);return t?.(i),i},e=>{throw n.cancel(r),e});try{let e=n.resolve(r,i);return t?.(e),Promise.resolve(e)}catch(e){throw n.cancel(r),e}}rampEffectParam(e,t,n,r){if(!this._effectChain)throw Error(`Cannot automate an effect before the playback effect chain is initialized`);this._effectChain.rampParam(e,t,n,r)}addFilter(e){if(!this._effectChain)throw Error(`Cannot add a filter before the playback effect chain is initialized`);this._effectChain.add(e,this._filters.length),this._filters.push(e)}removeFilter(e){if(!this._effectChain)throw Error(`Cannot remove a filter before the playback effect chain is initialized`);this._effectChain.remove(e),this._filters=this._filters.filter(t=>t!==e)}setFilterOrder(e){if(!this._effectChain)throw Error(`Cannot reorder filters before the playback effect chain is initialized`);let t=this._effectChain.nodes.filter(e=>!this._filters.includes(e));this._effectChain.setOrder([...e,...t]),this._filters=[...e]}setFilterBypassed(e,t){if(!this._effectChain)throw Error(`Cannot bypass a filter before the playback effect chain is initialized`);this._effectChain.setBypassed(e,t)}isFilterBypassed(e){return this._effectChain?.isBypassed(e)??!1}rampFilterParam(e,t,n,r){if(!this._effectChain)throw Error(`Cannot automate a filter before the playback effect chain is initialized`);this._effectChain.rampParam(e,t,n,r)}setPanType(e,t){let n=this.panner;super.setPanType(e,t),!(this.panner===n||!this.panner||!this.source||!this.gainNode)&&(this.setEffectChainEndpoints(this.source,this.panner),this.panner.connect(this.gainNode))}get state(){return this._state}get isPlaying(){return this._state===`playing`}get isPaused(){return this._state===`paused`}markPlaying(){this._state=`playing`}markPaused(){this._state=`paused`}markStopped(){this._state=`stopped`}emitPlayStarted(e){this.markPlaying(),this.emit(`play`,this),e&&this.emit(`resume`,void 0),this.origin.cacophony?.emit(`globalPlay`,{source:this.origin,timestamp:Date.now()})}emitPaused(){this.markPaused(),this.emit(`pause`,void 0),this.origin.cacophony?.emit(`globalPause`,{source:this.origin,timestamp:Date.now()})}emitStopped(){this.markStopped(),this.emit(`stop`,void 0),this.origin.cacophony?.emit(`globalStop`,{source:this.origin,timestamp:Date.now()})}on(e,t){return this.eventEmitter.on(e,t)}off(e,t){this.eventEmitter.off(e,t)}emit(e,t){this.eventEmitter.emit(e,t)}async emitAsync(e,t){return this.eventEmitter.emitAsync(e,t)}fadeTo(e,t,n=`linear`,r){return this.emit(`fadeStart`,{target:e,duration:t,type:n}),super.fadeTo(e,t,n,r).then(()=>{this.emit(`fadeEnd`,void 0)})}cancelFade(){let e=this._isFading;super.cancelFade(),e&&this.emit(`fadeCancel`,void 0)}cleanup(){for(let e of this._sendGains.values())try{e.disconnect()}catch{}this._sendGains.clear(),this._effectChain?.destroy(),this._effectChain=void 0,this._effectContext=void 0,this.eventEmitter.removeAllListeners(),super.cleanup()}};exports.BasePlayback=a;
2
+ //# sourceMappingURL=basePlayback.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"basePlayback.cjs","names":[],"sources":["../src/basePlayback.ts"],"sourcesContent":["import type { Bus } from \"./bus\";\nimport type { FadeType, PanType, PlayOptions } from \"./cacophony\";\nimport type { PlaybackContainer } from \"./container\";\nimport type { AudioNode, BaseContext, BiquadFilterNode, GainNode } from \"./context\";\nimport { EffectChain } from \"./effectChain\";\nimport type { CacophonyEffect } from \"./effects\";\nimport { TypedEventEmitter } from \"./eventEmitter\";\nimport type { PlaybackEvents } from \"./events\";\nimport { FilterManager } from \"./filters\";\nimport { PannerMixin } from \"./pannerMixin\";\nimport { VolumeMixin } from \"./volumeMixin\";\n\nexport type PlaybackState = \"unplayed\" | \"playing\" | \"paused\" | \"stopped\";\n\nexport abstract class BasePlayback extends /* @__PURE__ */ PannerMixin(/* @__PURE__ */ VolumeMixin(FilterManager)) {\n public source?: AudioNode;\n protected _effectChain?: EffectChain;\n private _effectContext?: BaseContext;\n /**\n * Per-playback send-gain allocations owned by the shared routing state\n * machine. Cleanup disconnects every allocation deterministically.\n */\n _sendGains: Map<Bus, GainNode> = new Map();\n protected _state: PlaybackState = \"unplayed\";\n public origin: PlaybackContainer;\n public eventEmitter: TypedEventEmitter<PlaybackEvents> = new TypedEventEmitter<PlaybackEvents>();\n\n constructor(origin: PlaybackContainer) {\n super();\n this.origin = origin;\n }\n\n protected setEffectChainEndpoints(input: AudioNode, output: AudioNode): void {\n this._effectContext =\n (input.context as BaseContext | undefined) ??\n (this.origin as PlaybackContainer & { context?: BaseContext }).context;\n if (this._effectChain) {\n this._effectChain.setEndpoints(input, output);\n return;\n }\n this._effectChain = new EffectChain(input, output, this.constructor.name);\n }\n\n /** Build and splice an effect into this live playback's pre-panner chain. */\n addEffect(effect: CacophonyEffect): Promise<AudioNode> {\n try {\n return this.materializeEffect(effect);\n } catch (error) {\n return Promise.reject(error);\n }\n }\n\n /** @internal Source preplay path that preserves synchronous native-build failures. */\n _addSourceEffect(effect: CacophonyEffect): Promise<AudioNode> {\n return this.materializeEffect(effect);\n }\n\n /** @internal Materialize a source-level biquad recipe and retain filter API compatibility. */\n _addFilterEffect(effect: CacophonyEffect): Promise<AudioNode> {\n return this.materializeEffect(effect, (handle) => {\n this._filters.push(handle as BiquadFilterNode);\n });\n }\n\n private materializeEffect(effect: CacophonyEffect, onBuilt?: (handle: AudioNode) => void): Promise<AudioNode> {\n if (!this._effectChain || !this._effectContext) {\n throw new Error(\"Cannot add an effect before the playback effect chain is initialized\");\n }\n const chain = this._effectChain;\n const reservation = chain.reserve();\n let built;\n try {\n built = effect.build(this._effectContext);\n } catch (error) {\n chain.cancel(reservation);\n throw error;\n }\n if (built instanceof Promise || (typeof built === \"object\" && built !== null && \"then\" in built)) {\n return Promise.resolve(built).then(\n (resolved) => {\n const handle = chain.resolve(reservation, resolved);\n onBuilt?.(handle);\n return handle;\n },\n (error) => {\n chain.cancel(reservation);\n throw error;\n },\n );\n }\n try {\n const handle = chain.resolve(reservation, built);\n onBuilt?.(handle);\n return Promise.resolve(handle);\n } catch (error) {\n chain.cancel(reservation);\n throw error;\n }\n }\n\n rampEffectParam(\n handle: AudioNode,\n paramName: string,\n value: number,\n options?: { duration?: number; type?: FadeType },\n ): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot automate an effect before the playback effect chain is initialized\");\n }\n this._effectChain.rampParam(handle, paramName, value, options);\n }\n\n addFilter(filter: BiquadFilterNode): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot add a filter before the playback effect chain is initialized\");\n }\n this._effectChain.add(filter, this._filters.length);\n this._filters.push(filter);\n }\n\n removeFilter(filter: BiquadFilterNode): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot remove a filter before the playback effect chain is initialized\");\n }\n this._effectChain.remove(filter);\n this._filters = this._filters.filter((candidate) => candidate !== filter);\n }\n\n setFilterOrder(filters: readonly BiquadFilterNode[]): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot reorder filters before the playback effect chain is initialized\");\n }\n const nonFilters = this._effectChain.nodes.filter((node) => !this._filters.includes(node as BiquadFilterNode));\n this._effectChain.setOrder([...filters, ...nonFilters]);\n this._filters = [...filters];\n }\n\n setFilterBypassed(filter: BiquadFilterNode, bypassed: boolean): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot bypass a filter before the playback effect chain is initialized\");\n }\n this._effectChain.setBypassed(filter, bypassed);\n }\n\n isFilterBypassed(filter: BiquadFilterNode): boolean {\n return this._effectChain?.isBypassed(filter) ?? false;\n }\n\n rampFilterParam(\n filter: BiquadFilterNode,\n paramName: string,\n value: number,\n options?: { duration?: number; type?: FadeType },\n ): void {\n if (!this._effectChain) {\n throw new Error(\"Cannot automate a filter before the playback effect chain is initialized\");\n }\n this._effectChain.rampParam(filter, paramName, value, options);\n }\n\n override setPanType(panType: PanType, audioContext: BaseContext): void {\n const previousPanner = this.panner;\n super.setPanType(panType, audioContext);\n if (this.panner === previousPanner || !this.panner || !this.source || !this.gainNode) return;\n this.setEffectChainEndpoints(this.source, this.panner);\n this.panner.connect(this.gainNode);\n }\n\n abstract play(options?: PlayOptions): [this];\n abstract pause(): void;\n abstract stop(): void;\n\n /**\n * Gets the current lifecycle state without changing playback.\n */\n\n get state(): PlaybackState {\n return this._state;\n }\n\n /** Checks if the audio is currently playing. */\n get isPlaying(): boolean {\n return this._state === \"playing\";\n }\n\n get isPaused(): boolean {\n return this._state === \"paused\";\n }\n\n protected markPlaying(): void {\n this._state = \"playing\";\n }\n\n protected markPaused(): void {\n this._state = \"paused\";\n }\n\n protected markStopped(): void {\n this._state = \"stopped\";\n }\n\n protected emitPlayStarted(isResume: boolean): void {\n this.markPlaying();\n this.emit(\"play\", this);\n if (isResume) {\n this.emit(\"resume\", undefined);\n }\n this.origin.cacophony?.emit(\"globalPlay\", {\n source: this.origin,\n timestamp: Date.now(),\n });\n }\n\n protected emitPaused(): void {\n this.markPaused();\n this.emit(\"pause\", undefined);\n this.origin.cacophony?.emit(\"globalPause\", {\n source: this.origin,\n timestamp: Date.now(),\n });\n }\n\n protected emitStopped(): void {\n this.markStopped();\n this.emit(\"stop\", undefined);\n this.origin.cacophony?.emit(\"globalStop\", {\n source: this.origin,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Register event listener.\n * @returns Cleanup function\n */\n on<K extends keyof PlaybackEvents>(event: K, listener: (data: PlaybackEvents[K]) => void): () => void {\n return this.eventEmitter.on(event, listener);\n }\n\n /**\n * Remove event listener.\n */\n off<K extends keyof PlaybackEvents>(event: K, listener: (data: PlaybackEvents[K]) => void): void {\n this.eventEmitter.off(event, listener);\n }\n\n public emit<K extends keyof PlaybackEvents>(event: K, data: PlaybackEvents[K]): void {\n this.eventEmitter.emit(event, data);\n }\n\n public async emitAsync<K extends keyof PlaybackEvents>(event: K, data: PlaybackEvents[K]): Promise<void> {\n return this.eventEmitter.emitAsync(event, data);\n }\n\n /**\n * Fades the volume to a target value, emitting fadeStart and fadeEnd events.\n */\n fadeTo(\n value: number,\n duration: number,\n type: FadeType = \"linear\",\n options?: { startTime?: number; startValue?: number },\n ): Promise<void> {\n this.emit(\"fadeStart\", { target: value, duration, type });\n return super.fadeTo(value, duration, type, options).then(() => {\n this.emit(\"fadeEnd\", undefined);\n });\n }\n\n /**\n * Cancels any in-progress fade, emitting fadeCancel if a fade was active.\n */\n cancelFade(): void {\n const wasFading = this._isFading;\n super.cancelFade();\n if (wasFading) {\n this.emit(\"fadeCancel\", undefined);\n }\n }\n\n cleanup(): void {\n for (const sendGain of this._sendGains.values()) {\n try {\n sendGain.disconnect();\n } catch {\n // The node may already have been disconnected externally.\n }\n }\n this._sendGains.clear();\n this._effectChain?.destroy();\n this._effectChain = undefined;\n this._effectContext = undefined;\n this.eventEmitter.removeAllListeners();\n super.cleanup();\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { Bus } from './bus';
2
- import { FadeType } from './cacophony';
2
+ import { FadeType, PanType, PlayOptions } from './cacophony';
3
3
  import { PlaybackContainer } from './container';
4
4
  import { AudioNode, BaseContext, BiquadFilterNode, GainNode } from './context';
5
5
  import { EffectChain } from './effectChain';
@@ -10,9 +10,9 @@ import { FilterManager } from './filters';
10
10
  export type PlaybackState = "unplayed" | "playing" | "paused" | "stopped";
11
11
  declare const BasePlayback_base: (abstract new (...args: any[]) => {
12
12
  panner?: import('./context').PannerNode | import('./context').StereoPannerNode | undefined;
13
- _panType: import('./cacophony').PanType;
14
- readonly panType: import('./cacophony').PanType;
15
- setPanType(panType: import('./cacophony').PanType, audioContext: BaseContext): void;
13
+ _panType: PanType;
14
+ readonly panType: PanType;
15
+ setPanType(panType: PanType, audioContext: BaseContext): void;
16
16
  setPannerNode(pannerNode: import('./context').PannerNode): void;
17
17
  get stereoPan(): number | null;
18
18
  set stereoPan(value: number);
@@ -26,7 +26,7 @@ declare const BasePlayback_base: (abstract new (...args: any[]) => {
26
26
  readonly filters: BiquadFilterNode[];
27
27
  addFilters(filters: BiquadFilterNode[]): void;
28
28
  }) & typeof FilterManager & (abstract new (...args: any[]) => import('./volumeMixin').VolumeControls);
29
- export declare abstract class BasePlayback extends BasePlayback_base {
29
+ export declare abstract class BasePlayback extends /* @__PURE__ */ BasePlayback_base {
30
30
  source?: AudioNode;
31
31
  protected _effectChain?: EffectChain;
32
32
  private _effectContext?;
@@ -60,12 +60,15 @@ export declare abstract class BasePlayback extends BasePlayback_base {
60
60
  duration?: number;
61
61
  type?: FadeType;
62
62
  }): void;
63
- abstract play(): [this];
63
+ setPanType(panType: PanType, audioContext: BaseContext): void;
64
+ abstract play(options?: PlayOptions): [this];
64
65
  abstract pause(): void;
65
66
  abstract stop(): void;
66
67
  /**
67
- * Checks if the audio is currently playing.
68
+ * Gets the current lifecycle state without changing playback.
68
69
  */
70
+ get state(): PlaybackState;
71
+ /** Checks if the audio is currently playing. */
69
72
  get isPlaying(): boolean;
70
73
  get isPaused(): boolean;
71
74
  protected markPlaying(): void;
@@ -88,7 +91,10 @@ export declare abstract class BasePlayback extends BasePlayback_base {
88
91
  /**
89
92
  * Fades the volume to a target value, emitting fadeStart and fadeEnd events.
90
93
  */
91
- fadeTo(value: number, duration: number, type?: FadeType): Promise<void>;
94
+ fadeTo(value: number, duration: number, type?: FadeType, options?: {
95
+ startTime?: number;
96
+ startValue?: number;
97
+ }): Promise<void>;
92
98
  /**
93
99
  * Cancels any in-progress fade, emitting fadeCancel if a fade was active.
94
100
  */