@datamoc/mw_games 0.42.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 (333) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/README.md +84 -1
  3. package/REFERENCE.md +134 -6
  4. package/api-surface.json +1814 -116
  5. package/data/microgm-essential.PROVENANCE.md +13 -0
  6. package/data/microgm-essential.sf2 +0 -0
  7. package/data/soundfont-catalog.json +8 -0
  8. package/dist/actors/Resource.d.ts.map +1 -1
  9. package/dist/actors/Resource.js +2 -3
  10. package/dist/actors/Resource.js.map +1 -1
  11. package/dist/audio/AdaptiveMusic.d.ts +85 -0
  12. package/dist/audio/AdaptiveMusic.d.ts.map +1 -0
  13. package/dist/audio/AdaptiveMusic.js +230 -0
  14. package/dist/audio/AdaptiveMusic.js.map +1 -0
  15. package/dist/audio/AudioLoader.d.ts +33 -0
  16. package/dist/audio/AudioLoader.d.ts.map +1 -0
  17. package/dist/audio/AudioLoader.js +58 -0
  18. package/dist/audio/AudioLoader.js.map +1 -0
  19. package/dist/audio/AudioViews.d.ts +70 -0
  20. package/dist/audio/AudioViews.d.ts.map +1 -0
  21. package/dist/audio/AudioViews.js +119 -0
  22. package/dist/audio/AudioViews.js.map +1 -0
  23. package/dist/audio/BusBudget.d.ts +89 -0
  24. package/dist/audio/BusBudget.d.ts.map +1 -0
  25. package/dist/audio/BusBudget.js +120 -0
  26. package/dist/audio/BusBudget.js.map +1 -0
  27. package/dist/audio/Channels.d.ts +66 -1
  28. package/dist/audio/Channels.d.ts.map +1 -1
  29. package/dist/audio/Channels.js +119 -31
  30. package/dist/audio/Channels.js.map +1 -1
  31. package/dist/audio/CoreHost.d.ts +114 -0
  32. package/dist/audio/CoreHost.d.ts.map +1 -0
  33. package/dist/audio/CoreHost.js +335 -0
  34. package/dist/audio/CoreHost.js.map +1 -0
  35. package/dist/audio/DecoderGuard.d.ts +139 -0
  36. package/dist/audio/DecoderGuard.d.ts.map +1 -0
  37. package/dist/audio/DecoderGuard.js +208 -0
  38. package/dist/audio/DecoderGuard.js.map +1 -0
  39. package/dist/audio/Fft.d.ts +3 -0
  40. package/dist/audio/Fft.d.ts.map +1 -0
  41. package/dist/audio/Fft.js +45 -0
  42. package/dist/audio/Fft.js.map +1 -0
  43. package/dist/audio/LoopTags.d.ts +18 -0
  44. package/dist/audio/LoopTags.d.ts.map +1 -1
  45. package/dist/audio/LoopTags.js +20 -0
  46. package/dist/audio/LoopTags.js.map +1 -1
  47. package/dist/audio/Midi.d.ts +35 -0
  48. package/dist/audio/Midi.d.ts.map +1 -1
  49. package/dist/audio/Midi.js +71 -1
  50. package/dist/audio/Midi.js.map +1 -1
  51. package/dist/audio/MidiRender.d.ts +47 -0
  52. package/dist/audio/MidiRender.d.ts.map +1 -1
  53. package/dist/audio/MidiRender.js +100 -2
  54. package/dist/audio/MidiRender.js.map +1 -1
  55. package/dist/audio/MidiSynth.d.ts +147 -0
  56. package/dist/audio/MidiSynth.d.ts.map +1 -0
  57. package/dist/audio/MidiSynth.js +205 -0
  58. package/dist/audio/MidiSynth.js.map +1 -0
  59. package/dist/audio/MusicPlayer.d.ts +80 -0
  60. package/dist/audio/MusicPlayer.d.ts.map +1 -0
  61. package/dist/audio/MusicPlayer.js +147 -0
  62. package/dist/audio/MusicPlayer.js.map +1 -0
  63. package/dist/audio/Playable.d.ts +7 -0
  64. package/dist/audio/Playable.d.ts.map +1 -1
  65. package/dist/audio/Playable.js.map +1 -1
  66. package/dist/audio/Positional.d.ts +12 -4
  67. package/dist/audio/Positional.d.ts.map +1 -1
  68. package/dist/audio/Positional.js +5 -2
  69. package/dist/audio/Positional.js.map +1 -1
  70. package/dist/audio/Resample.d.ts +61 -0
  71. package/dist/audio/Resample.d.ts.map +1 -0
  72. package/dist/audio/Resample.js +97 -0
  73. package/dist/audio/Resample.js.map +1 -0
  74. package/dist/audio/SampleDecoder.d.ts +16 -2
  75. package/dist/audio/SampleDecoder.d.ts.map +1 -1
  76. package/dist/audio/SampleDecoder.js +10 -6
  77. package/dist/audio/SampleDecoder.js.map +1 -1
  78. package/dist/audio/Settings.d.ts +99 -0
  79. package/dist/audio/Settings.d.ts.map +1 -0
  80. package/dist/audio/Settings.js +134 -0
  81. package/dist/audio/Settings.js.map +1 -0
  82. package/dist/audio/Sfx.d.ts +148 -0
  83. package/dist/audio/Sfx.d.ts.map +1 -0
  84. package/dist/audio/Sfx.js +213 -0
  85. package/dist/audio/Sfx.js.map +1 -0
  86. package/dist/audio/Sound.d.ts +2 -0
  87. package/dist/audio/Sound.d.ts.map +1 -1
  88. package/dist/audio/Sound.js +3 -0
  89. package/dist/audio/Sound.js.map +1 -1
  90. package/dist/audio/SoundFont.d.ts +5 -3
  91. package/dist/audio/SoundFont.d.ts.map +1 -1
  92. package/dist/audio/SoundFont.js +10 -8
  93. package/dist/audio/SoundFont.js.map +1 -1
  94. package/dist/audio/SpectralDistance.d.ts +34 -0
  95. package/dist/audio/SpectralDistance.d.ts.map +1 -0
  96. package/dist/audio/SpectralDistance.js +64 -0
  97. package/dist/audio/SpectralDistance.js.map +1 -0
  98. package/dist/audio/StreamChannel.d.ts +150 -0
  99. package/dist/audio/StreamChannel.d.ts.map +1 -0
  100. package/dist/audio/StreamChannel.js +390 -0
  101. package/dist/audio/StreamChannel.js.map +1 -0
  102. package/dist/audio/StreamPlayable.d.ts +73 -0
  103. package/dist/audio/StreamPlayable.d.ts.map +1 -0
  104. package/dist/audio/StreamPlayable.js +130 -0
  105. package/dist/audio/StreamPlayable.js.map +1 -0
  106. package/dist/audio/Synth.d.ts +1 -1
  107. package/dist/audio/Synth.d.ts.map +1 -1
  108. package/dist/audio/Synth.js +4 -0
  109. package/dist/audio/Synth.js.map +1 -1
  110. package/dist/audio/SyxBank.d.ts +73 -0
  111. package/dist/audio/SyxBank.d.ts.map +1 -0
  112. package/dist/audio/SyxBank.js +130 -0
  113. package/dist/audio/SyxBank.js.map +1 -0
  114. package/dist/audio/Underrun.d.ts +25 -0
  115. package/dist/audio/Underrun.d.ts.map +1 -0
  116. package/dist/audio/Underrun.js +18 -0
  117. package/dist/audio/Underrun.js.map +1 -0
  118. package/dist/audio/Wav.d.ts +56 -0
  119. package/dist/audio/Wav.d.ts.map +1 -0
  120. package/dist/audio/Wav.js +164 -0
  121. package/dist/audio/Wav.js.map +1 -0
  122. package/dist/audio/WebMidi.d.ts +58 -0
  123. package/dist/audio/WebMidi.d.ts.map +1 -0
  124. package/dist/audio/WebMidi.js +79 -0
  125. package/dist/audio/WebMidi.js.map +1 -0
  126. package/dist/audio/humanize/Onsets.d.ts.map +1 -1
  127. package/dist/audio/humanize/Onsets.js +1 -44
  128. package/dist/audio/humanize/Onsets.js.map +1 -1
  129. package/dist/audio/index.d.ts +43 -7
  130. package/dist/audio/index.d.ts.map +1 -1
  131. package/dist/audio/index.js +21 -3
  132. package/dist/audio/index.js.map +1 -1
  133. package/dist/battle/AttackPreview.d.ts.map +1 -1
  134. package/dist/battle/AttackPreview.js +8 -8
  135. package/dist/battle/AttackPreview.js.map +1 -1
  136. package/dist/battle/StatStages.d.ts.map +1 -1
  137. package/dist/battle/StatStages.js +2 -1
  138. package/dist/battle/StatStages.js.map +1 -1
  139. package/dist/board/Engine.d.ts +2 -0
  140. package/dist/board/Engine.d.ts.map +1 -1
  141. package/dist/board/Engine.js +1 -1
  142. package/dist/board/Engine.js.map +1 -1
  143. package/dist/board/HexSkirmish.d.ts.map +1 -1
  144. package/dist/board/HexSkirmish.js +11 -72
  145. package/dist/board/HexSkirmish.js.map +1 -1
  146. package/dist/board/Tactics.d.ts.map +1 -1
  147. package/dist/board/Tactics.js +3 -5
  148. package/dist/board/Tactics.js.map +1 -1
  149. package/dist/board/tourney.d.ts.map +1 -1
  150. package/dist/board/tourney.js +2 -19
  151. package/dist/board/tourney.js.map +1 -1
  152. package/dist/core/Handles.js +7 -7
  153. package/dist/core/Handles.js.map +1 -1
  154. package/dist/mw_games.global.js +25 -22
  155. package/dist/mwl/conditions.d.ts +12 -0
  156. package/dist/mwl/conditions.d.ts.map +1 -1
  157. package/dist/mwl/conditions.js +49 -8
  158. package/dist/mwl/conditions.js.map +1 -1
  159. package/dist/mwl/index.d.ts +13 -0
  160. package/dist/mwl/index.d.ts.map +1 -1
  161. package/dist/mwl/index.js +7 -0
  162. package/dist/mwl/index.js.map +1 -1
  163. package/dist/mwl/objects.d.ts +31 -0
  164. package/dist/mwl/objects.d.ts.map +1 -0
  165. package/dist/mwl/objects.js +83 -0
  166. package/dist/mwl/objects.js.map +1 -0
  167. package/dist/mwl/package-container.d.ts +185 -0
  168. package/dist/mwl/package-container.d.ts.map +1 -0
  169. package/dist/mwl/package-container.js +446 -0
  170. package/dist/mwl/package-container.js.map +1 -0
  171. package/dist/mwl/package-document.d.ts +217 -0
  172. package/dist/mwl/package-document.d.ts.map +1 -0
  173. package/dist/mwl/package-document.js +639 -0
  174. package/dist/mwl/package-document.js.map +1 -0
  175. package/dist/mwl/package-rom.d.ts +37 -0
  176. package/dist/mwl/package-rom.d.ts.map +1 -0
  177. package/dist/mwl/package-rom.js +96 -0
  178. package/dist/mwl/package-rom.js.map +1 -0
  179. package/dist/mwl/package-verify.d.ts +34 -0
  180. package/dist/mwl/package-verify.d.ts.map +1 -0
  181. package/dist/mwl/package-verify.js +111 -0
  182. package/dist/mwl/package-verify.js.map +1 -0
  183. package/dist/mwl/package.d.ts +184 -0
  184. package/dist/mwl/package.d.ts.map +1 -0
  185. package/dist/mwl/package.js +382 -0
  186. package/dist/mwl/package.js.map +1 -0
  187. package/dist/mwl/schema.d.ts +1 -1
  188. package/dist/mwl/schema.d.ts.map +1 -1
  189. package/dist/mwl/tables.d.ts +2 -0
  190. package/dist/mwl/tables.d.ts.map +1 -1
  191. package/dist/mwl/tables.js +13 -0
  192. package/dist/mwl/tables.js.map +1 -1
  193. package/dist/physics/PhysicsWorld.d.ts.map +1 -1
  194. package/dist/physics/PhysicsWorld.js +4 -3
  195. package/dist/physics/PhysicsWorld.js.map +1 -1
  196. package/dist/physics/XpbdWorld.d.ts +23 -5
  197. package/dist/physics/XpbdWorld.d.ts.map +1 -1
  198. package/dist/physics/XpbdWorld.js +91 -6
  199. package/dist/physics/XpbdWorld.js.map +1 -1
  200. package/dist/testing/FakeAudio.d.ts +149 -0
  201. package/dist/testing/FakeAudio.d.ts.map +1 -0
  202. package/dist/testing/FakeAudio.js +219 -0
  203. package/dist/testing/FakeAudio.js.map +1 -0
  204. package/dist/testing/index.d.ts +18 -0
  205. package/dist/testing/index.d.ts.map +1 -1
  206. package/dist/testing/index.js +22 -0
  207. package/dist/testing/index.js.map +1 -1
  208. package/dist/threads/index.d.ts +2 -2
  209. package/dist/threads/index.d.ts.map +1 -1
  210. package/dist/threads/index.js +1 -1
  211. package/dist/threads/index.js.map +1 -1
  212. package/dist/threads/spawn.d.ts +32 -0
  213. package/dist/threads/spawn.d.ts.map +1 -1
  214. package/dist/threads/spawn.js +35 -11
  215. package/dist/threads/spawn.js.map +1 -1
  216. package/dist/three-d/Collision3D.d.ts +46 -2
  217. package/dist/three-d/Collision3D.d.ts.map +1 -1
  218. package/dist/three-d/Collision3D.js +46 -2
  219. package/dist/three-d/Collision3D.js.map +1 -1
  220. package/dist/three-d/Grid.d.ts +12 -1
  221. package/dist/three-d/Grid.d.ts.map +1 -1
  222. package/dist/three-d/Grid.js +12 -1
  223. package/dist/three-d/Grid.js.map +1 -1
  224. package/dist/three-d/Vox.d.ts +13 -1
  225. package/dist/three-d/Vox.d.ts.map +1 -1
  226. package/dist/three-d/Vox.js +13 -1
  227. package/dist/three-d/Vox.js.map +1 -1
  228. package/dist/two-d/Game.d.ts +32 -1
  229. package/dist/two-d/Game.d.ts.map +1 -1
  230. package/dist/two-d/Game.js +105 -14
  231. package/dist/two-d/Game.js.map +1 -1
  232. package/dist/two-d/Scene2D.d.ts +24 -0
  233. package/dist/two-d/Scene2D.d.ts.map +1 -1
  234. package/dist/two-d/Scene2D.js +30 -0
  235. package/dist/two-d/Scene2D.js.map +1 -1
  236. package/dist/two-d/render/HazeLayer.d.ts.map +1 -1
  237. package/dist/two-d/render/HazeLayer.js +2 -1
  238. package/dist/two-d/render/HazeLayer.js.map +1 -1
  239. package/dist/two-d/render/ImageModifiers.d.ts +4 -2
  240. package/dist/two-d/render/ImageModifiers.d.ts.map +1 -1
  241. package/dist/two-d/render/ImageModifiers.js +13 -8
  242. package/dist/two-d/render/ImageModifiers.js.map +1 -1
  243. package/dist/two-d/render/NeonGlow.d.ts.map +1 -1
  244. package/dist/two-d/render/NeonGlow.js +2 -1
  245. package/dist/two-d/render/NeonGlow.js.map +1 -1
  246. package/dist/two-d/render/Particles.d.ts.map +1 -1
  247. package/dist/two-d/render/Particles.js +2 -1
  248. package/dist/two-d/render/Particles.js.map +1 -1
  249. package/dist/two-d/render/SceneTransition.d.ts +301 -0
  250. package/dist/two-d/render/SceneTransition.d.ts.map +1 -0
  251. package/dist/two-d/render/SceneTransition.js +458 -0
  252. package/dist/two-d/render/SceneTransition.js.map +1 -0
  253. package/dist/two-d/render/ScreenEffects.d.ts.map +1 -1
  254. package/dist/two-d/render/ScreenEffects.js +2 -1
  255. package/dist/two-d/render/ScreenEffects.js.map +1 -1
  256. package/dist/two-d/render/SkeletalSprite.d.ts.map +1 -1
  257. package/dist/two-d/render/SkeletalSprite.js +2 -1
  258. package/dist/two-d/render/SkeletalSprite.js.map +1 -1
  259. package/dist/two-d/render/Types2D.d.ts +2 -2
  260. package/dist/two-d/render/Types2D.d.ts.map +1 -1
  261. package/dist/two-d/render/Types2D.js +2 -2
  262. package/dist/two-d/render/Types2D.js.map +1 -1
  263. package/dist/two-d/render/index.d.ts +2 -0
  264. package/dist/two-d/render/index.d.ts.map +1 -1
  265. package/dist/two-d/render/index.js +1 -0
  266. package/dist/two-d/render/index.js.map +1 -1
  267. package/dist/two-d/ui/Bar.d.ts.map +1 -1
  268. package/dist/two-d/ui/Bar.js +3 -5
  269. package/dist/two-d/ui/Bar.js.map +1 -1
  270. package/dist/two-d/ui/Button.d.ts +23 -0
  271. package/dist/two-d/ui/Button.d.ts.map +1 -1
  272. package/dist/two-d/ui/Button.js +37 -0
  273. package/dist/two-d/ui/Button.js.map +1 -1
  274. package/dist/two-d/ui/Dropdown.d.ts +5 -1
  275. package/dist/two-d/ui/Dropdown.d.ts.map +1 -1
  276. package/dist/two-d/ui/Dropdown.js +8 -1
  277. package/dist/two-d/ui/Dropdown.js.map +1 -1
  278. package/dist/two-d/ui/DropdownView.d.ts +63 -3
  279. package/dist/two-d/ui/DropdownView.d.ts.map +1 -1
  280. package/dist/two-d/ui/DropdownView.js +168 -9
  281. package/dist/two-d/ui/DropdownView.js.map +1 -1
  282. package/dist/two-d/ui/FloatingText.d.ts.map +1 -1
  283. package/dist/two-d/ui/FloatingText.js +2 -1
  284. package/dist/two-d/ui/FloatingText.js.map +1 -1
  285. package/dist/two-d/ui/IconGrid.d.ts.map +1 -1
  286. package/dist/two-d/ui/IconGrid.js +2 -1
  287. package/dist/two-d/ui/IconGrid.js.map +1 -1
  288. package/dist/two-d/ui/ListView.d.ts.map +1 -1
  289. package/dist/two-d/ui/ListView.js +2 -1
  290. package/dist/two-d/ui/ListView.js.map +1 -1
  291. package/dist/two-d/ui/ScrollBox.d.ts +96 -0
  292. package/dist/two-d/ui/ScrollBox.d.ts.map +1 -1
  293. package/dist/two-d/ui/ScrollBox.js +218 -5
  294. package/dist/two-d/ui/ScrollBox.js.map +1 -1
  295. package/dist/two-d/ui/ScrollText.d.ts +131 -0
  296. package/dist/two-d/ui/ScrollText.d.ts.map +1 -0
  297. package/dist/two-d/ui/ScrollText.js +218 -0
  298. package/dist/two-d/ui/ScrollText.js.map +1 -0
  299. package/dist/two-d/ui/Slider.d.ts.map +1 -1
  300. package/dist/two-d/ui/Slider.js +4 -4
  301. package/dist/two-d/ui/Slider.js.map +1 -1
  302. package/dist/two-d/ui/Tooltip.d.ts +70 -1
  303. package/dist/two-d/ui/Tooltip.d.ts.map +1 -1
  304. package/dist/two-d/ui/Tooltip.js +115 -3
  305. package/dist/two-d/ui/Tooltip.js.map +1 -1
  306. package/dist/two-d/ui/Window.d.ts +35 -2
  307. package/dist/two-d/ui/Window.d.ts.map +1 -1
  308. package/dist/two-d/ui/Window.js +58 -3
  309. package/dist/two-d/ui/Window.js.map +1 -1
  310. package/dist/two-d/ui/index.d.ts +7 -5
  311. package/dist/two-d/ui/index.d.ts.map +1 -1
  312. package/dist/two-d/ui/index.js +3 -2
  313. package/dist/two-d/ui/index.js.map +1 -1
  314. package/dist/version.d.ts +1 -1
  315. package/dist/version.js +1 -1
  316. package/package.json +14 -3
  317. package/sbom.cdx.json +19 -260
  318. package/tools/audio.d.mts +75 -0
  319. package/tools/audio.mjs +437 -0
  320. package/tools/browser-driver.mjs +3 -3
  321. package/tools/browser-smoke.mjs +19 -2
  322. package/tools/build-tablebases.mjs +7 -3
  323. package/tools/compile-openings.mjs +7 -3
  324. package/tools/compile-resources.d.mts +2 -0
  325. package/tools/compile-resources.mjs +13 -1
  326. package/tools/emit-page.d.mts +6 -0
  327. package/tools/emit-page.mjs +43 -3
  328. package/tools/find-chrome.mjs +4 -2
  329. package/tools/init.mjs +11 -3
  330. package/tools/mwl.mjs +144 -2
  331. package/tools/play-uci.mjs +9 -5
  332. package/tools/vite.mjs +9 -1
  333. package/tools.md +2 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MidiSynth.js","sourceRoot":"","sources":["../../src/audio/MidiSynth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import { Signal } from '../core/Signal.ts';
