@datamoc/mw_games 0.43.0 → 0.44.2

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 (255) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +22 -0
  3. package/REFERENCE.md +82 -6
  4. package/api-surface.json +1028 -2
  5. package/dist/actors/Resource.d.ts.map +1 -1
  6. package/dist/actors/Resource.js +2 -3
  7. package/dist/actors/Resource.js.map +1 -1
  8. package/dist/audio/AdaptiveMusic.d.ts +85 -0
  9. package/dist/audio/AdaptiveMusic.d.ts.map +1 -0
  10. package/dist/audio/AdaptiveMusic.js +230 -0
  11. package/dist/audio/AdaptiveMusic.js.map +1 -0
  12. package/dist/audio/AudioLoader.d.ts +7 -2
  13. package/dist/audio/AudioLoader.d.ts.map +1 -1
  14. package/dist/audio/AudioLoader.js +23 -18
  15. package/dist/audio/AudioLoader.js.map +1 -1
  16. package/dist/audio/AudioViews.d.ts +70 -0
  17. package/dist/audio/AudioViews.d.ts.map +1 -0
  18. package/dist/audio/AudioViews.js +119 -0
  19. package/dist/audio/AudioViews.js.map +1 -0
  20. package/dist/audio/BusBudget.d.ts +89 -0
  21. package/dist/audio/BusBudget.d.ts.map +1 -0
  22. package/dist/audio/BusBudget.js +120 -0
  23. package/dist/audio/BusBudget.js.map +1 -0
  24. package/dist/audio/Channels.d.ts +65 -1
  25. package/dist/audio/Channels.d.ts.map +1 -1
  26. package/dist/audio/Channels.js +117 -30
  27. package/dist/audio/Channels.js.map +1 -1
  28. package/dist/audio/CoreHost.d.ts +114 -0
  29. package/dist/audio/CoreHost.d.ts.map +1 -0
  30. package/dist/audio/CoreHost.js +335 -0
  31. package/dist/audio/CoreHost.js.map +1 -0
  32. package/dist/audio/DecoderGuard.d.ts +139 -0
  33. package/dist/audio/DecoderGuard.d.ts.map +1 -0
  34. package/dist/audio/DecoderGuard.js +208 -0
  35. package/dist/audio/DecoderGuard.js.map +1 -0
  36. package/dist/audio/Fft.d.ts +3 -0
  37. package/dist/audio/Fft.d.ts.map +1 -0
  38. package/dist/audio/Fft.js +45 -0
  39. package/dist/audio/Fft.js.map +1 -0
  40. package/dist/audio/LoopTags.d.ts +18 -0
  41. package/dist/audio/LoopTags.d.ts.map +1 -1
  42. package/dist/audio/LoopTags.js +20 -0
  43. package/dist/audio/LoopTags.js.map +1 -1
  44. package/dist/audio/Midi.d.ts +35 -0
  45. package/dist/audio/Midi.d.ts.map +1 -1
  46. package/dist/audio/Midi.js +71 -1
  47. package/dist/audio/Midi.js.map +1 -1
  48. package/dist/audio/MidiRender.d.ts +31 -0
  49. package/dist/audio/MidiRender.d.ts.map +1 -1
  50. package/dist/audio/MidiRender.js +79 -2
  51. package/dist/audio/MidiRender.js.map +1 -1
  52. package/dist/audio/MidiSynth.d.ts +147 -0
  53. package/dist/audio/MidiSynth.d.ts.map +1 -0
  54. package/dist/audio/MidiSynth.js +205 -0
  55. package/dist/audio/MidiSynth.js.map +1 -0
  56. package/dist/audio/Playable.d.ts +7 -0
  57. package/dist/audio/Playable.d.ts.map +1 -1
  58. package/dist/audio/Playable.js.map +1 -1
  59. package/dist/audio/Positional.d.ts +12 -4
  60. package/dist/audio/Positional.d.ts.map +1 -1
  61. package/dist/audio/Positional.js +5 -2
  62. package/dist/audio/Positional.js.map +1 -1
  63. package/dist/audio/Resample.d.ts +61 -0
  64. package/dist/audio/Resample.d.ts.map +1 -0
  65. package/dist/audio/Resample.js +97 -0
  66. package/dist/audio/Resample.js.map +1 -0
  67. package/dist/audio/Settings.d.ts +99 -0
  68. package/dist/audio/Settings.d.ts.map +1 -0
  69. package/dist/audio/Settings.js +134 -0
  70. package/dist/audio/Settings.js.map +1 -0
  71. package/dist/audio/Sfx.d.ts +148 -0
  72. package/dist/audio/Sfx.d.ts.map +1 -0
  73. package/dist/audio/Sfx.js +213 -0
  74. package/dist/audio/Sfx.js.map +1 -0
  75. package/dist/audio/SpectralDistance.d.ts +34 -0
  76. package/dist/audio/SpectralDistance.d.ts.map +1 -0
  77. package/dist/audio/SpectralDistance.js +64 -0
  78. package/dist/audio/SpectralDistance.js.map +1 -0
  79. package/dist/audio/StreamChannel.d.ts +150 -0
  80. package/dist/audio/StreamChannel.d.ts.map +1 -0
  81. package/dist/audio/StreamChannel.js +390 -0
  82. package/dist/audio/StreamChannel.js.map +1 -0
  83. package/dist/audio/StreamPlayable.d.ts +73 -0
  84. package/dist/audio/StreamPlayable.d.ts.map +1 -0
  85. package/dist/audio/StreamPlayable.js +130 -0
  86. package/dist/audio/StreamPlayable.js.map +1 -0
  87. package/dist/audio/SyxBank.d.ts +73 -0
  88. package/dist/audio/SyxBank.d.ts.map +1 -0
  89. package/dist/audio/SyxBank.js +130 -0
  90. package/dist/audio/SyxBank.js.map +1 -0
  91. package/dist/audio/Underrun.d.ts +25 -0
  92. package/dist/audio/Underrun.d.ts.map +1 -0
  93. package/dist/audio/Underrun.js +18 -0
  94. package/dist/audio/Underrun.js.map +1 -0
  95. package/dist/audio/Wav.d.ts +56 -0
  96. package/dist/audio/Wav.d.ts.map +1 -0
  97. package/dist/audio/Wav.js +164 -0
  98. package/dist/audio/Wav.js.map +1 -0
  99. package/dist/audio/WebMidi.d.ts +58 -0
  100. package/dist/audio/WebMidi.d.ts.map +1 -0
  101. package/dist/audio/WebMidi.js +79 -0
  102. package/dist/audio/WebMidi.js.map +1 -0
  103. package/dist/audio/humanize/Onsets.d.ts.map +1 -1
  104. package/dist/audio/humanize/Onsets.js +1 -44
  105. package/dist/audio/humanize/Onsets.js.map +1 -1
  106. package/dist/audio/index.d.ts +38 -6
  107. package/dist/audio/index.d.ts.map +1 -1
  108. package/dist/audio/index.js +19 -3
  109. package/dist/audio/index.js.map +1 -1
  110. package/dist/battle/AttackPreview.d.ts.map +1 -1
  111. package/dist/battle/AttackPreview.js +8 -8
  112. package/dist/battle/AttackPreview.js.map +1 -1
  113. package/dist/battle/StatStages.d.ts.map +1 -1
  114. package/dist/battle/StatStages.js +2 -1
  115. package/dist/battle/StatStages.js.map +1 -1
  116. package/dist/board/Engine.d.ts +2 -0
  117. package/dist/board/Engine.d.ts.map +1 -1
  118. package/dist/board/Engine.js +1 -1
  119. package/dist/board/Engine.js.map +1 -1
  120. package/dist/board/HexSkirmish.d.ts.map +1 -1
  121. package/dist/board/HexSkirmish.js +11 -72
  122. package/dist/board/HexSkirmish.js.map +1 -1
  123. package/dist/board/Tactics.d.ts.map +1 -1
  124. package/dist/board/Tactics.js +3 -5
  125. package/dist/board/Tactics.js.map +1 -1
  126. package/dist/board/tourney.d.ts.map +1 -1
  127. package/dist/board/tourney.js +2 -19
  128. package/dist/board/tourney.js.map +1 -1
  129. package/dist/core/Handles.js +7 -7
  130. package/dist/core/Handles.js.map +1 -1
  131. package/dist/mw_games.global.js +25 -22
  132. package/dist/mwl/conditions.d.ts +12 -0
  133. package/dist/mwl/conditions.d.ts.map +1 -1
  134. package/dist/mwl/conditions.js +49 -8
  135. package/dist/mwl/conditions.js.map +1 -1
  136. package/dist/mwl/index.d.ts +11 -0
  137. package/dist/mwl/index.d.ts.map +1 -1
  138. package/dist/mwl/index.js +6 -0
  139. package/dist/mwl/index.js.map +1 -1
  140. package/dist/mwl/package-container.d.ts +185 -0
  141. package/dist/mwl/package-container.d.ts.map +1 -0
  142. package/dist/mwl/package-container.js +446 -0
  143. package/dist/mwl/package-container.js.map +1 -0
  144. package/dist/mwl/package-document.d.ts +217 -0
  145. package/dist/mwl/package-document.d.ts.map +1 -0
  146. package/dist/mwl/package-document.js +639 -0
  147. package/dist/mwl/package-document.js.map +1 -0
  148. package/dist/mwl/package-rom.d.ts +37 -0
  149. package/dist/mwl/package-rom.d.ts.map +1 -0
  150. package/dist/mwl/package-rom.js +96 -0
  151. package/dist/mwl/package-rom.js.map +1 -0
  152. package/dist/mwl/package-verify.d.ts +34 -0
  153. package/dist/mwl/package-verify.d.ts.map +1 -0
  154. package/dist/mwl/package-verify.js +111 -0
  155. package/dist/mwl/package-verify.js.map +1 -0
  156. package/dist/mwl/package.d.ts +184 -0
  157. package/dist/mwl/package.d.ts.map +1 -0
  158. package/dist/mwl/package.js +382 -0
  159. package/dist/mwl/package.js.map +1 -0
  160. package/dist/physics/PhysicsWorld.d.ts.map +1 -1
  161. package/dist/physics/PhysicsWorld.js +4 -3
  162. package/dist/physics/PhysicsWorld.js.map +1 -1
  163. package/dist/physics/XpbdWorld.d.ts.map +1 -1
  164. package/dist/physics/XpbdWorld.js +2 -1
  165. package/dist/physics/XpbdWorld.js.map +1 -1
  166. package/dist/testing/FakeAudio.d.ts +149 -0
  167. package/dist/testing/FakeAudio.d.ts.map +1 -0
  168. package/dist/testing/FakeAudio.js +219 -0
  169. package/dist/testing/FakeAudio.js.map +1 -0
  170. package/dist/testing/index.d.ts +2 -0
  171. package/dist/testing/index.d.ts.map +1 -1
  172. package/dist/testing/index.js +1 -0
  173. package/dist/testing/index.js.map +1 -1
  174. package/dist/threads/index.d.ts +2 -2
  175. package/dist/threads/index.d.ts.map +1 -1
  176. package/dist/threads/index.js +1 -1
  177. package/dist/threads/index.js.map +1 -1
  178. package/dist/threads/spawn.d.ts +32 -0
  179. package/dist/threads/spawn.d.ts.map +1 -1
  180. package/dist/threads/spawn.js +35 -11
  181. package/dist/threads/spawn.js.map +1 -1
  182. package/dist/two-d/Game.d.ts +13 -2
  183. package/dist/two-d/Game.d.ts.map +1 -1
  184. package/dist/two-d/Game.js +42 -18
  185. package/dist/two-d/Game.js.map +1 -1
  186. package/dist/two-d/render/HazeLayer.d.ts.map +1 -1
  187. package/dist/two-d/render/HazeLayer.js +2 -1
  188. package/dist/two-d/render/HazeLayer.js.map +1 -1
  189. package/dist/two-d/render/ImageModifiers.d.ts +4 -2
  190. package/dist/two-d/render/ImageModifiers.d.ts.map +1 -1
  191. package/dist/two-d/render/ImageModifiers.js +13 -8
  192. package/dist/two-d/render/ImageModifiers.js.map +1 -1
  193. package/dist/two-d/render/NeonGlow.d.ts.map +1 -1
  194. package/dist/two-d/render/NeonGlow.js +2 -1
  195. package/dist/two-d/render/NeonGlow.js.map +1 -1
  196. package/dist/two-d/render/Particles.d.ts.map +1 -1
  197. package/dist/two-d/render/Particles.js +2 -1
  198. package/dist/two-d/render/Particles.js.map +1 -1
  199. package/dist/two-d/render/SceneTransition.d.ts +29 -0
  200. package/dist/two-d/render/SceneTransition.d.ts.map +1 -1
  201. package/dist/two-d/render/SceneTransition.js +27 -0
  202. package/dist/two-d/render/SceneTransition.js.map +1 -1
  203. package/dist/two-d/render/ScreenEffects.d.ts.map +1 -1
  204. package/dist/two-d/render/ScreenEffects.js +2 -1
  205. package/dist/two-d/render/ScreenEffects.js.map +1 -1
  206. package/dist/two-d/render/SkeletalSprite.d.ts.map +1 -1
  207. package/dist/two-d/render/SkeletalSprite.js +2 -1
  208. package/dist/two-d/render/SkeletalSprite.js.map +1 -1
  209. package/dist/two-d/render/Types2D.d.ts +2 -2
  210. package/dist/two-d/render/Types2D.d.ts.map +1 -1
  211. package/dist/two-d/render/Types2D.js +2 -2
  212. package/dist/two-d/render/Types2D.js.map +1 -1
  213. package/dist/two-d/render/index.d.ts +2 -2
  214. package/dist/two-d/render/index.d.ts.map +1 -1
  215. package/dist/two-d/render/index.js +1 -1
  216. package/dist/two-d/render/index.js.map +1 -1
  217. package/dist/two-d/ui/Bar.d.ts.map +1 -1
  218. package/dist/two-d/ui/Bar.js +3 -5
  219. package/dist/two-d/ui/Bar.js.map +1 -1
  220. package/dist/two-d/ui/Dropdown.d.ts +5 -1
  221. package/dist/two-d/ui/Dropdown.d.ts.map +1 -1
  222. package/dist/two-d/ui/Dropdown.js +8 -1
  223. package/dist/two-d/ui/Dropdown.js.map +1 -1
  224. package/dist/two-d/ui/DropdownView.d.ts +15 -1
  225. package/dist/two-d/ui/DropdownView.d.ts.map +1 -1
  226. package/dist/two-d/ui/DropdownView.js +21 -3
  227. package/dist/two-d/ui/DropdownView.js.map +1 -1
  228. package/dist/two-d/ui/FloatingText.d.ts.map +1 -1
  229. package/dist/two-d/ui/FloatingText.js +2 -1
  230. package/dist/two-d/ui/FloatingText.js.map +1 -1
  231. package/dist/two-d/ui/IconGrid.d.ts.map +1 -1
  232. package/dist/two-d/ui/IconGrid.js +2 -1
  233. package/dist/two-d/ui/IconGrid.js.map +1 -1
  234. package/dist/two-d/ui/ListView.d.ts.map +1 -1
  235. package/dist/two-d/ui/ListView.js +2 -1
  236. package/dist/two-d/ui/ListView.js.map +1 -1
  237. package/dist/two-d/ui/ScrollBox.d.ts +6 -0
  238. package/dist/two-d/ui/ScrollBox.d.ts.map +1 -1
  239. package/dist/two-d/ui/ScrollBox.js +12 -1
  240. package/dist/two-d/ui/ScrollBox.js.map +1 -1
  241. package/dist/two-d/ui/ScrollText.d.ts +11 -2
  242. package/dist/two-d/ui/ScrollText.d.ts.map +1 -1
  243. package/dist/two-d/ui/ScrollText.js +18 -2
  244. package/dist/two-d/ui/ScrollText.js.map +1 -1
  245. package/dist/two-d/ui/Slider.d.ts.map +1 -1
  246. package/dist/two-d/ui/Slider.js +4 -4
  247. package/dist/two-d/ui/Slider.js.map +1 -1
  248. package/dist/version.d.ts +1 -1
  249. package/dist/version.js +1 -1
  250. package/package.json +8 -1
  251. package/sbom.cdx.json +6 -6
  252. package/tools/audio.d.mts +75 -0
  253. package/tools/audio.mjs +437 -0
  254. package/tools/mwl.mjs +144 -2
  255. package/tools.md +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Resource.d.ts","sourceRoot":"","sources":["../../src/actors/Resource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CACf;AAoBD,yFAAyF;AACzF,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,GAAG,SAAS,YAAY,EAAE,GAAG,OAAO,CAEjG;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,GAAG,SAAS,YAAY,EAAE,GAAG,OAAO,CAK7F;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,GAAG,SAAS,YAAY,EAAE,EAAE,QAAQ,SAAI,GAAG,IAAI,CAEzG;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7G"}
