@lewismoten/gamemaker-sandbox-tools 0.0.0-stage → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/LICENSE +24 -0
  2. package/NOTICE.md +19 -0
  3. package/README.md +76 -2
  4. package/dist/audio.js +140 -0
  5. package/dist/binary-D7wuaC5g.js +120 -0
  6. package/dist/drawing.js +871 -0
  7. package/dist/formats/blocks.js +103 -0
  8. package/dist/formats/character.js +83 -0
  9. package/dist/formats/cmf.js +37 -0
  10. package/dist/formats/fli.js +296 -0
  11. package/dist/formats/fliLimits.js +54 -0
  12. package/dist/formats/game.js +193 -0
  13. package/dist/formats/gif.js +125 -0
  14. package/dist/formats/graphicScripts.js +44 -0
  15. package/dist/formats/map.js +38 -0
  16. package/dist/formats/monster.js +69 -0
  17. package/dist/formats/palette.js +58 -0
  18. package/dist/formats/recording.js +17 -0
  19. package/dist/formats/sound.js +44 -0
  20. package/dist/formats/voc.js +303 -0
  21. package/dist/images.js +50 -0
  22. package/dist/pixels-DzfIIUOl.js +76 -0
  23. package/dist/simulation.js +2776 -0
  24. package/dist/types/games/kit/blit.d.ts +10 -0
  25. package/dist/types/games/kit/clips.d.ts +36 -0
  26. package/dist/types/games/kit/gameSim.d.ts +108 -0
  27. package/dist/types/games/kit/music.d.ts +1 -0
  28. package/dist/types/games/kit/palette.d.ts +16 -0
  29. package/dist/types/games/kit/pcText.d.ts +8 -0
  30. package/dist/types/games/kit/pixels.d.ts +26 -0
  31. package/dist/types/games/kit/reel.d.ts +34 -0
  32. package/dist/types/games/kit/sceneSim.d.ts +84 -0
  33. package/dist/types/games/kit/shading.d.ts +11 -0
  34. package/dist/types/games/kit/sound.d.ts +29 -0
  35. package/dist/types/games/kit/textBounds.d.ts +51 -0
  36. package/dist/types/games/kit/tune.d.ts +6 -0
  37. package/dist/types/web/packages/tools/src/audio.d.ts +3 -0
  38. package/dist/types/web/packages/tools/src/colors.d.ts +20 -0
  39. package/dist/types/web/packages/tools/src/drawing.d.ts +10 -0
  40. package/dist/types/web/packages/tools/src/images.d.ts +2 -0
  41. package/dist/types/web/packages/tools/src/microFont.d.ts +1 -0
  42. package/dist/types/web/packages/tools/src/simulation.d.ts +3 -0
  43. package/dist/types/web/packages/tools/src/text.d.ts +41 -0
  44. package/dist/types/web/scripts/cdGames.d.ts +103 -0
  45. package/dist/types/web/scripts/gameShots.d.ts +12 -0
  46. package/dist/types/web/scripts/ogImage.d.ts +21 -0
  47. package/dist/types/web/src/assistant/png.d.ts +10 -0
  48. package/dist/types/web/src/audio/cmfLoad.d.ts +7 -0
  49. package/dist/types/web/src/audio/cmfPlayer.d.ts +69 -0
  50. package/dist/types/web/src/audio/compose.d.ts +51 -0
  51. package/dist/types/web/src/audio/context.d.ts +12 -0
  52. package/dist/types/web/src/audio/engine.d.ts +39 -0
  53. package/dist/types/web/src/audio/music.worklet.d.ts +14 -0
  54. package/dist/types/web/src/audio/opl.d.ts +37 -0
  55. package/dist/types/web/src/audio/output.d.ts +13 -0
  56. package/dist/types/web/src/desk/boxes/boxFacts.d.ts +85 -0
  57. package/dist/types/web/src/dosPlayer/fileExtensions.d.ts +44 -0
  58. package/dist/types/web/src/dosPlayer/patchRules.d.ts +24 -0
  59. package/dist/types/web/src/dosPlayer/patchTable.d.ts +80 -0
  60. package/dist/types/web/src/edition.d.ts +6 -0
  61. package/dist/types/web/src/eggs/eggState.d.ts +26 -0
  62. package/dist/types/web/src/eggs/eggs.d.ts +31 -0
  63. package/dist/types/web/src/engine/character.d.ts +30 -0
  64. package/dist/types/web/src/engine/funcKeys.d.ts +33 -0
  65. package/dist/types/web/src/engine/game.d.ts +52 -0
  66. package/dist/types/web/src/engine/keys.d.ts +17 -0
  67. package/dist/types/web/src/engine/machine.d.ts +99 -0
  68. package/dist/types/web/src/engine/menus.d.ts +46 -0
  69. package/dist/types/web/src/engine/monsters.d.ts +19 -0
  70. package/dist/types/web/src/engine/playGain.d.ts +11 -0
  71. package/dist/types/web/src/engine/playerLook.d.ts +26 -0
  72. package/dist/types/web/src/engine/recorder.d.ts +60 -0
  73. package/dist/types/web/src/engine/render.d.ts +8 -0
  74. package/dist/types/web/src/engine/scene.d.ts +156 -0
  75. package/dist/types/web/src/engine/scroll.d.ts +16 -0
  76. package/dist/types/web/src/engine/showGraphic.d.ts +39 -0
  77. package/dist/types/web/src/engine/slowPc.d.ts +62 -0
  78. package/dist/types/web/src/engine/touch.d.ts +33 -0
  79. package/dist/types/web/src/engine/world.d.ts +215 -0
  80. package/dist/types/web/src/export/gameAbout.d.ts +15 -0
  81. package/dist/types/web/src/formats/binary.d.ts +44 -0
  82. package/dist/types/web/src/formats/blocks.d.ts +52 -0
  83. package/dist/types/web/src/formats/character.d.ts +65 -0
  84. package/dist/types/web/src/formats/cmf.d.ts +33 -0
  85. package/dist/types/web/src/formats/fli.d.ts +45 -0
  86. package/dist/types/web/src/formats/fliLimits.d.ts +13 -0
  87. package/dist/types/web/src/formats/game.d.ts +72 -0
  88. package/dist/types/web/src/formats/gif.d.ts +15 -0
  89. package/dist/types/web/src/formats/graphicScripts.d.ts +18 -0
  90. package/dist/types/web/src/formats/highscore.d.ts +10 -0
  91. package/dist/types/web/src/formats/map.d.ts +24 -0
  92. package/dist/types/web/src/formats/monster.d.ts +48 -0
  93. package/dist/types/web/src/formats/palette.d.ts +15 -0
  94. package/dist/types/web/src/formats/recording.d.ts +7 -0
  95. package/dist/types/web/src/formats/sound.d.ts +13 -0
  96. package/dist/types/web/src/formats/voc.d.ts +13 -0
  97. package/dist/types/web/src/gfx/cp437Glyphs.d.ts +2 -0
  98. package/dist/types/web/src/gfx/font8x8.d.ts +2 -0
  99. package/dist/types/web/src/gfx/levelPicture.d.ts +81 -0
  100. package/dist/types/web/src/gfx/screen.d.ts +46 -0
  101. package/dist/types/web/src/gfx/titlePicture.d.ts +21 -0
  102. package/dist/types/web/src/input/chords.d.ts +22 -0
  103. package/dist/types/web/src/input/deskMouse.d.ts +14 -0
  104. package/dist/types/web/src/input/keyboard.d.ts +39 -0
  105. package/dist/types/web/src/input/scancodes.d.ts +3 -0
  106. package/dist/types/web/src/manual/printables/types.d.ts +277 -0
  107. package/dist/types/web/src/project/bizarre.d.ts +28 -0
  108. package/dist/types/web/src/project/companion.d.ts +35 -0
  109. package/dist/types/web/src/project/diskFacts.d.ts +30 -0
  110. package/dist/types/web/src/project/files.d.ts +124 -0
  111. package/dist/types/web/src/project/gameFiles.d.ts +49 -0
  112. package/dist/types/web/src/project/gamePack.d.ts +17 -0
  113. package/dist/types/web/src/project/health.d.ts +30 -0
  114. package/dist/types/web/src/project/secretGames.d.ts +19 -0
  115. package/dist/types/web/src/project/storage.d.ts +46 -0
  116. package/dist/types/web/src/project/streamGame.d.ts +38 -0
  117. package/dist/types/web/src/project/zip.d.ts +33 -0
  118. package/dist/types/web/src/retro/dos/disks.d.ts +33 -0
  119. package/dist/types/web/src/retro/text/cp437.d.ts +4 -0
  120. package/dist/types/web/src/seededRandom.d.ts +6 -0
  121. package/dist/types/web/src/tinHearts/deskHearts.d.ts +33 -0
  122. package/dist/types/web/src/tinHearts/manifest.d.ts +30 -0
  123. package/dist/types/web/src/tinHearts/tinHearts.d.ts +84 -0
  124. package/dist/types/web/src/walkthrough/format.d.ts +92 -0
  125. package/dist/types/web/src/walkthrough/madeUpScores.d.ts +6 -0
  126. package/dist/types/web/src/walkthrough/runtime.d.ts +134 -0
  127. package/package.json +60 -4