2
+ import { type RenderedMidi } from './MidiRender.ts';
3
+ export interface MusicPlayerOptions {
4
+ /** level, 0 to 1; 1 when omitted */
5
+ volume?: number;
6
+ /** where the sound goes; the context's destination when omitted (pass a gain or compressor to shape it) */
7
+ destination?: AudioNode;
8
+ }
9
+ export interface MusicPlayOptions {
10
+ /** start offset in seconds */
11
+ pos?: number;
12
+ /** true repeats the track through its loop region instead of ending; false when omitted */
13
+ repeat?: boolean;
14
+ }
15
+ /**
16
+ * One music track at a time with the controls a game's jukebox needs: load a decoded track or a
17
+ * rendered MIDI file, play, pause, resume, read the position, seek, and be told when the track
18
+ * ends so the game can pick the next one. It wraps a `Channel`, so the buffer, the loop region
19
+ * and the playhead behave as they do there; what it adds is the paused state and the end of the
20
+ * track. Like `Music`, nothing advances on its own: call `update()` each frame.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * import { MusicPlayer, renderMidiToBufferAsync } from '@datamoc/mw_games/audio';
25
+ *
26
+ * declare const context: AudioContext;
27
+ * declare const midiBytes: ArrayBuffer;
28
+ * declare function nextTrack(): Promise<ArrayBuffer>;
29
+ *
30
+ * const player = new MusicPlayer(context, { volume: 0.8 });
31
+ * player.load(await renderMidiToBufferAsync(midiBytes));
32
+ * player.play();
33
+ * async function playNext(): Promise<void> {
34
+ * player.load(await renderMidiToBufferAsync(await nextTrack()));
35
+ * player.play();
36
+ * }
37
+ * player.onEnded.add(() => { void playNext(); });
38
+ *
39
+ * // in the scene's update:
40
+ * player.update();
41
+ * ```
42
+ */
43
+ export declare class MusicPlayer {
44
+ /** fires once when a track that was not repeating plays through to its end */
45
+ readonly onEnded: Signal<void>;
46
+ private readonly context;
47
+ private readonly channel;
48
+ private buffer;
49
+ private loop;
50
+ private repeat;
51
+ private playing;
52
+ private paused;
53
+ private resumeAt;
54
+ private volume_;
55
+ constructor(context: AudioContext, options?: MusicPlayerOptions);
56
+ /** stops what plays and makes this track the one to play; a rendered MIDI file is copied into a buffer first */
57
+ load(track: RenderedMidi | AudioBuffer): void;
58
+ /** plays the loaded track from the start, or from `pos` */
59
+ play(options?: MusicPlayOptions): void;
60
+ pause(): void;
61
+ resume(): void;
62
+ /** pauses a playing track and resumes a paused one */
63
+ togglePause(): void;
64
+ /** stops and forgets the position; `play()` starts over */
65
+ stop(): void;
66
+ get isPlaying(): boolean;
67
+ get isPaused(): boolean;
68
+ /** seconds of the loaded track */
69
+ get duration(): number;
70
+ /** seconds into the track, held while paused */
71
+ position(): number;
72
+ /** moves the playhead, keeping a paused track paused */
73
+ seek(pos: number): void;
74
+ get volume(): number;
75
+ set volume(level: number);
76
+ /** call each frame: fires `onEnded` once when a non-repeating track has finished */
77
+ update(): void;
78
+ private start;
79
+ }
80
+ //# sourceMappingURL=MusicPlayer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MusicPlayer.d.ts","sourceRoot":"","sources":["../../src/audio/MusicPlayer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,OAAO,EAAiB,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEnE,MAAM,WAAW,kBAAkB;IAClC,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2GAA2G;IAC3G,WAAW,CAAC,EAAE,SAAS,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAChC,8BAA8B;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2FAA2F;IAC3F,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,WAAW;IACvB,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,eAAsB;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,IAAI,CAA+C;IAC3D,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,OAAO,CAAS;IAExB,YAAY,OAAO,EAAE,YAAY,EAAE,OAAO,GAAE,kBAAuB,EAIlE;IAED,gHAAgH;IAChH,IAAI,CAAC,KAAK,EAAE,YAAY,GAAG,WAAW,GAAG,IAAI,CAS5C;IAED,2DAA2D;IAC3D,IAAI,CAAC,OAAO,GAAE,gBAAqB,GAAG,IAAI,CAOzC;IAED,KAAK,IAAI,IAAI,CAKZ;IAED,MAAM,IAAI,IAAI,CAIb;IAED,sDAAsD;IACtD,WAAW,IAAI,IAAI,CAGlB;IAED,2DAA2D;IAC3D,IAAI,IAAI,IAAI,CAKX;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,kCAAkC;IAClC,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,gDAAgD;IAChD,QAAQ,IAAI,MAAM,CAGjB;IAED,wDAAwD;IACxD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAKtB;IAED,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,IAAI,MAAM,CAAC,KAAK,EAAE,MAAM,EAGvB;IAED,oFAAoF;IACpF,MAAM,IAAI,IAAI,CAKb;IAED,OAAO,CAAC,KAAK;CASb"}
@@ -0,0 +1,147 @@
1
+ import { Signal } from '../core/Signal.js';
2
+ import { Channel } from './Channels.js';
3
+ import { toAudioBuffer } from './MidiRender.js';
4
+ /**
5
+ * One music track at a time with the controls a game's jukebox needs: load a decoded track or a
6
+ * rendered MIDI file, play, pause, resume, read the position, seek, and be told when the track
7
+ * ends so the game can pick the next one. It wraps a `Channel`, so the buffer, the loop region
8
+ * and the playhead behave as they do there; what it adds is the paused state and the end of the
9
+ * track. Like `Music`, nothing advances on its own: call `update()` each frame.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import { MusicPlayer, renderMidiToBufferAsync } from '@datamoc/mw_games/audio';
14
+ *
15
+ * declare const context: AudioContext;
16
+ * declare const midiBytes: ArrayBuffer;
17
+ * declare function nextTrack(): Promise<ArrayBuffer>;
18
+ *
19
+ * const player = new MusicPlayer(context, { volume: 0.8 });
20
+ * player.load(await renderMidiToBufferAsync(midiBytes));
21
+ * player.play();
22
+ * async function playNext(): Promise<void> {
23
+ * player.load(await renderMidiToBufferAsync(await nextTrack()));
24
+ * player.play();
25
+ * }
26
+ * player.onEnded.add(() => { void playNext(); });
27
+ *
28
+ * // in the scene's update:
29
+ * player.update();
30
+ * ```
31
+ */
32
+ export class MusicPlayer {
33
+ constructor(context, options = {}) {
34
+ /** fires once when a track that was not repeating plays through to its end */
35
+ this.onEnded = new Signal();
36
+ this.buffer = null;
37
+ this.loop = null;
38
+ this.repeat = false;
39
+ this.playing = false;
40
+ this.paused = false;
41
+ this.resumeAt = 0;
42
+ this.context = context;
43
+ this.channel = new Channel(context, options.destination);
44
+ this.volume_ = Math.min(1, Math.max(0, options.volume ?? 1));
45
+ }
46
+ /** stops what plays and makes this track the one to play; a rendered MIDI file is copied into a buffer first */
47
+ load(track) {
48
+ this.stop();
49
+ if ('left' in track) {
50
+ this.buffer = toAudioBuffer(this.context, track);
51
+ this.loop = { start: track.loopStart, end: track.loopEnd };
52
+ }
53
+ else {
54
+ this.buffer = track;
55
+ this.loop = null;
56
+ }
57
+ }
58
+ /** plays the loaded track from the start, or from `pos` */
59
+ play(options = {}) {
60
+ if (!this.buffer)
61
+ return;
62
+ this.repeat = options.repeat ?? false;
63
+ this.paused = false;
64
+ this.playing = true;
65
+ this.resumeAt = options.pos ?? 0;
66
+ this.start();
67
+ }
68
+ pause() {
69
+ if (!this.playing || this.paused)
70
+ return;
71
+ this.resumeAt = this.channel.position();
72
+ this.paused = true;
73
+ this.channel.stop();
74
+ }
75
+ resume() {
76
+ if (!this.paused || !this.buffer)
77
+ return;
78
+ this.paused = false;
79
+ this.start();
80
+ }
81
+ /** pauses a playing track and resumes a paused one */
82
+ togglePause() {
83
+ if (this.paused)
84
+ this.resume();
85
+ else
86
+ this.pause();
87
+ }
88
+ /** stops and forgets the position; `play()` starts over */
89
+ stop() {
90
+ this.channel.stop();
91
+ this.playing = false;
92
+ this.paused = false;
93
+ this.resumeAt = 0;
94
+ }
95
+ get isPlaying() {
96
+ return this.playing && !this.paused;
97
+ }
98
+ get isPaused() {
99
+ return this.paused;
100
+ }
101
+ /** seconds of the loaded track */
102
+ get duration() {
103
+ return this.buffer?.duration ?? 0;
104
+ }
105
+ /** seconds into the track, held while paused */
106
+ position() {
107
+ if (!this.playing)
108
+ return 0;
109
+ return this.paused ? this.resumeAt : this.channel.position();
110
+ }
111
+ /** moves the playhead, keeping a paused track paused */
112
+ seek(pos) {
113
+ if (!this.buffer)
114
+ return;
115
+ const target = Math.min(Math.max(0, pos), this.buffer.duration);
116
+ if (this.paused)
117
+ this.resumeAt = target;
118
+ else if (this.playing)
119
+ this.play({ pos: target, repeat: this.repeat });
120
+ }
121
+ get volume() {
122
+ return this.volume_;
123
+ }
124
+ set volume(level) {
125
+ this.volume_ = Math.min(1, Math.max(0, level));
126
+ this.channel.update({ volume: this.volume_ });
127
+ }
128
+ /** call each frame: fires `onEnded` once when a non-repeating track has finished */
129
+ update() {
130
+ if (!this.playing || this.paused || this.channel.isPlaying)
131
+ return;
132
+ this.playing = false;
133
+ this.resumeAt = 0;
134
+ this.onEnded.dispatch();
135
+ }
136
+ start() {
137
+ if (!this.buffer)
138
+ return;
139
+ this.channel.play(this.buffer, {
140
+ volume: this.volume_,
141
+ pos: this.resumeAt,
142
+ loop: this.loop,
143
+ repeat: this.repeat,
144
+ });
145
+ }
146
+ }
147
+ //# sourceMappingURL=MusicPlayer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MusicPlayer.js","sourceRoot":"","sources":["../../src/audio/MusicPlayer.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,13 @@
3
3
  * already provides, named here so a test (or a game with its own audio backend) can supply