1
+ {"version":3,"file":"Resource.d.ts","sourceRoot":"","sources":["../../src/actors/Resource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CACf;AAoBD,yFAAyF;AACzF,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,GAAG,SAAS,YAAY,EAAE,GAAG,OAAO,CAEjG;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,GAAG,SAAS,YAAY,EAAE,GAAG,OAAO,CAI7F;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,GAAG,SAAS,YAAY,EAAE,EAAE,QAAQ,SAAI,GAAG,IAAI,CAEzG;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7G"}
@@ -26,10 +26,9 @@ export function canAfford(stats, cost) {
26
26
  * this returns false. A zero cost is always affordable and deducts nothing.
27
27
  */
28
28
  export function spend(stats, cost) {
29
- const combined = totals(all(cost));
30
- if (![...combined].every(([stat, amount]) => stats.base(stat) >= amount))
29
+ if (!canAfford(stats, cost))
31
30
  return false;
32
- for (const [stat, amount] of combined)
31
+ for (const [stat, amount] of totals(all(cost)))
33
32
  stats.setBase(stat, stats.base(stat) - amount);
34
33
  return true;
35
34
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Resource.js","sourceRoot":"","sources":["../../src/actors/Resource.ts"],"names":[],"mappings":"AA8BA,SAAS,GAAG,CAAC,IAA4C;IACxD,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACjD,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACtB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChD,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QACjG,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,qFAAqF;AACrF,qFAAqF;AACrF,SAAS,MAAM,CAAC,IAA6B;IAC5C,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtC,KAAK,MAAM,CAAC,IAAI,IAAI;QAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IACzE,OAAO,GAAG,CAAC;AACZ,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,SAAS,CAAC,KAAgB,EAAE,IAA4C;IACvF,OAAO,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,CAAC;AACrF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,KAAK,CAAC,KAAgB,EAAE,IAA4C;IACnF,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IACnC,IAAI,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IACvF,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,QAAQ;QAAE,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;IACtF,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,MAAM,CAAC,KAAgB,EAAE,IAA4C,EAAE,QAAQ,GAAG,CAAC;IAClG,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAAE,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAC;AAC3G,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAgB,EAAE,IAAkB,EAAE,OAAgB,EAAE,IAAY;IACpG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAClC,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;AACvD,CAAC"}
1
+ {"version":3,"file":"Resource.js","sourceRoot":"","sources":["../../src/actors/Resource.ts"],"names":[],"mappings":"AA8BA,SAAS,GAAG,CAAC,IAA4C;IACxD,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACjD,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACtB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChD,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QACjG,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,qFAAqF;AACrF,qFAAqF;AACrF,SAAS,MAAM,CAAC,IAA6B;IAC5C,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtC,KAAK,MAAM,CAAC,IAAI,IAAI;QAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC;IACzE,OAAO,GAAG,CAAC;AACZ,CAAC;AAED,yFAAyF;AACzF,MAAM,UAAU,SAAS,CAAC,KAAgB,EAAE,IAA4C;IACvF,OAAO,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,CAAC;AACrF,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,KAAK,CAAC,KAAgB,EAAE,IAA4C;IACnF,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAAE,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC;IAC/F,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,MAAM,CAAC,KAAgB,EAAE,IAA4C,EAAE,QAAQ,GAAG,CAAC;IAClG,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAAE,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAC;AAC3G,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAgB,EAAE,IAAkB,EAAE,OAAgB,EAAE,IAAY;IACpG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAClC,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;AACvD,CAAC"}
@@ -0,0 +1,85 @@
1
+ import type { LoopRegion } from './Channels.ts';
2
+ /** a set of stems that line up: same length, same rate, so they can be mixed and swapped in step */
3
+ export type StemSet = Readonly<Record<string, AudioBuffer>>;
4
+ export interface AdaptiveMusicOptions {
5
+ destination?: AudioNode;
6
+ /** seconds of fade for a level change or a version switch when the call gives none; 1 when omitted */
7
+ fadeSeconds?: number;
8
+ }
9
+ export interface AdaptiveLoadOptions {
10
+ /** names a group of stems so one call sets them all: `{ combat: ['drums', 'brass'] }` */
11
+ groups?: Readonly<Record<string, readonly string[]>>;
12
+ /** the region the whole set repeats; the whole buffer when omitted */
13
+ loop?: LoopRegion | null;
14
+ }
15
+ export interface AdaptivePlayOptions {
16
+ /** the version to start with; the first one loaded when omitted */
17
+ version?: string;
18
+ /** start offset in seconds */
19
+ pos?: number;
20
+ /** starting level of each stem or group, 0 to 1; a stem not named starts at 0 */
21
+ levels?: Readonly<Record<string, number>>;
22
+ }
23
+ /**
24
+ * Adaptive music from stems that play in lockstep: every stem of a version starts on the same
25
+ * clock tick at the same offset, each through its own gain, so raising or lowering a stem (or a
26
+ * named group of them) with a fade changes the intensity without changing the file or losing the
27
+ * beat. Several versions of the arrangement can be loaded; switching between them crossfades at
28
+ * the same position, counted in frames, so a calm version and a combat version stay in step. Stems
29
+ * come from `renderMidiStems` or from separately recorded layers; all stems of a version must have
30
+ * the same length and sample rate, and versions must agree on the sample rate.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * import { AdaptiveMusic, renderMidiStems, parseMidi, toAudioBuffer } from '@datamoc/mw_games/audio';
35
+ *
36
+ * declare const context: AudioContext;
37
+ * declare const midiBytes: ArrayBuffer;
38
+ *
39
+ * const rendered = renderMidiStems(parseMidi(midiBytes), { rhythm: [9], melody: [0], pads: [1] });
40
+ * const stems = Object.fromEntries(Object.entries(rendered).map(([name, r]) => [name, toAudioBuffer(context, r)]));
41
+ *
42
+ * const music = new AdaptiveMusic(context);
43
+ * music.load('calm', stems, { groups: { intensity: ['rhythm', 'melody'] } });
44
+ * music.play({ levels: { pads: 1 } });
45
+ * music.setLevel('intensity', 1, 2); // fade the fight in over two seconds
46
+ * ```
47
+ */
48
+ export declare class AdaptiveMusic {
49
+ private readonly context;
50
+ private readonly destination;
51
+ private readonly defaultFade;
52
+ private readonly versions;
53
+ private running;
54
+ private startedAt;
55
+ private offsetFrames;
56
+ private levels_;
57
+ constructor(context: AudioContext, options?: AdaptiveMusicOptions);
58
+ get isPlaying(): boolean;
59
+ /** the version that sounds now, or null */
60
+ get version(): string | null;
61
+ /** loads one arrangement under a name; throws when its stems do not line up */
62
+ load(name: string, stems: StemSet, options?: AdaptiveLoadOptions): void;
63
+ /** starts the set from the beginning, or from `pos` */
64
+ play(options?: AdaptivePlayOptions): void;
65
+ /** sets a stem's or group's level (0 to 1), fading over `fadeSeconds` */
66
+ setLevel(target: string, level: number, fadeSeconds?: number): void;
67
+ /** the level each stem is heading to */
68
+ levels(): Record<string, number>;
69
+ /** frame position inside the buffers, wrapping inside the loop region */
70
+ positionFrames(): number;
71
+ position(): number;
72
+ /**
73
+ * Crossfades to another loaded version at the position the current one has reached, in
74
+ * frames, so the beat carries over. Levels carry over too: a stem or group of the same name
75
+ * keeps the level it had.
76
+ */
77
+ switchVersion(name: string, fadeSeconds?: number): void;
78
+ /** stops every stem, fading over `fadeSeconds` first when positive; the position is forgotten */
79
+ stop(fadeSeconds?: number): void;
80
+ private startVersion;
81
+ private membersOf;
82
+ private ramp;
83
+ private stopAt;
84
+ }
85
+ //# sourceMappingURL=AdaptiveMusic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdaptiveMusic.d.ts","sourceRoot":"","sources":["../../src/audio/AdaptiveMusic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,oGAAoG;AACpG,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;AAE5D,MAAM,WAAW,oBAAoB;IACpC,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,sGAAsG;IACtG,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IACnC,yFAAyF;IACzF,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;IACrD,sEAAsE;IACtE,IAAI,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IACnC,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC1C;AAqBD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,aAAa;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAY;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8B;IACvD,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,OAAO,CAA8B;IAE7C,YAAY,OAAO,EAAE,YAAY,EAAE,OAAO,GAAE,oBAAyB,EAIpE;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,2CAA2C;IAC3C,IAAI,OAAO,IAAI,MAAM,GAAG,IAAI,CAE3B;IAED,+EAA+E;IAC/E,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAE,mBAAwB,GAAG,IAAI,CA8B1E;IAED,uDAAuD;IACvD,IAAI,CAAC,OAAO,GAAE,mBAAwB,GAAG,IAAI,CAU5C;IAED,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAYlE;IAED,wCAAwC;IACxC,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAE/B;IAED,yEAAyE;IACzE,cAAc,IAAI,MAAM,CASvB;IAED,QAAQ,IAAI,MAAM,CAGjB;IAED;;;;OAIG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAetD;IAED,iGAAiG;IACjG,IAAI,CAAC,WAAW,SAAI,GAAG,IAAI,CAS1B;IAED,OAAO,CAAC,YAAY;IAyCpB,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,IAAI;IAWZ,OAAO,CAAC,MAAM;CAOd"}
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Adaptive music from stems that play in lockstep: every stem of a version starts on the same
3
+ * clock tick at the same offset, each through its own gain, so raising or lowering a stem (or a
4
+ * named group of them) with a fade changes the intensity without changing the file or losing the
5
+ * beat. Several versions of the arrangement can be loaded; switching between them crossfades at
6
+ * the same position, counted in frames, so a calm version and a combat version stay in step. Stems
7
+ * come from `renderMidiStems` or from separately recorded layers; all stems of a version must have
8
+ * the same length and sample rate, and versions must agree on the sample rate.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { AdaptiveMusic, renderMidiStems, parseMidi, toAudioBuffer } from '@datamoc/mw_games/audio';
13
+ *
14
+ * declare const context: AudioContext;
15
+ * declare const midiBytes: ArrayBuffer;
16
+ *
17
+ * const rendered = renderMidiStems(parseMidi(midiBytes), { rhythm: [9], melody: [0], pads: [1] });
18
+ * const stems = Object.fromEntries(Object.entries(rendered).map(([name, r]) => [name, toAudioBuffer(context, r)]));
19
+ *
20
+ * const music = new AdaptiveMusic(context);
21
+ * music.load('calm', stems, { groups: { intensity: ['rhythm', 'melody'] } });
22
+ * music.play({ levels: { pads: 1 } });
23
+ * music.setLevel('intensity', 1, 2); // fade the fight in over two seconds
24
+ * ```
25
+ */
26
+ export class AdaptiveMusic {
27
+ constructor(context, options = {}) {
28
+ this.versions = new Map();
29
+ this.running = null;
30
+ this.startedAt = 0;
31
+ this.offsetFrames = 0;
32
+ this.levels_ = {};
33
+ this.context = context;
34
+ this.destination = options.destination ?? context.destination;
35
+ this.defaultFade = Math.max(0, options.fadeSeconds ?? 1);
36
+ }
37
+ get isPlaying() {
38
+ return this.running !== null;
39
+ }
40
+ /** the version that sounds now, or null */
41
+ get version() {
42
+ return this.running?.version ?? null;
43
+ }
44
+ /** loads one arrangement under a name; throws when its stems do not line up */
45
+ load(name, stems, options = {}) {
46
+ const names = Object.keys(stems);
47
+ if (names.length === 0)
48
+ throw new Error(`adaptive version "${name}" has no stems`);
49
+ const first = stems[names[0]];
50
+ for (const stem of names) {
51
+ const buffer = stems[stem];
52
+ if (buffer.length !== first.length || buffer.sampleRate !== first.sampleRate) {
53
+ throw new Error(`stem "${stem}" of "${name}" differs in length or sample rate from "${names[0]}"`);
54
+ }
55
+ }
56
+ const other = [...this.versions.values()][0];
57
+ if (other && other.sampleRate !== first.sampleRate) {
58
+ throw new Error(`version "${name}" is at ${first.sampleRate} Hz but the others are at ${other.sampleRate} Hz`);
59
+ }
60
+ const groups = options.groups ?? {};
61
+ for (const [group, members] of Object.entries(groups)) {
62
+ const missing = members.find((member) => !(member in stems));
63
+ if (missing !== undefined)
64
+ throw new Error(`group "${group}" names stem "${missing}", which "${name}" lacks`);
65
+ if (group in stems)
66
+ throw new Error(`group "${group}" has the name of a stem`);
67
+ }
68
+ this.versions.set(name, {
69
+ stems,
70
+ groups,
71
+ loop: options.loop ?? null,
72
+ frames: first.length,
73
+ sampleRate: first.sampleRate,
74
+ });
75
+ }
76
+ /** starts the set from the beginning, or from `pos` */
77
+ play(options = {}) {
78
+ this.stop();
79
+ const name = options.version ?? [...this.versions.keys()][0];
80
+ const version = name === undefined ? undefined : this.versions.get(name);
81
+ if (!version || name === undefined)
82
+ throw new Error(`no adaptive version "${options.version ?? ''}" is loaded`);
83
+ this.levels_ = {};
84
+ const offset = Math.min(Math.max(0, Math.floor((options.pos ?? 0) * version.sampleRate)), version.frames - 1);
85
+ this.offsetFrames = offset;
86
+ this.startedAt = this.context.currentTime;
87
+ this.running = this.startVersion(name, version, offset, options.levels ?? {}, 0);
88
+ }
89
+ /** sets a stem's or group's level (0 to 1), fading over `fadeSeconds` */
90
+ setLevel(target, level, fadeSeconds) {
91
+ const running = this.running;
92
+ if (!running)
93
+ throw new Error('adaptive music is not playing');
94
+ const version = this.versions.get(running.version);
95
+ const members = this.membersOf(version, target);
96
+ const value = Math.min(1, Math.max(0, level));
97
+ const fade = Math.max(0, fadeSeconds ?? this.defaultFade);
98
+ for (const member of members) {
99
+ this.levels_[member] = value;
100
+ const gain = running.voices.get(member)?.gain.gain;
101
+ if (gain)
102
+ this.ramp(gain, value, fade);
103
+ }
104
+ }
105
+ /** the level each stem is heading to */
106
+ levels() {
107
+ return { ...this.levels_ };
108
+ }
109
+ /** frame position inside the buffers, wrapping inside the loop region */
110
+ positionFrames() {
111
+ const running = this.running;
112
+ if (!running)
113
+ return 0;
114
+ const version = this.versions.get(running.version);
115
+ let frame = this.offsetFrames + Math.max(0, this.context.currentTime - this.startedAt) * version.sampleRate;
116
+ const start = version.loop ? Math.floor(version.loop.start * version.sampleRate) : 0;
117
+ const end = version.loop ? Math.floor(version.loop.end * version.sampleRate) : version.frames;
118
+ if (end > start && frame >= end)
119
+ frame = start + ((frame - start) % (end - start));
120
+ return Math.floor(frame);
121
+ }
122
+ position() {
123
+ const running = this.running;
124
+ return running ? this.positionFrames() / this.versions.get(running.version).sampleRate : 0;
125
+ }
126
+ /**
127
+ * Crossfades to another loaded version at the position the current one has reached, in
128
+ * frames, so the beat carries over. Levels carry over too: a stem or group of the same name
129
+ * keeps the level it had.
130
+ */
131
+ switchVersion(name, fadeSeconds) {
132
+ const running = this.running;
133
+ if (!running)
134
+ throw new Error('adaptive music is not playing');
135
+ const next = this.versions.get(name);
136
+ if (!next)
137
+ throw new Error(`no adaptive version "${name}" is loaded`);
138
+ if (name === running.version)
139
+ return;
140
+ const fade = Math.max(0, fadeSeconds ?? this.defaultFade);
141
+ const frame = this.positionFrames();
142
+ const retired = running;
143
+ const now = this.context.currentTime;
144
+ this.ramp(retired.master.gain, 0, fade);
145
+ for (const { source } of retired.voices.values())
146
+ this.stopAt(source, now + fade + 0.05);
147
+ this.offsetFrames = Math.min(frame, next.frames - 1);
148
+ this.startedAt = now;
149
+ this.running = this.startVersion(name, next, this.offsetFrames, this.levels_, fade);
150
+ }
151
+ /** stops every stem, fading over `fadeSeconds` first when positive; the position is forgotten */
152
+ stop(fadeSeconds = 0) {
153
+ const running = this.running;
154
+ this.running = null;
155
+ this.levels_ = {};
156
+ if (!running)
157
+ return;
158
+ const now = this.context.currentTime;
159
+ if (fadeSeconds > 0)
160
+ this.ramp(running.master.gain, 0, fadeSeconds);
161
+ for (const { source } of running.voices.values())
162
+ this.stopAt(source, fadeSeconds > 0 ? now + fadeSeconds + 0.05 : undefined);
163
+ }
164
+ startVersion(name, version, offsetFrames, levels, fadeInSeconds) {
165
+ const master = this.context.createGain();
166
+ master.gain.value = fadeInSeconds > 0 ? 0 : 1;
167
+ master.connect(this.destination);
168
+ if (fadeInSeconds > 0)
169
+ this.ramp(master.gain, 1, fadeInSeconds);
170
+ const voices = new Map();
171
+ const when = this.context.currentTime;
172
+ const offset = offsetFrames / version.sampleRate;
173
+ //levels given by group name apply to every member; a stem named directly overrides its group
174
+ const resolved = {};
175
+ for (const [target, level] of Object.entries(levels)) {
176
+ if (target in version.groups)
177
+ for (const member of version.groups[target])
178
+ resolved[member] = level;
179
+ }
180
+ for (const [target, level] of Object.entries(levels))
181
+ if (target in version.stems)
182
+ resolved[target] = level;
183
+ for (const [stem, buffer] of Object.entries(version.stems)) {
184
+ const gain = this.context.createGain();
185
+ const level = Math.min(1, Math.max(0, resolved[stem] ?? 0));
186
+ gain.gain.value = level;
187
+ this.levels_[stem] = level;
188
+ gain.connect(master);
189
+ const source = this.context.createBufferSource();
190
+ source.buffer = buffer;
191
+ source.loop = true;
192
+ if (version.loop) {
193
+ source.loopStart = version.loop.start;
194
+ source.loopEnd = version.loop.end;
195
+ }
196
+ source.connect(gain);
197
+ //every stem on the same tick at the same offset is what keeps them in step
198
+ source.start(when, offset);
199
+ voices.set(stem, { source, gain });
200
+ }
201
+ return { version: name, voices, master };
202
+ }
203
+ membersOf(version, target) {
204
+ if (target in version.stems)
205
+ return [target];
206
+ const group = version.groups[target];
207
+ if (!group)
208
+ throw new Error(`"${target}" is neither a stem nor a group of "${this.running?.version}"`);
209
+ return group;
210
+ }
211
+ ramp(param, value, seconds) {
212
+ if (seconds <= 0) {
213
+ param.value = value;
214
+ return;
215
+ }
216
+ const now = this.context.currentTime;
217
+ param.cancelScheduledValues(now);
218
+ param.setValueAtTime(param.value, now);
219
+ param.linearRampToValueAtTime(value, now + seconds);
220
+ }
221
+ stopAt(source, when) {
222
+ try {
223
+ source.stop(when);
224
+ }
225
+ catch {
226
+ //already stopped
227
+ }
228
+ }
229
+ }
230
+ //# sourceMappingURL=AdaptiveMusic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdaptiveMusic.js","sourceRoot":"","sources":["../../src/audio/AdaptiveMusic.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { type BusBudgetPolicy } from './BusBudget.ts';
1
2
  import type { BusLoader } from './Channels.ts';
2
3
  import { type RenderMidiAsyncOptions } from './MidiRender.ts';
3
4
  export interface AudioLoaderOptions {
@@ -5,12 +6,16 @@ export interface AudioLoaderOptions {
5
6
  midi?: RenderMidiAsyncOptions;
6
7
  /** Reads embedded assets by default; inject a byte source for a custom archive or a test. */
7
8
  read?: (path: string) => Promise<ArrayBuffer>;
9
+ /** keeps decoded tracks inside a memory budget, evicting the least recently used; every track stays cached when omitted */
10
+ budget?: BusBudgetPolicy;
8
11
  }
9
12
  /**
10
13
  * One loader for `AudioBus` music, jingles and effects: MIDI is rendered on a worker,
11
14
  * other formats use the browser's audio decoder. MIDI detection uses the file header,
12
- * so an archive alias does not need a filename extension. Results are cached by path;
13
- * a failed load can be retried. Returned buffers can also be used by a `MusicPlayer`.
15
+ * so an archive alias does not need a filename extension. Results are cached by path
16
+ * (under `budget` when one is given, which also caps and shrinks rendered MIDI); a failed
17
+ * load can be retried. A load reports 0, 0.5 and 1 through the bus's progress context, and
18
+ * skips its render when the request was replaced before the bytes arrived. Returned buffers can also be used by a `MusicPlayer`.
14
19
  *
15
20
  * @example
16
21
  * ```ts
@@ -1 +1 @@
1
- {"version":3,"file":"AudioLoader.d.ts","sourceRoot":"","sources":["../../src/audio/AudioLoader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,eAAe,CAAC;AAC5D,OAAO,EAA0C,KAAK,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEtG,MAAM,WAAW,kBAAkB;IAClC,mEAAmE;IACnE,IAAI,CAAC,EAAE,sBAAsB,CAAC;IAC9B,6FAA6F;IAC7F,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;CAC9C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,GAAE,kBAAuB,GAAG,SAAS,CA2BxG"}
1
+ {"version":3,"file":"AudioLoader.d.ts","sourceRoot":"","sources":["../../src/audio/AudioLoader.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,KAAK,EAAkB,SAAS,EAAe,MAAM,eAAe,CAAC;AAC5E,OAAO,EAA0C,KAAK,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAGtG,MAAM,WAAW,kBAAkB;IAClC,mEAAmE;IACnE,IAAI,CAAC,EAAE,sBAAsB,CAAC;IAC9B,6FAA6F;IAC7F,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC9C,2HAA2H;IAC3H,MAAM,CAAC,EAAE,eAAe,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,GAAE,kBAAuB,GAAG,SAAS,CAyBxG"}
@@ -1,10 +1,14 @@
1
1
  import { getBinary, loadBinary } from '../assets/binary.js';
2
+ import { TrackCache } from './BusBudget.js';
2
3
  import { renderMidiToBufferAsync, toAudioBuffer } from './MidiRender.js';
4
+ import { fitToBudget } from './Resample.js';
3
5
  /**
4
6
  * One loader for `AudioBus` music, jingles and effects: MIDI is rendered on a worker,
5
7
  * other formats use the browser's audio decoder. MIDI detection uses the file header,
6
- * so an archive alias does not need a filename extension. Results are cached by path;
7
- * a failed load can be retried. Returned buffers can also be used by a `MusicPlayer`.
8
+ * so an archive alias does not need a filename extension. Results are cached by path
9
+ * (under `budget` when one is given, which also caps and shrinks rendered MIDI); a failed
10
+ * load can be retried. A load reports 0, 0.5 and 1 through the bus's progress context, and
11
+ * skips its render when the request was replaced before the bytes arrived. Returned buffers can also be used by a `MusicPlayer`.
8
12
  *
9
13
  * @example
10
14
  * ```ts
@@ -19,32 +23,33 @@ import { renderMidiToBufferAsync, toAudioBuffer } from './MidiRender.js';
19
23
  * ```
20
24
  */
21
25
  export function createAudioLoader(context, options = {}) {
22
- const pending = new Map();
26
+ const cache = new TrackCache(options.budget);
23
27
  const read = options.read ?? readAsset;
24
- async function decode(path) {
28
+ async function decode(path, loadContext) {
29
+ loadContext?.progress(0);
25
30
  const bytes = await read(path);
31
+ if (loadContext?.signal.aborted)
32
+ return null;
33
+ loadContext?.progress(0.5);
26
34
  const header = new Uint8Array(bytes, 0, Math.min(4, bytes.byteLength));
35
+ let loaded;
27
36
  if (header[0] === 77 && header[1] === 84 && header[2] === 104 && header[3] === 100) {
28
- const rendered = await renderMidiToBufferAsync(bytes, options.midi);
29
- return {
37
+ let rendered = await renderMidiToBufferAsync(bytes, options.midi);
38
+ if (options.budget?.render)
39
+ rendered = fitToBudget(rendered, options.budget.render);
40
+ loaded = {
30
41
  buffer: toAudioBuffer(context, rendered),
31
42
  loop: { start: rendered.loopStart, end: rendered.loopEnd },
32
43
  };
33
44
  }
34
- // decodeAudioData can detach its input; the asset cache still owns the original bytes.
35
- return { buffer: await context.decodeAudioData(bytes.slice(0)) };
36
- }
37
- return (_kind, path) => {
38
- let loaded = pending.get(path);
39
- if (!loaded) {
40
- loaded = decode(path).catch((error) => {
41
- pending.delete(path);
42
- throw error;
43
- });
44
- pending.set(path, loaded);
45
+ else {
46
+ // decodeAudioData can detach its input; the asset cache still owns the original bytes.
47
+ loaded = { buffer: await context.decodeAudioData(bytes.slice(0)) };
45
48
  }
49
+ loadContext?.progress(1);
46
50
  return loaded;
47
- };
51
+ }
52
+ return (_kind, path, loadContext) => cache.load(path, () => decode(path, loadContext));
48
53
  }
49
54
  async function readAsset(path) {
50
55
  await loadBinary([path]);
@@ -1 +1 @@
1
- {"version":3,"file":"AudioLoader.js","sourceRoot":"","sources":["../../src/audio/AudioLoader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAE5D,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAA+B,MAAM,iBAAiB,CAAC;AAStG;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAyB,EAAE,OAAO,GAAuB,EAAE;IAC5F,MAAM,OAAO,GAAG,IAAI,GAAG,EAAgC,CAAC;IACxD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,SAAS,CAAC;IACvC,KAAK,UAAU,MAAM,CAAC,IAAY;QACjC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;QACvE,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACpF,MAAM,QAAQ,GAAG,MAAM,uBAAuB,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;YACpE,OAAO;gBACN,MAAM,EAAE,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC;gBACxC,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE,QAAQ,CAAC,OAAO,EAAE;aAC1D,CAAC;QACH,CAAC;QACD,uFAAuF;QACvF,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAClE,CAAC;IACD,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACtB,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;gBAC9C,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;gBACrB,MAAM,KAAK,CAAC;YACb,CAAC,CAAC,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3B,CAAC;QACD,OAAO,MAAM,CAAC;IACf,CAAC,CAAC;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACzB,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC"}
1
+ {"version":3,"file":"AudioLoader.js","sourceRoot":"","sources":["../../src/audio/AudioLoader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAwB,MAAM,gBAAgB,CAAC;AAElE,OAAO,EAAE,uBAAuB,EAAE,aAAa,EAA+B,MAAM,iBAAiB,CAAC;AACtG,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAW5C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAyB,EAAE,OAAO,GAAuB,EAAE;IAC5F,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,SAAS,CAAC;IACvC,KAAK,UAAU,MAAM,CAAC,IAAY,EAAE,WAA4B;QAC/D,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;QACzB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,WAAW,EAAE,MAAM,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAC7C,WAAW,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC3B,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;QACvE,IAAI,MAAmB,CAAC;QACxB,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YACpF,IAAI,QAAQ,GAAG,MAAM,uBAAuB,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;YAClE,IAAI,OAAO,CAAC,MAAM,EAAE,MAAM;gBAAE,QAAQ,GAAG,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACpF,MAAM,GAAG;gBACR,MAAM,EAAE,aAAa,CAAC,OAAO,EAAE,QAAQ,CAAC;gBACxC,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE,QAAQ,CAAC,OAAO,EAAE;aAC1D,CAAC;QACH,CAAC;aAAM,CAAC;YACP,uFAAuF;YACvF,MAAM,GAAG,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,CAAC;QACD,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;QACzB,OAAO,MAAM,CAAC;IACf,CAAC;IACD,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;AACxF,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACzB,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { type MidiFile } from './Midi.ts';
2
+ /** one bucket of a waveform view: the lowest and highest sample it covers */
3
+ export interface WaveformBucket {
4
+ min: number;
5
+ max: number;
6
+ }
7
+ /**
8
+ * A waveform reduced to `buckets` min/max pairs, what an oscilloscope or overview strip
9
+ * draws: each bucket covers an equal span of the samples (the last may be shorter), so a
10
+ * peak that lasts one sample still shows, which a plain average would erase.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { waveformEnvelope } from '@datamoc/mw_games/audio';
15
+ *
16
+ * const view = waveformEnvelope(new Float32Array([0, 1, -1, 0.5]), 2);
17
+ * console.log(view); // [{ min: 0, max: 1 }, { min: -1, max: 0.5 }]
18
+ * ```
19
+ */
20
+ export declare function waveformEnvelope(samples: Float32Array, buckets: number): WaveformBucket[];
21
+ /**
22
+ * Per-channel waveform envelopes of stereo frames: channel 0 is the left, 1 the right,
23
+ * and a view asks each channel for the same number of buckets.
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * import { channelEnvelopes } from '@datamoc/mw_games/audio';
28
+ *
29
+ * const [left] = channelEnvelopes(new Float32Array([1, -1]), new Float32Array([0.5, -0.5]), 1);
30
+ * console.log(left); // [{ min: -1, max: 1 }]
31
+ * ```
32
+ */
33
+ export declare function channelEnvelopes(left: Float32Array, right: Float32Array, buckets: number): [WaveformBucket[], WaveformBucket[]];
34
+ /**
35
+ * A MIDI note number as a tracker spells it: name and octave, middle C as C4.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * import { trackerNoteName } from '@datamoc/mw_games/audio';
40
+ *
41
+ * console.log(trackerNoteName(60)); // 'C4'
42
+ * console.log(trackerNoteName(61)); // 'C#4'
43
+ * ```
44
+ */
45
+ export declare function trackerNoteName(note: number): string;
46
+ /** one row of a tracker pattern: its step, and the note each channel starts on it (null for none) */
47
+ export interface TrackerRow {
48
+ step: number;
49
+ /** seconds from the start, of the first note on the row (the previous note's time for an empty row) */
50
+ time: number;
51
+ notes: (string | null)[];
52
+ }
53
+ /**
54
+ * A MIDI file as tracker pattern rows: `stepsPerQuarter` rows per quarter note, one
55
+ * column per channel that plays, and a cell naming the note that starts on that row
56
+ * (the highest when a channel starts several at once, as a tracker cell holds one).
57
+ * Steps are counted in the file's own ticks, and a row's time follows its tempo map.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * import { trackerPattern, type MidiFile } from '@datamoc/mw_games/audio';
62
+ *
63
+ * declare const file: MidiFile;
64
+ *
65
+ * const rows = trackerPattern(file);
66
+ * console.log(rows[0].notes); // the first step's notes by channel, or nulls
67
+ * ```
68
+ */
69
+ export declare function trackerPattern(file: MidiFile, stepsPerQuarter?: number): TrackerRow[];
70
+ //# sourceMappingURL=AudioViews.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioViews.d.ts","sourceRoot":"","sources":["../../src/audio/AudioViews.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,QAAQ,EAAsB,MAAM,WAAW,CAAC;AAEjF,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CAezF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC/B,IAAI,EAAE,YAAY,EAClB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,MAAM,GACb,CAAC,cAAc,EAAE,EAAE,cAAc,EAAE,CAAC,CAEtC;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,qGAAqG;AACrG,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,uGAAuG;IACvG,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;CACzB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,eAAe,SAAI,GAAG,UAAU,EAAE,CA8BhF"}