package/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 gandrewstone
4
+ Copyright (c) 2026 Lewis Moten (modifications: the web port, the 1994 desk, the
5
+ Sandbox Software and Bard of Bizarre games, and the Mac, Windows and Linux players;
6
+ see NOTICE.md)
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all
16
+ copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ SOFTWARE.
package/NOTICE.md ADDED
@@ -0,0 +1,19 @@
1
+ # Notices
2
+
3
+ This development toolkit is extracted from Game-Maker: Sandbox Edition by Lewis Moten, released under the MIT License. It includes the fork's native format codecs, drawing and audio builders, and headless reference simulation.
4
+
5
+ The reference engine is a TypeScript port of RSD Game-Maker 3.0 by Recreational Software Designs: G. Andrew Stone, G. Oliver Stone, Oliver Stone Jr. and Joan Stone. Andy Stone released the original source under the MIT License in 2014. The original copyright notice is retained in LICENSE. Peter Savage wrote the original Sound Blaster voice-file and joystick routines.
6
+
7
+ ## Fonts
8
+
9
+ - **8×8 ASCII and selected CP437 glyphs:** Daniel Hepper's [font8x8](https://github.com/dhepper/font8x8), specifically `font8x8_basic`, `font8x8_ext_latin`, `font8x8_greek`, and `font8x8_misc`. Each upstream header declares **Public Domain** and credits **Marcel Sondaar** and **International Business Machines (public domain VGA fonts)** as its basis. See the [upstream source header](https://github.com/dhepper/font8x8/blob/master/font8x8_basic.h). This data is used in `web/src/gfx/font8x8.ts` and the marked entries of `web/src/gfx/cp437Glyphs.ts`, exposed here through `FONT8X8` and `FONT_CP437`.
10
+ - **Additional CP437 glyphs:** the other symbols and box-drawing glyphs in `web/src/gfx/cp437Glyphs.ts` were drawn for Game-Maker: Sandbox Edition using **Anthropic Claude Opus 5.5**, under **Lewis Moten's** direction, and are distributed under the project's **MIT License**. These additions were not copied from IBM's ROM; the full 256-glyph table combines them with the public-domain data above.
11
+ - **3×5 micro font (`MICRO_GLYPHS`):** the project's small sign font, introduced in `games/teddy/draw.ts` by **Lewis Moten using Anthropic Claude Opus 5.5**, then maintained in `games/teddy/src/art/draw.ts`. Its provenance is recorded in [commit 7241036f49cab9dea52f58541e30ce6c9ce86052](https://github.com/lewismoten/GameMaker-SandboxEdition/commit/7241036f49cab9dea52f58541e30ce6c9ce86052). It is distributed under the project's **MIT License**, not the public-domain dedication of Hepper's font. **OpenAI Codex (GPT-6/6.1)** extracted the table into this toolkit's `src/microFont.ts` and added the reusable rendering APIs under Lewis's direction.
12
+
13
+ ## Other formats and tooling
14
+
15
+ The lighting presets were written for this project. GIF, FLI, Creative Voice File, Creative Music File, and ZIP/PNG support follow their respective file formats; CRC-32 uses the reflected polynomial 0xEDB88320.
16
+
17
+ The underlying web port and game-building tools were written with Anthropic Claude Opus 5.5 under Lewis Moten's direction. OpenAI Codex (GPT-6/6.1) prepared the toolkit packaging, consumer migration, documentation, and verification under his direction.
18
+
19
+ No game-specific artwork, palettes, characters, recordings, or complete games are included. Consumers supply and credit their own content.
package/README.md CHANGED
@@ -1,3 +1,77 @@
1
- # Temporary Holding Version
1
+ # @lewismoten/gamemaker-sandbox-tools
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Development-only tools for creating and checking native RSD Game-Maker data. Browser playback belongs to `@lewismoten/gamemaker-sandbox-engine`; this package has no game artwork, recordings, characters, or palettes.
4
+
5
+ Requires Node.js 22.12 or newer. Install with `npm install --save-dev @lewismoten/gamemaker-sandbox-tools` once published. Until publication, `npm pack` produces a self-contained installation candidate.
6
+
7
+ ## APIs
8
+
9
+ - `formats/blocks`, `character`, `game`, `map`, `monster`, `palette`, `gif`, `graphicScripts`, `sound`, `fli`, `fliLimits`, `voc`, `recording`, and `cmf`: native readers, writers, types, and limits. Each is a separate `formats/<name>` subpath.
10
+ - `drawing`: indexed pixels, VGA palette builders, font drawing, shading, compositing, and cartoon reels.
11
+ - `audio`: original FM instrument presets, CMF composition, PC speaker effects, and VOC synthesis.
12
+ - `images`: indexed PNG encoding and CRC-32.
13
+ - `simulation`: headless `Sim`, `GameSim`, `readDisk`, `diskSim`, and `newCharInfo` for fixture checks. This includes the reference simulation; it does not replace testing the browser engine package.
14
+
15
+ ```ts
16
+ import { emptyBlock, serializeBlocks } from '@lewismoten/gamemaker-sandbox-tools/formats/blocks';
17
+ import { Pixels } from '@lewismoten/gamemaker-sandbox-tools/drawing';
18
+
19
+ const blocks = Array.from({ length: 150 }, (_, i) => emptyBlock(i));
20
+ blocks[0].pixels.set(new Pixels(20, 20, 1).px);
21
+ const data = serializeBlocks(blocks);
22
+ ```
23
+
24
+ Keep these imports in build scripts and tests. The package explicitly rejects browser imports. Generated game data and your own copied assets belong in the consuming project.
25
+
26
+ ## Fonts, text images, and lighting
27
+
28
+ `drawing` exports the complete `FONT_CP437` (256 × 8 rows, bit 0 leftmost), including DOS symbols, accents, block shading, and box drawing. NUL, space, and nonbreaking space are intentionally blank. The legacy `FONT8X8` and `text` helpers retain their ASCII behavior. `glyphRows(code)` returns a copy; `encodeText`, `toCp437`, and `fromCp437` convert Unicode DOS symbols and CP437 codes. This is the DOS glyph set, not an ANSI escape-sequence parser.
29
+
30
+ `MICRO_GLYPHS` exposes the project's original 3×5 uppercase font and punctuation. `glyphRows(code, 'micro')` accepts lowercase as uppercase and uses `?` for unsupported glyphs. It does not include game-specific lettering.
31
+
32
+ ```ts
33
+ import { renderText, encodeText, PaletteBuilder, lightColor, TIME_OF_DAY_SKY } from '@lewismoten/gamemaker-sandbox-tools/drawing';
34
+
35
+ const caption = renderText('╔═╗\n║é║\n╚═╝', {
36
+ color: 12, outline: 0, shadow: { color: 8 },
37
+ });
38
+ // caption.width, caption.height, caption.pixels suit indexed GIF/picture builders.
39
+ // Copy small images into a native block's 20×20 pixel array before serializing it.
40
+ const sign = renderText('GO!', { font: 'micro', color: 15, scale: 2 });
41
+ const rawGlyphs = renderText(Uint8Array.of(1, 3, 10, 219), { color: 15 });
42
+ const dosTextBytes = encodeText('Café ♥');
43
+
44
+ const palette = new PaletteBuilder();
45
+ palette.designerColors();
46
+ const [dark, light, middle] = TIME_OF_DAY_SKY.sunset;
47
+ palette.ramp('sky', 8, dark, light, middle);
48
+ const lit = lightColor([100, 180, 80], 'night');
49
+ ```
50
+
51
+ `renderText` returns row-major indexed pixels with transparent index 255 and an origin for the first text cell. It pads the output for a one-pixel outline and shadow, even at larger scales or negative shadow offsets. Strings support LF/CRLF lines; raw byte arrays draw every byte as a glyph on one line. Empty text returns a 0×0 image. Unsupported Unicode throws rather than selecting an unrelated glyph. Ink indexes are 0–254.
52
+
53
+ `outline` and `dropShadow` also decorate `Pixels` sprites within their existing bounds; edges clip. The existing sprite outline uses four neighbors, while text outlines include all eight neighbors.
54
+
55
+ `colorRamp` returns eight-bit RGB stops without allocating palette entries. `PaletteBuilder.ramp` allocates named ramps in a six-bit VGA palette. `TIME_OF_DAY_LIGHT`, `TIME_OF_DAY_SKY`, and `lightColor` provide morning, noon, afternoon, sunset, night, and stage-light presets. `adjustPalette` returns an adjusted native VGA palette without mutating its input. By default it preserves indexes 0 and 255; pass a custom index list to protect glowing colors or designer inks. Game-specific ramp names and cast colors belong in the consumer.
56
+
57
+ ## Build and inspect
58
+
59
+ From this directory, with the repository's web development dependencies installed:
60
+
61
+ ```sh
62
+ npm run build
63
+ npm run verify
64
+ npm pack
65
+ ```
66
+
67
+ The build bundles source from the format, drawing, audio, and simulation modules. It does not use or restart the site's Vite configuration. The tarball contains JavaScript, declarations, this guide, the MIT license, and notices. Validate a packed installation in a separate project before publishing.
68
+
69
+ `verify` installs the tarball in a temporary npm consumer and checks codecs, drawing, sound generation, headless simulation, NodeNext declarations, absence of game content, and rejected browser imports. After reviewing the candidate, publish with `npm publish --access public`; publishing is a separate action.
70
+
71
+ ## Tagged releases
72
+
73
+ The [release workflow](../../../.github/workflows/publish-tools.yml) publishes to the public npm registry when a `tools-v<version>` tag is pushed. The tag must match this package's version and point to a commit on `master`. For this release, use `tools-v3.1.0` after merging the package changes.
74
+
75
+ Configure the repository's `NPM_TOKEN` Actions secret with an npm publishing token authorized for `@lewismoten/gamemaker-sandbox-tools` (including automated publishing with 2FA). The workflow checks source, builds and verifies the packed package, then publishes with provenance. Ordinary branch pushes do not publish anything. See npm's [publishing documentation](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/).
76
+
77
+ Tarballs are local build artifacts and must not be committed to a consuming project. Consumers should use `npm install --save-dev @lewismoten/gamemaker-sandbox-tools@^3.1.0` after the tagged release is published.
package/dist/audio.js ADDED
@@ -0,0 +1,140 @@
1
+ import { encodeCmf as S } from "./formats/cmf.js";
2
+ import { encodeVoc as L } from "./formats/voc.js";
3
+ import { NUMFREQ as p } from "./formats/sound.js";
4
+ import { r as b } from "./pixels-DzfIIUOl.js";
5
+ function s(e) {
6
+ return Uint8Array.from([
7
+ e.modChar,
8
+ e.carChar,
9
+ e.modLevel,
10
+ e.carLevel,
11
+ e.modAD,
12
+ e.carAD,
13
+ e.modSR,
14
+ e.carSR,
15
+ e.modWave ?? 0,
16
+ e.carWave ?? 0,
17
+ e.feedback,
18
+ 0,
19
+ 0,
20
+ 0,
21
+ 0,
22
+ 0
23
+ ]);
24
+ }
25
+ const C = {
26
+ piano: s({ modChar: 1, carChar: 1, modLevel: 90, carLevel: 0, modAD: 242, carAD: 243, modSR: 99, carSR: 101, feedback: 8 }),
27
+ bass: s({ modChar: 1, carChar: 1, modLevel: 26, carLevel: 0, modAD: 246, carAD: 245, modSR: 84, carSR: 69, feedback: 8 }),
28
+ strings: s({ modChar: 97, carChar: 33, modLevel: 29, carLevel: 0, modAD: 116, carAD: 99, modSR: 19, carSR: 21, feedback: 14 }),
29
+ brass: s({ modChar: 33, carChar: 33, modLevel: 26, carLevel: 0, modAD: 99, carAD: 116, modSR: 40, carSR: 23, feedback: 12 }),
30
+ lead: s({ modChar: 34, carChar: 33, modLevel: 27, carLevel: 0, modAD: 244, carAD: 244, modSR: 68, carSR: 36, modWave: 2, feedback: 12 }),
31
+ bell: s({ modChar: 7, carChar: 1, modLevel: 35, carLevel: 0, modAD: 245, carAD: 243, modSR: 34, carSR: 19, feedback: 4 }),
32
+ organ: s({ modChar: 50, carChar: 49, modLevel: 68, carLevel: 0, modAD: 240, carAD: 240, modSR: 15, carSR: 15, feedback: 1 }),
33
+ pad: s({ modChar: 161, carChar: 33, modLevel: 41, carLevel: 0, modAD: 67, carAD: 82, modSR: 5, carSR: 4, modWave: 1, feedback: 10 }),
34
+ pluck: s({ modChar: 3, carChar: 1, modLevel: 30, carLevel: 0, modAD: 248, carAD: 246, modSR: 53, carSR: 85, feedback: 12 })
35
+ }, D = { C: 0, D: 2, E: 4, F: 5, G: 7, A: 9, B: 11 }, k = { w: 4, h: 2, q: 1, e: 0.5, s: 0.25, t: 1 / 3 };
36
+ function M(e) {
37
+ if (/^r$/i.test(e)) return null;
38
+ const r = /^([A-G])([#b]?)(-?\d)$/.exec(e);
39
+ if (!r) throw new Error(`Bad note ${e}`);
40
+ return 12 * (Number(r[3]) + 1) + D[r[1]] + (r[2] === "#" ? 1 : r[2] === "b" ? -1 : 0);
41
+ }
42
+ function g(e, r) {
43
+ const a = Math.round(8760 / e), t = [], o = [], l = /* @__PURE__ */ new Map();
44
+ for (const n of r) {
45
+ t[n.channel] = n.instrument;
46
+ let c = 0;
47
+ for (const i of n.notes.split(",").map((d) => d.trim()).filter(Boolean)) {
48
+ const d = /^(.+?)\s+([whqest])(\.?)$/.exec(i);
49
+ if (!d) throw new Error(`Bad note "${i}"`);
50
+ const f = k[d[2]] * (d[3] ? 1.5 : 1) * a;
51
+ d[1].replace(/[[\]]/g, " ").trim().split(/\s+/).forEach((R, u) => {
52
+ const v = M(R);
53
+ if (v === null) return;
54
+ const h = Math.min(15, n.channel + u);
55
+ u > 0 && l.set(h, n.instrument);
56
+ const A = v + (n.transpose ?? 0);
57
+ o.push({ tick: c, data: [144 | h, A, n.velocity ?? 100] }), o.push({ tick: c + f * 0.92, data: [128 | h, A, 0] });
58
+ }), c += f;
59
+ }
60
+ }
61
+ for (const [n, c] of l) t[n] ??= c;
62
+ for (let n = 0; n < 16; n++) t[n] ??= C.piano;
63
+ return S({ instruments: t, events: o, ticksPerBeat: a });
64
+ }
65
+ function j(e = 120) {
66
+ const r = Math.round(8760 / e), a = Array.from({ length: 16 }, () => C.piano);
67
+ return S({ instruments: a, events: [{ tick: 4 * r, data: [176, 7, 100] }], ticksPerBeat: r });
68
+ }
69
+ const w = { w: 4, h: 2, q: 1, e: 0.5, s: 0.25, t: 1 / 3 }, E = (e) => e.split(",").map((r) => r.trim()).filter(Boolean).reduce((r, a) => r + w[a.endsWith(".") ? a.slice(-2, -1) : a.slice(-1)] * (a.endsWith(".") ? 1.5 : 1), 0);
70
+ function q(e, r) {
71
+ const a = r.map((t) => E(t.notes));
72
+ if (a.some((t) => Math.abs(t - a[0]) > 1e-6)) throw new Error(`Voices differ in length: ${a.join(" ")}`);
73
+ return g(e, r);
74
+ }
75
+ const P = (e, r, a, t = 100, o = 0) => ({
76
+ channel: e,
77
+ instrument: r,
78
+ notes: a,
79
+ velocity: t,
80
+ transpose: o
81
+ }), U = (...e) => e.filter(Boolean).join(", "), F = (e, r) => Array(e).fill(r).join(", ");
82
+ function m(e) {
83
+ return Uint16Array.from({ length: p }, (r, a) => Math.max(0, Math.min(8192, Math.round(e(a, a / (p - 1))))));
84
+ }
85
+ const T = {
86
+ rise: (e = 200, r = 2e3) => m((a, t) => e * (r / e) ** t),
87
+ fall: (e = 2e3, r = 150) => m((a, t) => e * (r / e) ** t),
88
+ coin: () => m((e) => e < 4 ? 988 : e < 18 ? 1319 : 0),
89
+ hurt: () => m((e) => (e % 2 ? 140 : 95) * (1 - e / 60)),
90
+ zap: () => m((e) => 3e3 - e * 75 + e % 3 * 300),
91
+ chirp: () => m((e) => e < 12 ? 1200 + e * 120 : 0),
92
+ arpeggio: (e = [523, 659, 784, 1047]) => m((r) => r < e.length * 6 ? e[Math.floor(r / 6)] : 0),
93
+ noise: (e = 1, r = 100, a = 2e3) => {
94
+ const t = b(e);
95
+ return m((o) => o < 30 ? r + t() * (a - r) : 0);
96
+ },
97
+ warble: (e = 600, r = 300, a = 1.3) => m((t) => e + Math.sin(t * a) * r)
98
+ }, B = {
99
+ sine: (e) => {
100
+ let r = 0, a = 0;
101
+ return (t) => {
102
+ const o = typeof e == "number" ? e : e(t);
103
+ return r += 2 * Math.PI * o * (t - a), a = t, Math.sin(r);
104
+ };
105
+ },
106
+ square: (e) => {
107
+ const r = B.sine(e);
108
+ return (a) => r(a) >= 0 ? 1 : -1;
109
+ },
110
+ noise: (e = 7) => {
111
+ const r = b(e);
112
+ return () => r() * 2 - 1;
113
+ }
114
+ };
115
+ function _(e, r, a = {}) {
116
+ const t = a.rate ?? 11025, o = Math.round(e * t), l = new Float32Array(o), n = a.attack ?? 5e-3;
117
+ for (let c = 0; c < o; c++) {
118
+ const i = c / t, d = a.envelope ? a.envelope(i / e) : Math.min(1, i / n) * (1 - i / e) ** 1.5;
119
+ l[c] = Math.max(-1, Math.min(1, r(i) * d * (a.gain ?? 0.9)));
120
+ }
121
+ return L(l, t);
122
+ }
123
+ const G = (...e) => (r) => e.reduce((a, [t, o]) => a + t(r) * o, 0);
124
+ export {
125
+ C as INSTRUMENTS,
126
+ w as LEN,
127
+ T as SPEAKER,
128
+ j as blankSong,
129
+ U as join,
130
+ M as midi,
131
+ G as mix,
132
+ B as osc,
133
+ _ as sample,
134
+ s as sbi,
135
+ g as song,
136
+ m as speaker,
137
+ F as times,
138
+ q as tune,
139
+ P as v
140
+ };
@@ -0,0 +1,120 @@
1
+ const n = new TextDecoder("latin1");
2
+ class h {
3
+ bytes;
4
+ offset;
5
+ constructor(t, s = 0) {
6
+ this.bytes = t, this.offset = s;
7
+ }
8
+ get length() {
9
+ return this.bytes.length;
10
+ }
11
+ get eof() {
12
+ return this.offset >= this.bytes.length;
13
+ }
14
+ skip(t) {
15
+ return this.offset += t, this;
16
+ }
17
+ u8() {
18
+ const t = this.offset < this.bytes.length ? this.bytes[this.offset] : 0;
19
+ return this.offset++, t;
20
+ }
21
+ i8() {
22
+ const t = this.u8();
23
+ return t > 127 ? t - 256 : t;
24
+ }
25
+ u16() {
26
+ return this.u8() | this.u8() << 8;
27
+ }
28
+ i16() {
29
+ const t = this.u16();
30
+ return t > 32767 ? t - 65536 : t;
31
+ }
32
+ u32() {
33
+ return (this.u16() | this.u16() << 16) >>> 0;
34
+ }
35
+ i32() {
36
+ return this.u16() | this.u16() << 16;
37
+ }
38
+ /** Copies n bytes (zero padded when the source is short). */
39
+ bytesOf(t) {
40
+ const s = new Uint8Array(t), e = Math.max(0, Math.min(t, this.bytes.length - this.offset));
41
+ return e > 0 && s.set(this.bytes.subarray(this.offset, this.offset + e)), this.offset += t, s;
42
+ }
43
+ /** Reads a fixed size, NUL terminated string field. */
44
+ cstr(t) {
45
+ return u(this.bytesOf(t));
46
+ }
47
+ }
48
+ class f {
49
+ buf;
50
+ offset = 0;
51
+ constructor(t = 1024) {
52
+ this.buf = new Uint8Array(t);
53
+ }
54
+ ensure(t) {
55
+ if (this.offset + t <= this.buf.length) return;
56
+ let s = this.buf.length * 2;
57
+ for (; s < this.offset + t; ) s *= 2;
58
+ const e = new Uint8Array(s);
59
+ e.set(this.buf), this.buf = e;
60
+ }
61
+ u8(t) {
62
+ return this.ensure(1), this.buf[this.offset++] = t & 255, this;
63
+ }
64
+ // Signed writes store the same two's complement bytes as the unsigned ones.
65
+ i8(t) {
66
+ return this.u8(t);
67
+ }
68
+ u16(t) {
69
+ return this.u8(t).u8(t >> 8);
70
+ }
71
+ i16(t) {
72
+ return this.u16(t);
73
+ }
74
+ u32(t) {
75
+ return this.u16(t & 65535).u16(t >>> 16 & 65535);
76
+ }
77
+ i32(t) {
78
+ return this.u32(t >>> 0);
79
+ }
80
+ bytes(t, s = t.length) {
81
+ this.ensure(s);
82
+ for (let e = 0; e < s; e++) this.buf[this.offset++] = e < t.length ? t[e] & 255 : 0;
83
+ return this;
84
+ }
85
+ /** Writes a string into a fixed size NUL padded field (truncated to leave room for the NUL). */
86
+ cstr(t, s) {
87
+ const e = new Uint8Array(s);
88
+ for (let i = 0; i < Math.min(t.length, s - 1); i++) e[i] = t.charCodeAt(i) & 255;
89
+ return this.bytes(e);
90
+ }
91
+ /**
92
+ * Writes a fixed size string field. When the text is unchanged the original raw
93
+ * bytes (including any garbage after the NUL) are kept so files round-trip exactly.
94
+ */
95
+ cstrOver(t, s, e) {
96
+ return s && s.length === e && u(s) === t ? this.bytes(s) : this.cstr(t, e);
97
+ }
98
+ toBytes() {
99
+ return this.buf.slice(0, this.offset);
100
+ }
101
+ }
102
+ function u(r) {
103
+ const t = r.indexOf(0);
104
+ return n.decode(t < 0 ? r : r.subarray(0, t));
105
+ }
106
+ function o(r) {
107
+ return n.decode(r);
108
+ }
109
+ function c(r) {
110
+ const t = new Uint8Array(r.length);
111
+ for (let s = 0; s < r.length; s++) t[s] = r.charCodeAt(s) & 255;
112
+ return t;
113
+ }
114
+ export {
115
+ h as R,
116
+ f as W,
117
+ u as c,
118
+ o as l,
119
+ c as t
120
+ };