4
4
  * a fake without a real `Audio` element existing, which nothing outside a browser can
5
5
  * create.
6
+ *
7
+ * A backend that synthesises its sound instead of decoding a file (see `StreamPlayable`) has to
8
+ * answer the questions `HTMLAudioElement` answers implicitly: `currentTime` reads and seeks the
9
+ * playhead and holds the duration once a track that does not loop has ended; `playbackRate` is
10
+ * honoured (by resampling) or ignored but never throws; `onended` fires once at the end of a
11
+ * track that does not loop and never for one that does; and `play()` may return a promise that
12
+ * settles when the audio is allowed to start.
6
13
  */
7
14
  export interface Playable {
8
15
  play(): void | Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"Playable.d.ts","sourceRoot":"","sources":["../../src/audio/Playable.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACxB,IAAI,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,KAAK,IAAI,IAAI,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,yFAAyF;IACzF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;+EAC2E;IAC3E,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,GAAG,IAAI,CAAC;CAC7C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAUlD"}
1
+ {"version":3,"file":"Playable.d.ts","sourceRoot":"","sources":["../../src/audio/Playable.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,QAAQ;IACxB,IAAI,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,KAAK,IAAI,IAAI,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,yFAAyF;IACzF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;+EAC2E;IAC3E,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,GAAG,IAAI,CAAC;CAC7C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAUlD"}
@@ -1 +1 @@
1
- {"version":3,"file":"Playable.js","sourceRoot":"","sources":["../../src/audio/Playable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAC;AAqBhD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACvC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACJ,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,0FAA0F;QAC1F,OAAO,CAAC,IAAI,CAAC,GAAI,KAAe,CAAC,OAAO,OAAO,IAAI,kBAAkB,CAAC,CAAC;QACvE,OAAO,cAAc,EAAE,CAAC;IACzB,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC;AAED,4FAA4F;AAC5F,SAAS,cAAc;IACtB,OAAO,EAAE,IAAI,KAAI,CAAC,EAAE,KAAK,KAAI,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAC1E,CAAC"}
1
+ {"version":3,"file":"Playable.js","sourceRoot":"","sources":["../../src/audio/Playable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,oBAAoB,CAAC;AA4BhD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY;IACvC,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACJ,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,0FAA0F;QAC1F,OAAO,CAAC,IAAI,CAAC,GAAI,KAAe,CAAC,OAAO,OAAO,IAAI,kBAAkB,CAAC,CAAC;QACvE,OAAO,cAAc,EAAE,CAAC;IACzB,CAAC;IACD,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC;AAED,4FAA4F;AAC5F,SAAS,cAAc;IACtB,OAAO,EAAE,IAAI,KAAI,CAAC,EAAE,KAAK,KAAI,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAC1E,CAAC"}
@@ -1,4 +1,7 @@
1
- import type { Sound } from './Sound.ts';
1
+ /** anything a `SoundSource` can play at a mix: a `Sound`, or an `SfxSound` that also takes a pan */
2
+ export interface PositionalSound {
3
+ play(gain?: number, pitch?: number, pan?: number): unknown;
4
+ }
2
5
  /** A point in the same 2D space the game's camera works in, in world units. */
3
6
  export interface AudioPoint {
4
7
  x: number;
@@ -113,13 +116,18 @@ export declare class SoundSource implements AudioPoint {
113
116
  y: number;
114
117
  private readonly sound;
115
118
  private readonly falloff;
116
- constructor(sound: Sound, options?: SoundSourceOptions);
119
+ constructor(sound: PositionalSound, options?: SoundSourceOptions);
117
120
  moveTo(x: number, y: number): void;
118
121
  gainTo(listener: AudioPoint): number;
119
122
  panTo(listener: AudioPoint & {
120
123
  facing: number;
121
124
  }): number;
122
- /** Plays the source at the gain the listener hears it; returns the gain actually used. */
123
- playFor(listener: AudioPoint): number;
125
+ /**
126
+ * Plays the source at the gain the listener hears it; returns the gain actually used. A listener
127
+ * with a `facing` also pans the sound, for a sound that takes a pan (a `Sound` ignores it).
128
+ */
129
+ playFor(listener: AudioPoint | (AudioPoint & {
130
+ facing: number;
131
+ })): number;
124
132
  }
125
133
  //# sourceMappingURL=Positional.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Positional.d.ts","sourceRoot":"","sources":["../../src/audio/Positional.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,+EAA+E;AAC/E,MAAM,WAAW,UAAU;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACV;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC5B,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,MAAM,CAU9E;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,UAAU,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAW9F;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,aAAc,YAAW,UAAU;IAC/C,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IAEV,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC;IAEf,YAAY,OAAO,GAAE;QAAE,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,EAIpE;IAED,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAGjC;IAED,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1B;CACD;AAED,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACvD,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,WAAY,YAAW,UAAU;IAC7C,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IAEV,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IAEvC,YAAY,KAAK,EAAE,KAAK,EAAE,OAAO,GAAE,kBAAuB,EASzD;IAED,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAGjC;IAED,MAAM,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAEnC;IAED,KAAK,CAAC,QAAQ,EAAE,UAAU,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAEvD;IAED,0FAA0F;IAC1F,OAAO,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAKpC;CACD"}
1
+ {"version":3,"file":"Positional.d.ts","sourceRoot":"","sources":["../../src/audio/Positional.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,MAAM,WAAW,eAAe;IAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3D;AAED,+EAA+E;AAC/E,MAAM,WAAW,UAAU;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACV;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC5B,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,MAAM,CAU9E;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,UAAU,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAW9F;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,aAAc,YAAW,UAAU;IAC/C,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IAEV,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC;IAEf,YAAY,OAAO,GAAE;QAAE,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,EAIpE;IAED,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAGjC;IAED,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1B;CACD;AAED,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACvD,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,CAAC,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,WAAY,YAAW,UAAU;IAC7C,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IAEV,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IAEvC,YAAY,KAAK,EAAE,eAAe,EAAE,OAAO,GAAE,kBAAuB,EASnE;IAED,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAGjC;IAED,MAAM,CAAC,QAAQ,EAAE,UAAU,GAAG,MAAM,CAEnC;IAED,KAAK,CAAC,QAAQ,EAAE,UAAU,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAEvD;IAED;;;OAGG;IACH,OAAO,CAAC,QAAQ,EAAE,UAAU,GAAG,CAAC,UAAU,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,MAAM,CAKxE;CACD"}
@@ -123,12 +123,15 @@ export class SoundSource {
123
123
  panTo(listener) {
124
124
  return audioPan(listener, this);
125
125
  }
126
- /** Plays the source at the gain the listener hears it; returns the gain actually used. */
126
+ /**
127
+ * Plays the source at the gain the listener hears it; returns the gain actually used. A listener
128
+ * with a `facing` also pans the sound, for a sound that takes a pan (a `Sound` ignores it).
129
+ */
127
130
  playFor(listener) {
128
131
  const gain = this.gainTo(listener);
129
132
  if (gain <= 0)
130
133
  return 0;
131
- this.sound.play(gain);
134
+ this.sound.play(gain, 1, 'facing' in listener ? this.panTo(listener) : 0);
132
135
  return gain;
133
136
  }
134
137
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Positional.js","sourceRoot":"","sources":["../../src/audio/Positional.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Positional.js","sourceRoot":"","sources":["../../src/audio/Positional.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Plain-array helpers for fitting rendered audio to a budget: cut to a length with a fade,
3
+ * fold to mono, halve the rate, convert between rates. They work on the `Float32Array`
4
+ * pairs a render produces, so nothing here needs an `AudioContext`.
5
+ */
6
+ /** stereo frames at a sample rate: the shape every renderer in this module hands back */
7
+ export interface StereoFrames {
8
+ sampleRate: number;
9
+ left: Float32Array;
10
+ right: Float32Array;
11
+ }
12
+ /**
13
+ * Converts stereo frames to another sample rate by linear interpolation. Linear is audibly
14
+ * soft above the new Nyquist, which is the right trade for ambience and background music where
15
+ * this is used to save memory; a game that needs a clean conversion renders at the target
16
+ * rate instead.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { resampleStereo } from '@datamoc/mw_games/audio';
21
+ *
22
+ * declare const frames: { sampleRate: number; left: Float32Array; right: Float32Array };
23
+ *
24
+ * const half = resampleStereo(frames, frames.sampleRate / 2);
25
+ * console.log(half.left.length);
26
+ * ```
27
+ */
28
+ export declare function resampleStereo<T extends StereoFrames>(frames: T, sampleRate: number): T;
29
+ /** what a track may cost: every field optional, an omitted one leaves that dimension alone */
30
+ export interface RenderBudget {
31
+ /** cut the track to this many seconds, fading the cut so it does not click */
32
+ maxSeconds?: number;
33
+ /** seconds of fade at a cut; 0.5 when omitted */
34
+ fadeSeconds?: number;
35
+ /** fold both channels into one, stored twice-referenced so a stereo consumer still works */
36
+ mono?: boolean;
37
+ /** halve the sample rate, for ambience that carries no high frequencies */
38
+ halfRate?: boolean;
39
+ }
40
+ /**
41
+ * Fits a rendered track to a budget: cuts it to `maxSeconds` with a fade, folds it to mono
42
+ * and halves its rate, in that order. A mono track shares one array between both channels, so
43
+ * it costs half the memory in the renderer's hands even though a stereo `AudioBuffer` made
44
+ * from it later still has two. Loop points past the cut are pulled back to the new end.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * import { fitToBudget, renderMidiToBuffer, parseMidi } from '@datamoc/mw_games/audio';
49
+ *
50
+ * declare const midiBytes: ArrayBuffer;
51
+ *
52
+ * const ambience = fitToBudget(renderMidiToBuffer(parseMidi(midiBytes)), { maxSeconds: 60, mono: true, halfRate: true });
53
+ * console.log(ambience.duration);
54
+ * ```
55
+ */
56
+ export declare function fitToBudget<T extends StereoFrames & {
57
+ loopStart?: number;
58
+ loopEnd?: number;
59
+ duration?: number;
60
+ }>(track: T, budget: RenderBudget): T;
61
+ //# sourceMappingURL=Resample.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Resample.d.ts","sourceRoot":"","sources":["../../src/audio/Resample.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,yFAAyF;AACzF,MAAM,WAAW,YAAY;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,YAAY,CAAC;CACpB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,YAAY,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,CAAC,CAiBvF;AAED,8FAA8F;AAC9F,MAAM,WAAW,YAAY;IAC5B,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4FAA4F;IAC5F,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,YAAY,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,EAC/G,KAAK,EAAE,CAAC,EACR,MAAM,EAAE,YAAY,GAClB,CAAC,CAyBH"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Plain-array helpers for fitting rendered audio to a budget: cut to a length with a fade,
3
+ * fold to mono, halve the rate, convert between rates. They work on the `Float32Array`
4
+ * pairs a render produces, so nothing here needs an `AudioContext`.
5
+ */
6
+ /**
7
+ * Converts stereo frames to another sample rate by linear interpolation. Linear is audibly
8
+ * soft above the new Nyquist, which is the right trade for ambience and background music where
9
+ * this is used to save memory; a game that needs a clean conversion renders at the target
10
+ * rate instead.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { resampleStereo } from '@datamoc/mw_games/audio';
15
+ *
16
+ * declare const frames: { sampleRate: number; left: Float32Array; right: Float32Array };
17
+ *
18
+ * const half = resampleStereo(frames, frames.sampleRate / 2);
19
+ * console.log(half.left.length);
20
+ * ```
21
+ */
22
+ export function resampleStereo(frames, sampleRate) {
23
+ if (!(sampleRate > 0))
24
+ throw new Error('target sample rate must be positive');
25
+ if (sampleRate === frames.sampleRate)
26
+ return frames;
27
+ const length = Math.max(1, Math.round((frames.left.length * sampleRate) / frames.sampleRate));
28
+ const step = frames.sampleRate / sampleRate;
29
+ const left = new Float32Array(length);
30
+ const right = new Float32Array(length);
31
+ const last = frames.left.length - 1;
32
+ for (let i = 0; i < length; i++) {
33
+ const position = i * step;
34
+ const floor = Math.min(last, Math.floor(position));
35
+ const next = Math.min(last, floor + 1);
36
+ const fraction = position - floor;
37
+ left[i] = frames.left[floor] + (frames.left[next] - frames.left[floor]) * fraction;
38
+ right[i] = frames.right[floor] + (frames.right[next] - frames.right[floor]) * fraction;
39
+ }
40
+ return { ...frames, sampleRate, left, right };
41
+ }
42
+ /**
43
+ * Fits a rendered track to a budget: cuts it to `maxSeconds` with a fade, folds it to mono
44
+ * and halves its rate, in that order. A mono track shares one array between both channels, so
45
+ * it costs half the memory in the renderer's hands even though a stereo `AudioBuffer` made
46
+ * from it later still has two. Loop points past the cut are pulled back to the new end.
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * import { fitToBudget, renderMidiToBuffer, parseMidi } from '@datamoc/mw_games/audio';
51
+ *
52
+ * declare const midiBytes: ArrayBuffer;
53
+ *
54
+ * const ambience = fitToBudget(renderMidiToBuffer(parseMidi(midiBytes)), { maxSeconds: 60, mono: true, halfRate: true });
55
+ * console.log(ambience.duration);
56
+ * ```
57
+ */
58
+ export function fitToBudget(track, budget) {
59
+ let result = track;
60
+ if (budget.maxSeconds !== undefined) {
61
+ if (!(budget.maxSeconds > 0))
62
+ throw new Error('budget maxSeconds must be positive');
63
+ const limit = Math.max(1, Math.floor(budget.maxSeconds * result.sampleRate));
64
+ if (result.left.length > limit) {
65
+ const left = result.left.slice(0, limit);
66
+ const right = result.right.slice(0, limit);
67
+ const fade = Math.min(limit, Math.floor((budget.fadeSeconds ?? 0.5) * result.sampleRate));
68
+ for (let i = 0; i < fade; i++) {
69
+ const gain = i / fade;
70
+ left[limit - 1 - i] *= gain;
71
+ right[limit - 1 - i] *= gain;
72
+ }
73
+ const seconds = limit / result.sampleRate;
74
+ result = withEnd({ ...result, left, right }, seconds);
75
+ }
76
+ }
77
+ if (budget.mono) {
78
+ const mixed = new Float32Array(result.left.length);
79
+ for (let i = 0; i < mixed.length; i++)
80
+ mixed[i] = (result.left[i] + result.right[i]) / 2;
81
+ result = { ...result, left: mixed, right: mixed };
82
+ }
83
+ if (budget.halfRate)
84
+ result = resampleStereo(result, result.sampleRate / 2);
85
+ return result;
86
+ }
87
+ function withEnd(track, seconds) {
88
+ const next = { ...track };
89
+ if (next.duration !== undefined)
90
+ next.duration = seconds;
91
+ if (next.loopEnd !== undefined)
92
+ next.loopEnd = Math.min(next.loopEnd, seconds);
93
+ if (next.loopStart !== undefined)
94
+ next.loopStart = Math.min(next.loopStart, Math.max(0, seconds - 0.1));
95
+ return next;
96
+ }
97
+ //# sourceMappingURL=Resample.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Resample.js","sourceRoot":"","sources":["../../src/audio/Resample.ts"],"names":[],"mappings":"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"}
@@ -21,11 +21,25 @@ export interface SampleDecodingContext {
21
21
  export declare const MIN_SAMPLE_RATE = 400;
22
22
  export declare const MAX_SAMPLE_RATE = 200000;
23
23
  export declare const MAX_SAMPLE_FRAMES: number;
24
+ /**
25
+ * How much a SoundFont 3 may decode to, in frames. The defaults (`MAX_SAMPLE_FRAMES` for one sample, 128
26
+ * million, 256 MB of 16-bit audio, for the whole font) suit a game font; raise them for a very large one
27
+ * you trust, since each frame is two bytes of memory and a damaged font can ask for any number.
28
+ */
29
+ export interface SoundFontLimits {
30
+ /** frames one decoded sample may have */
31
+ sampleFrames?: number;
32
+ /** frames all the decoded samples of one font may have */
33
+ totalFrames?: number;
34
+ }
35
+ /** the whole-font default of {@link SoundFontLimits} */
36
+ export declare const MAX_TOTAL_FRAMES: number;
24
37
  /**
25
38
  * A decoder over WebAudio's `decodeAudioData`, for a browser: each sample decodes in a context
26
39
  * created at the sample's own rate, so the browser does not resample it and the loop points keep
27
40
  * meaning what the font wrote. A rate outside what a context accepts (8 to 96 kHz) decodes at
28
- * the nearest accepted rate and is resampled back. `create` replaces the context factory.
41
+ * the nearest accepted rate and is resampled back. `create` replaces the context factory and `limits`
42
+ * the longest sample it will return.
29
43
  *
30
44
  * @example
31
45
  * ```ts
@@ -37,5 +51,5 @@ export declare const MAX_SAMPLE_FRAMES: number;
37
51
  * console.log(font.presetCount);
38
52
  * ```
39
53
  */
40
- export declare function webAudioSampleDecoder(create?: (rate: number) => SampleDecodingContext): SoundFontSampleDecoder;
54
+ export declare function webAudioSampleDecoder(create?: (rate: number) => SampleDecodingContext, limits?: SoundFontLimits): SoundFontSampleDecoder;
41
55
  //# sourceMappingURL=SampleDecoder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SampleDecoder.d.ts","sourceRoot":"","sources":["../../src/audio/SampleDecoder.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,KAAK,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzG,yFAAyF;AACzF,MAAM,WAAW,qBAAqB;IACrC,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC;QAC3C,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAAC;KAC9C,CAAC,CAAC;CACH;AAMD;;;;GAIG;AACH,eAAO,MAAM,eAAe,MAAM,CAAC;AACnC,eAAO,MAAM,eAAe,SAAS,CAAC;AACtC,eAAO,MAAM,iBAAiB,QAAU,CAAC;AA+BzC;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACpC,MAAM,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,qBAAsC,GAC9D,sBAAsB,CAcxB"}
1
+ {"version":3,"file":"SampleDecoder.d.ts","sourceRoot":"","sources":["../../src/audio/SampleDecoder.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,KAAK,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzG,yFAAyF;AACzF,MAAM,WAAW,qBAAqB;IACrC,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC;QAC3C,UAAU,EAAE,MAAM,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAAC;KAC9C,CAAC,CAAC;CACH;AAMD;;;;GAIG;AACH,eAAO,MAAM,eAAe,MAAM,CAAC;AACnC,eAAO,MAAM,eAAe,SAAS,CAAC;AACtC,eAAO,MAAM,iBAAiB,QAAU,CAAC;AAEzC;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC/B,yCAAyC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wDAAwD;AACxD,eAAO,MAAM,gBAAgB,QAAU,CAAC;AA+BxC;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CACpC,MAAM,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,qBAAsC,EAChE,MAAM,GAAE,eAAoB,GAC1B,sBAAsB,CAcxB"}
@@ -9,6 +9,8 @@ const MAX_RATE = 96000;
9
9
  export const MIN_SAMPLE_RATE = 400;
10
10
  export const MAX_SAMPLE_RATE = 200000;
11
11
  export const MAX_SAMPLE_FRAMES = 1 << 24;
12
+ /** the whole-font default of {@link SoundFontLimits} */
13
+ export const MAX_TOTAL_FRAMES = 1 << 27;
12
14
  function toInt16(frames) {
13
15
  const out = new Int16Array(frames.length);
14
16
  for (let index = 0; index < frames.length; index++) {
@@ -17,9 +19,9 @@ function toInt16(frames) {
17
19
  return out;
18
20
  }
19
21
  /** linear resampling, only for the rare sample whose rate a context cannot be created at */
20
- function resample(frames, from, to) {
22
+ function resample(frames, from, to, maxFrames) {
21
23
  const length = Math.max(1, Math.round((frames.length * to) / from));
22
- if (length > MAX_SAMPLE_FRAMES)
24
+ if (length > maxFrames)
23
25
  throw new Error('a decoded sample is longer than a SoundFont sample may be');
24
26
  const out = new Float32Array(length);
25
27
  for (let index = 0; index < out.length; index++) {
@@ -40,7 +42,8 @@ function offlineContext(rate) {
40
42
  * A decoder over WebAudio's `decodeAudioData`, for a browser: each sample decodes in a context
41
43
  * created at the sample's own rate, so the browser does not resample it and the loop points keep
42
44
  * meaning what the font wrote. A rate outside what a context accepts (8 to 96 kHz) decodes at
43
- * the nearest accepted rate and is resampled back. `create` replaces the context factory.
45
+ * the nearest accepted rate and is resampled back. `create` replaces the context factory and `limits`
46
+ * the longest sample it will return.
44
47
  *
45
48
  * @example
46
49
  * ```ts
@@ -52,7 +55,8 @@ function offlineContext(rate) {
52
55
  * console.log(font.presetCount);
53
56
  * ```
54
57
  */
55
- export function webAudioSampleDecoder(create = offlineContext) {
58
+ export function webAudioSampleDecoder(create = offlineContext, limits = {}) {
59
+ const maxFrames = limits.sampleFrames ?? MAX_SAMPLE_FRAMES;
56
60
  return async (ogg, rate) => {
57
61
  if (!(rate >= MIN_SAMPLE_RATE && rate <= MAX_SAMPLE_RATE)) {
58
62
  throw new Error(`a sample rate of ${rate} Hz is not one a SoundFont can state`);
@@ -61,10 +65,10 @@ export function webAudioSampleDecoder(create = offlineContext) {
61
65
  //decodeAudioData detaches what it is given, and `ogg` is a view into the caller's font
62
66
  const copy = ogg.slice().buffer;
63
67
  const decoded = await create(contextRate).decodeAudioData(copy);
64
- if (decoded.length > MAX_SAMPLE_FRAMES)
68
+ if (decoded.length > maxFrames)
65
69
  throw new Error('a decoded sample is longer than a SoundFont sample may be');
66
70
  const channel = decoded.getChannelData(0);
67
- return toInt16(decoded.sampleRate === rate ? channel : resample(channel, decoded.sampleRate, rate));
71
+ return toInt16(decoded.sampleRate === rate ? channel : resample(channel, decoded.sampleRate, rate, maxFrames));
68
72
  };
69
73
  }
70
74
  //# sourceMappingURL=SampleDecoder.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SampleDecoder.js","sourceRoot":"","sources":["../../src/audio/SampleDecoder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SampleDecoder.js","sourceRoot":"","sources":["../../src/audio/SampleDecoder.ts"],"names":[],"mappings":"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"}