@typeonce/ritmo-tools 0.18.0 → 0.19.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 (131) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +304 -24
  3. package/dist/cli.js +5 -1
  4. package/dist/cli.js.map +1 -1
  5. package/dist/internal/version.d.ts +1 -1
  6. package/dist/internal/version.js +1 -1
  7. package/dist/music/cli/commands.d.ts +15 -0
  8. package/dist/music/cli/commands.d.ts.map +1 -0
  9. package/dist/music/cli/commands.js +180 -0
  10. package/dist/music/cli/commands.js.map +1 -0
  11. package/dist/music/cli/io.d.ts +11 -0
  12. package/dist/music/cli/io.d.ts.map +1 -0
  13. package/dist/music/cli/io.js +28 -0
  14. package/dist/music/cli/io.js.map +1 -0
  15. package/dist/music/cli/serve.d.ts +13 -0
  16. package/dist/music/cli/serve.d.ts.map +1 -0
  17. package/dist/music/cli/serve.js +168 -0
  18. package/dist/music/cli/serve.js.map +1 -0
  19. package/dist/music/core/audio.d.ts +22 -0
  20. package/dist/music/core/audio.d.ts.map +1 -0
  21. package/dist/music/core/audio.js +95 -0
  22. package/dist/music/core/audio.js.map +1 -0
  23. package/dist/music/core/check.d.ts +51 -0
  24. package/dist/music/core/check.d.ts.map +1 -0
  25. package/dist/music/core/check.js +113 -0
  26. package/dist/music/core/check.js.map +1 -0
  27. package/dist/music/core/instruments.d.ts +179 -0
  28. package/dist/music/core/instruments.d.ts.map +1 -0
  29. package/dist/music/core/instruments.js +531 -0
  30. package/dist/music/core/instruments.js.map +1 -0
  31. package/dist/music/core/listen.d.ts +32 -0
  32. package/dist/music/core/listen.d.ts.map +1 -0
  33. package/dist/music/core/listen.js +350 -0
  34. package/dist/music/core/listen.js.map +1 -0
  35. package/dist/music/core/midi.d.ts +4 -0
  36. package/dist/music/core/midi.d.ts.map +1 -0
  37. package/dist/music/core/midi.js +76 -0
  38. package/dist/music/core/midi.js.map +1 -0
  39. package/dist/music/core/render.d.ts +37 -0
  40. package/dist/music/core/render.d.ts.map +1 -0
  41. package/dist/music/core/render.js +204 -0
  42. package/dist/music/core/render.js.map +1 -0
  43. package/dist/music/core/review.d.ts +41 -0
  44. package/dist/music/core/review.d.ts.map +1 -0
  45. package/dist/music/core/review.js +139 -0
  46. package/dist/music/core/review.js.map +1 -0
  47. package/dist/music/core/song.d.ts +144 -0
  48. package/dist/music/core/song.d.ts.map +1 -0
  49. package/dist/music/core/song.js +291 -0
  50. package/dist/music/core/song.js.map +1 -0
  51. package/dist/music/page/assets/index-BfO_jOqP.js +4 -0
  52. package/dist/music/page/assets/index-Dw14kxkL.css +1 -0
  53. package/dist/music/page/index.html +19 -0
  54. package/dist/pixel/cli/commands.d.ts.map +1 -1
  55. package/dist/pixel/cli/commands.js +122 -46
  56. package/dist/pixel/cli/commands.js.map +1 -1
  57. package/dist/pixel/cli/io.d.ts +5 -1
  58. package/dist/pixel/cli/io.d.ts.map +1 -1
  59. package/dist/pixel/cli/io.js +16 -1
  60. package/dist/pixel/cli/io.js.map +1 -1
  61. package/dist/pixel/core/animation.d.ts +23 -8
  62. package/dist/pixel/core/animation.d.ts.map +1 -1
  63. package/dist/pixel/core/animation.js +41 -18
  64. package/dist/pixel/core/animation.js.map +1 -1
  65. package/dist/pixel/core/grid.d.ts +3 -2
  66. package/dist/pixel/core/grid.d.ts.map +1 -1
  67. package/dist/pixel/core/grid.js +14 -6
  68. package/dist/pixel/core/grid.js.map +1 -1
  69. package/dist/pixel/core/preview.d.ts +8 -3
  70. package/dist/pixel/core/preview.d.ts.map +1 -1
  71. package/dist/pixel/core/preview.js +39 -25
  72. package/dist/pixel/core/preview.js.map +1 -1
  73. package/dist/pixel/core/project.d.ts +27 -0
  74. package/dist/pixel/core/project.d.ts.map +1 -0
  75. package/dist/pixel/core/project.js +117 -0
  76. package/dist/pixel/core/project.js.map +1 -0
  77. package/dist/pixel/core/recipe.d.ts.map +1 -1
  78. package/dist/pixel/core/recipe.js +43 -6
  79. package/dist/pixel/core/recipe.js.map +1 -1
  80. package/dist/pixel/core/report.d.ts +21 -3
  81. package/dist/pixel/core/report.d.ts.map +1 -1
  82. package/dist/pixel/core/report.js +52 -22
  83. package/dist/pixel/core/report.js.map +1 -1
  84. package/dist/pixel/core/sheet.d.ts +68 -8
  85. package/dist/pixel/core/sheet.d.ts.map +1 -1
  86. package/dist/pixel/core/sheet.js +250 -32
  87. package/dist/pixel/core/sheet.js.map +1 -1
  88. package/dist/pixel/core/spritesheet.d.ts +43 -35
  89. package/dist/pixel/core/spritesheet.d.ts.map +1 -1
  90. package/dist/pixel/core/spritesheet.js +63 -59
  91. package/dist/pixel/core/spritesheet.js.map +1 -1
  92. package/dist/sound/cli/commands.d.ts +25 -0
  93. package/dist/sound/cli/commands.d.ts.map +1 -0
  94. package/dist/sound/cli/commands.js +137 -0
  95. package/dist/sound/cli/commands.js.map +1 -0
  96. package/dist/sound/cli/io.d.ts +24 -0
  97. package/dist/sound/cli/io.d.ts.map +1 -0
  98. package/dist/sound/cli/io.js +51 -0
  99. package/dist/sound/cli/io.js.map +1 -0
  100. package/dist/sound/cli/serve.d.ts +11 -0
  101. package/dist/sound/cli/serve.d.ts.map +1 -0
  102. package/dist/sound/cli/serve.js +147 -0
  103. package/dist/sound/cli/serve.js.map +1 -0
  104. package/dist/sound/core/measure.d.ts +67 -0
  105. package/dist/sound/core/measure.d.ts.map +1 -0
  106. package/dist/sound/core/measure.js +138 -0
  107. package/dist/sound/core/measure.js.map +1 -0
  108. package/dist/sound/core/recipe.d.ts +143 -0
  109. package/dist/sound/core/recipe.d.ts.map +1 -0
  110. package/dist/sound/core/recipe.js +292 -0
  111. package/dist/sound/core/recipe.js.map +1 -0
  112. package/dist/sound/core/render.d.ts +37 -0
  113. package/dist/sound/core/render.d.ts.map +1 -0
  114. package/dist/sound/core/render.js +317 -0
  115. package/dist/sound/core/render.js.map +1 -0
  116. package/dist/sound/core/review.d.ts +52 -0
  117. package/dist/sound/core/review.d.ts.map +1 -0
  118. package/dist/sound/core/review.js +89 -0
  119. package/dist/sound/core/review.js.map +1 -0
  120. package/dist/sound/core/takes.d.ts +29 -0
  121. package/dist/sound/core/takes.d.ts.map +1 -0
  122. package/dist/sound/core/takes.js +109 -0
  123. package/dist/sound/core/takes.js.map +1 -0
  124. package/dist/sound/core/wav.d.ts +4 -0
  125. package/dist/sound/core/wav.d.ts.map +1 -0
  126. package/dist/sound/core/wav.js +28 -0
  127. package/dist/sound/core/wav.js.map +1 -0
  128. package/dist/sound/page/assets/index-Cjun5b92.js +6 -0
  129. package/dist/sound/page/assets/index-DZm59wkQ.css +1 -0
  130. package/dist/sound/page/index.html +19 -0
  131. package/package.json +10 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # @typeonce/ritmo-tools
2
2
 
3
+ ## 0.19.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 6e69a51: `ritmo-tools pixel`: project PNGs. A project PNG is the source picture with its recipe stored inside (one uncompressed `iTXt` chunk, keyword `ritmo-pixel-recipe`; the pixels are untouched), so one file reopens a whole session. The web page exports one (Export → **Project PNG**, for pictures and animation sheets) and restores every setting when it opens one. `pixel fix --project` writes `<name>.project.png`, and `check`, `fix`, `sheet`, and `preview` run the recipe a project PNG holds (a `--recipe` file wins over it; the report says `"storedRecipe": true`). A project PNG is not a game asset: `exiftool -all=` or `oxipng --strip safe` turns it back into the plain picture.
8
+
9
+ Grid detection finds cells that a screenshot or resize stretched a few percent on one axis, instead of falling back to a fraction of the cell (a sprite came out 25×68 instead of 28×32).
10
+
11
+ The `ritmo-pixel-art` skill says when to hand a person a project PNG, and how to spot and strip one.
12
+
13
+ - 612c30a: `ritmo-tools pixel`: several animations on one sheet. Each row of frames is one animation by default (`animation-1`, ... in reading order; where two blocks sit side by side, a row across both is split where they meet). The page's **Animations** panel chooses which are exported (**In**/**Out**), plays, renames, and sets each one's speed and loop, and **Split here** (between two frames of the chosen animation) and **Join with** regroup them. Each animation gets its own canvas. The sheet PNG holds every exported animation, each from a new row; its JSON names each one under `animations` and `frameTags`, frames are named `<animation> 0`, ..., and `meta.fps` is a map from animation to frames per second. `pixel sheet` writes a GIF per animation (`<name>.<animation>.gif`) and frames under `.frames/<animation>/`, lists the animations in its report (`animation.animations`), and takes `--animations "walk=1-4;explode=5-14"` and `--only walk,explode`.
14
+
15
+ Breaking, for recipes and code reading the sheet: `sheet.fps` and `sheet.loop` moved onto each animation (`sheet.animations[i].fps`, `.loop`). A recipe without `animations` still reads, as one animation of every frame at its `fps` and `loop`. The report's `animation.fps` and `animation.loop` are now per animation in `animation.animations`, and `output` is the size of the sheet written.
16
+
17
+ The `ritmo-pixel-art` skill says how to name and pick animations with `--animations` and `--only`.
18
+
19
+ - 832169a: `ritmo-tools music`: game music as JSON songs (a tempo, a key, bars, and tracks of notes, each track one of sixteen synthesized instruments: piano, electric piano, acoustic and distorted guitar, bass guitar and a reese bass, strings, flute, marimba, bells, a pad, a supersaw lead, two chiptune voices, and a clean and a punchy drum kit; `"compressor": true` squeezes a rock or electronic song louder and punchier). `music listen <song> <audio.wav>` hears a hum, a whistle, a tapped beat, or a recording and writes its tempo, key, and melody into the song as its reference, for the agent to build the song around; a voice that sits off pitch the whole way is heard in tune. `music check` measures the song and lists what usually sounds wrong (clipping, notes outside the key or an instrument's range, a buried track, a melody that wandered from the reference); `music render` writes a seamless looping WAV, a MIDI file, and with `--stems` each track alone. `music review <song>` opens a local page where a person plays the song, records a hum from the microphone, edits notes on a grid, swaps instruments, and comments on bars; `music notes` gives the agent the comments and a list of the person's edits.
20
+
21
+ `@typeonce/ritmo` ships a new skill, `skills/ritmo-music`. Link it next to the others:
22
+
23
+ ```sh
24
+ ln -s ../../node_modules/@typeonce/ritmo/skills/ritmo-music .agents/skills/ritmo-music
25
+ ```
26
+
27
+ - 818ad0b: `ritmo-tools sound`: game sounds as JSON recipes. Agents write sounds into one bank file, check them (`sound check`: length, loudness, clipping, a late start, a click at the end, much louder or quieter than their kind), make takes nudged one way (`sound vary --toward heavier`), and write WAV files (`sound render`). `sound review <bank>` opens a local page where a person listens, picks among takes, tunes with sliders, and approves or notes each sound; `sound notes` gives the agent the person's answers.
28
+
29
+ `@typeonce/ritmo` ships a new skill, `skills/ritmo-sounds`. Link it next to the others:
30
+
31
+ ```sh
32
+ ln -s ../../node_modules/@typeonce/ritmo/skills/ritmo-sounds .agents/skills/ritmo-sounds
33
+ ```
34
+
35
+ ### Patch Changes
36
+
37
+ - 793a5f3: The pixel fixer's page opens pixel art that is already at its own size (a small sprite, even with an image generator's noisy colors) one pixel per cell, as `ritmo-tools pixel check` does, instead of applying an unsure grid its own shapes suggest (a 24×24 sprite came out about 8×8).
38
+
39
+ Animation sheets: frames are found on sheets made of blocks side by side whose rows do not line up (walking on the left, throwing on the right), instead of boxing frames from two rows together, and a last frame of a few pixels (the end of an explosion) is kept instead of dropped. Such a sheet opens as an animation: most frames alike is enough, so a few faint frames (sparks, smoke) no longer make the page open it as a single picture.
40
+
3
41
  ## 0.18.0
4
42
 
5
43
  ### Minor Changes
package/README.md CHANGED
@@ -7,6 +7,8 @@ tool per kind of asset:
7
7
  | Tool | For |
8
8
  | --- | --- |
9
9
  | `ritmo-tools pixel` | Pixel art: fixes AI-made or blurry pictures and animation sheets onto a true pixel grid and a palette, renders sprites drawn as text, and checks a project's art against its rules. |
10
+ | `ritmo-tools sound` | Sound effects: keeps them as JSON recipes, measures and varies them, writes WAV files, and opens a local page where a person listens, picks, tunes, and notes. |
11
+ | `ritmo-tools music` | Game music: hears a person's hum for its tune, tempo, and key, keeps songs as JSON tracks of notes, checks them, writes WAV and MIDI files, and opens a local page where a person plays, edits notes, records, and comments. |
10
12
 
11
13
  ```sh
12
14
  npx @typeonce/ritmo-tools pixel check art.png
@@ -15,7 +17,9 @@ pnpm add -D @typeonce/ritmo-tools # pins it to the game's Ritmo version
15
17
 
16
18
  Every command prints JSON on stdout and exits with a code that says what
17
19
  to do next. A game's agent learns them from the skills that ship with
18
- `@typeonce/ritmo` (`skills/ritmo-pixel-art`), linked by its `SETUP.md`.
20
+ `@typeonce/ritmo` (`skills/ritmo-pixel-art`, `skills/ritmo-sounds`,
21
+ `skills/ritmo-music`), linked by
22
+ its `SETUP.md`.
19
23
  The tools are built on Effect; nothing a game imports depends on this
20
24
  package.
21
25
 
@@ -53,7 +57,8 @@ pnpm --dir website build # the site, with the page at dist/tools/pixel/
53
57
  ```
54
58
 
55
59
  Open a picture (PNG or JPEG: **Open** in the top bar, drag and drop, or paste), or press
56
- **Try the demo** for a fake AI picture of a mushroom. The same button opens a sprite source
60
+ **Try the demo** for a fake AI picture of a mushroom. The same button opens a project PNG
61
+ (the picture with every setting it was saved with), a sprite source
57
62
  (`.txt`) or a JSON map as pixel art at its own size, a recipe (applied to the
58
63
  picture open), and a `pixel.config.json` (its rules, palette, and settings).
59
64
 
@@ -157,7 +162,8 @@ node packages/tools/bin/ritmo-tools.mjs pixel check art.png # from its source,
157
162
  units with crisp edges (it stays sharp at any size; same-colored areas are
158
163
  merged into rectangles); a JSON map (size, palette, one row of palette
159
164
  indices per pixel row, -1 for transparent); the palette as `.hex` or `.gpl`;
160
- **Sprite text** (the sprite source format below); and the **Recipe**.
165
+ **Sprite text** (the sprite source format below); the **Recipe**; and the
166
+ **Project PNG** (below).
161
167
 
162
168
  **Undo** and **Redo** (in the top bar, or Ctrl/Cmd+Z and Ctrl/Cmd+Shift+Z
163
169
  or Ctrl+Y) cover every setting: the grid, the palette, the background,
@@ -167,25 +173,41 @@ step. Opening another picture starts the history over.
167
173
  ### Animations
168
174
 
169
175
  An image generator asked for an animation draws a sheet: the frames side by
170
- side (or in rows), each in a box, over a dark background with a painted grid,
176
+ side (or in rows, often one animation per row: walking each way, throwing,
177
+ exploding), each in a box, over a dark background with a painted grid,
171
178
  often with a label under each frame. Open it like any picture: when the
172
- fixer finds several boxes holding about as much art each, it opens the sheet
173
- as an **Animation** and says so. **Picture** and **Animation** in the top bar
179
+ fixer finds several boxes, most of them holding about as much art each (a
180
+ few faint ones, like the last smoke of an explosion, do not count against
181
+ it), it opens the sheet as an **Animation** and says so. **Picture** and **Animation** in the top bar
174
182
  switch between the two at any time; everything about pictures above stays as
175
183
  it is.
176
184
 
177
185
  - **Frames.** The sheet's background is cleared from its border (the painted
178
186
  grid and the boxes with it), and each run of art is a frame: rows of art
179
187
  are split where the columns empty out, and short rows (the labels) are
180
- dropped. A grid painted behind the frames is painted out first, where it
188
+ dropped. A box that still holds frames one over another (blocks side by
189
+ side whose rows do not line up) is cut into rows again, and a frame of a
190
+ few pixels set apart like the others (the last wisp of smoke) stays a
191
+ frame. A grid painted behind the frames is painted out first, where it
181
192
  runs over the background, so lines as dark as the art's outline never
182
193
  flood into the art; how close a color must be to the background follows
183
194
  how noisy the background is. The **Sheet** view shows the source with every frame's box,
184
195
  numbered. Choose **Boxes** (F) to drag a box, resize it by its handles, or
185
196
  draw a new one on empty space when something was cut out; Delete removes the
186
- selected box. **Frames** on the right lists every frame: **In**/**Out**
187
- leaves one out of the animation without losing its box, and **Find again**
188
- starts over (boxes changed by hand are dropped).
197
+ selected box. Each box is tinted with its animation's color.
198
+ - **Animations.** Each row of frames is one animation, named
199
+ `animation-1`, `animation-2`, ... in reading order; where two blocks sit
200
+ side by side (walking on the left, throwing on the right), a row across
201
+ both is split where they meet, when its two parts hold as many frames as
202
+ the blocks' rows. **Animations** on the right lists them: **In**/**Out**
203
+ chooses which are exported (the others keep their frames and settings);
204
+ choose one to play it, rename it (the name a game plays it by), and set its
205
+ speed and loop. Its frames are listed under it: **In**/**Out** leaves one
206
+ out without losing its box. **Split here**, on the line between two of
207
+ its frames, cuts the animation in two there (the frames after it become a
208
+ new animation), and **Join with** puts it together with the next one, for
209
+ a sheet whose rows are not its animations. **Find again** starts over
210
+ (boxes and animations changed by hand are dropped).
189
211
  - **Grid and palette.** Every frame is read on one cell size (found from all
190
212
  the frames together, or typed), each at its own offset, and fixed with one
191
213
  recipe: the palette, matching, and cleanup are the picture's settings, and
@@ -196,23 +218,51 @@ it is.
196
218
  height it was drawn, so a hop or a bob stays; **Feet** pins the body's
197
219
  lowest row to one spot (hops are flattened); **Center** pins the body's
198
220
  middle, for things that float; **Box** places frames as cut. Arrow keys nudge the selected frame by a pixel
199
- (Shift: 4). **Margin** sets the empty pixels around the frames.
200
- - **Playing.** The **Play** view shows the animation running, with the grid
221
+ (Shift: 4). Each animation gets its own canvas, the smallest that holds
222
+ its frames; **Margin** sets the empty pixels around them.
223
+ - **Playing.** The **Play** view shows the chosen animation running, with the grid
201
224
  if you want it; Space plays and pauses, `,` and `.` step a frame, and the
202
225
  bar under it sets the frame rate, **Loop** or **Ping-pong**, and **Onion**
203
226
  (the previous frame, faint, under the current one). **Frames** shows them
204
227
  all side by side; click one to select it.
205
- - **Export.** A **Sheet PNG** with every frame in one row or in rows of
206
- **Per row** frames, with **Spacing** between them, at any whole scale; its
228
+ - **Export.** A **Sheet PNG** with every exported animation, each from a
229
+ new row, its frames in one row or in rows of **Per row** frames, with
230
+ **Spacing** between frames and animations, at any whole scale; its
207
231
  **JSON** in the hash format Pixi (`Spritesheet`), Phaser (`atlas`), and
208
- Aseprite share, with each frame's rectangle, duration, and anchor, the
209
- animation's frame list, and Aseprite's `frameTags`; a **GIF**; **Frames
210
- .zip** (one PNG per frame); the **Recipe** (frames, anchor, frame rate, and
211
- loop included); and the palette.
232
+ Aseprite share, with each frame's rectangle, duration, and anchor (frames
233
+ are named `<animation> 0`, `<animation> 1`, ...), each animation's frame
234
+ list under `animations`, and Aseprite's `frameTags`; a **GIF** of the
235
+ chosen animation; **Frames .zip** (one PNG per frame, a folder per
236
+ animation); the **Recipe** (frames, animations, and anchor included); and
237
+ the palette.
212
238
 
213
239
  Hand edits (**Erase**, **Paint**) and **Size** are for single pictures and
214
240
  are not offered for animations.
215
241
 
242
+ ### Project PNGs
243
+
244
+ A **Project PNG** (in Export) is the source picture with the recipe stored
245
+ inside it, so one file holds the whole session: open it on the page and every
246
+ setting comes back, hand edits included. It is how to share work in progress
247
+ or pick it up later; nothing leaves the device unless you send the file. It is
248
+ not a game asset: export the fixed art for that.
249
+
250
+ The picture's pixels are untouched (a PNG opened keeps its own bytes; another
251
+ format is stored as PNG). The recipe is one uncompressed `iTXt` text chunk
252
+ with the keyword `ritmo-pixel-recipe`, right after the header, holding the
253
+ recipe JSON: about 0.5 to 2 KB, more with many hand strokes. Viewers ignore
254
+ it. Some apps re-encode pictures and drop it (chat apps sending a photo as a
255
+ photo): send it as a file. A project PNG without its chunk opens as the plain
256
+ picture.
257
+
258
+ To see the recipe: `exiftool art.png`, `pngcheck -t art.png`, or
259
+ `grep -a ritmo-pixel-recipe art.png`. To remove it: `exiftool -all= art.png`,
260
+ `oxipng --strip safe art.png`, or `pngcrush -rem text in.png out.png`; what is
261
+ left is the plain picture. `ritmo-tools pixel fix --project` writes one from the CLI, and
262
+ `check`, `fix`, `sheet`, and `preview` run the recipe a project PNG holds (a
263
+ `--recipe` file wins over it; the report says `"storedRecipe": true` when it
264
+ ran).
265
+
216
266
  ### The CLI
217
267
 
218
268
  `ritmo-tools pixel` runs the page's pipeline from the command line, for agents. It is
@@ -225,8 +275,8 @@ the source), 4 needs a person (the grid is unsure: look at the preview).
225
275
  | Command | What it does |
226
276
  | --- | --- |
227
277
  | `check <file>` | Clean pixel art, fixable with confidence, or needs a person? Writes nothing. |
228
- | `fix <png>` | Fixes it, writing `.png` (1x), `@Nx.png` (`--scale N`), `.svg`, `.json` map, `.sprite.txt`, `.recipe.json`, and `.preview.png`. |
229
- | `sheet <png>` | Fixes an animation sheet: `.png` (the frames in a strip, or `--columns N` per row), its `.json`, a `.gif`, `.frames/` (one PNG each), `.recipe.json`, and `.preview.png` (the boxes found over the source, and the fixed frames). |
278
+ | `fix <png>` | Fixes it, writing `.png` (1x), `@Nx.png` (`--scale N`), `.svg`, `.json` map, `.sprite.txt`, `.recipe.json`, and `.preview.png`; `--project` also writes `<name>.project.png`. |
279
+ | `sheet <png>` | Fixes an animation sheet: `.png` (each animation's frames in a strip from a new row, or `--columns N` per row), its `.json`, a `.<animation>.gif` each, `.frames/<animation>/` (one PNG per frame), `.recipe.json`, and `.preview.png` (the boxes found over the source, and each animation's fixed frames). The report lists the animations with their frame numbers. |
230
280
  | `pixelate <png> -w N` | Cuts a picture that is not pixel art into N pixels across, on the palette (or `--colors N` proposed from it). |
231
281
  | `render <sprite>` | Renders a sprite source or JSON map to PNG and SVG, and lints it. |
232
282
  | `lint <file>` | The project's rules on the pixels as they are. |
@@ -238,9 +288,11 @@ the source), 4 needs a person (the grid is unsure: look at the preview).
238
288
  `fix`, `check`, `pixelate`, and `preview` take the page's settings as flags
239
289
  (`--palette`, `--cell 8x8 --offset 2,3`, `--follow`, `--cleanup`,
240
290
  `--remove-background`, `--crop`, `--dither`, `--metric`, `--shrink-to`), over a
241
- `--recipe` file, over the config. `ritmo-tools pixel <command> --help` lists them. `sheet` takes them too, plus
242
- `--boxes`, `--skip`, `--anchor`, `--fps`, `--pingpong`, `--columns`, and
243
- `--spacing`. Every command reads PNG and JPEG.
291
+ `--recipe` file (or the recipe a project PNG holds), over the config. `ritmo-tools pixel <command> --help` lists them. `sheet` takes them too, plus
292
+ `--boxes`, `--skip`, `--animations` (which frames make which animation:
293
+ `"walk=1-4;throw=5-8;explode=9-18"`, every frame in one), `--only` (the
294
+ animations to write, by name: `"walk,explode"`), `--anchor`, `--fps`,
295
+ `--pingpong`, `--columns`, and `--spacing`. Every command reads PNG and JPEG.
244
296
 
245
297
  **`pixel.config.json`**, found from the working directory up (or
246
298
  `--config`), holds a project's rules: `palette` (a preset's name, a list of
@@ -311,7 +363,8 @@ of the true size, which are as clean but have half their lines mid-cell.
311
363
  (colors the palette lost), `export.ts` (PNG pixels,
312
364
  SVG, JSON), `demo.ts` (the fake AI picture generator),
313
365
  `pipeline.ts` (the whole fix, from a picture and a recipe: what the page and
314
- the CLI both run), `recipe.ts` (recipes as JSON), `sprite.ts` (sprite
366
+ the CLI both run), `recipe.ts` (recipes as JSON), `project.ts` (project
367
+ PNGs: the recipe in a PNG text chunk), `sprite.ts` (sprite
315
368
  sources), `lint.ts` (the rules), `report.ts` (the check and its verdict),
316
369
  `preview.ts` (pictures to look at), `sheet.ts` (clearing a sheet, finding
317
370
  its frames, anchors, and laying frames on one canvas), `animation.ts` (the
@@ -325,3 +378,230 @@ of the true size, which are as clean but have half their lines mid-cell.
325
378
  JPEG through `jpeg-js`;
326
379
  `src/cli.ts` is `ritmo-tools` itself, and `bin/ritmo-tools.mjs` runs it from
327
380
  its TypeScript source inside the repository.
381
+
382
+ ## Sound lab (`ritmo-tools sound`)
383
+
384
+ Game sounds as JSON recipes, for agents and the people they make games with.
385
+ An agent writes and checks sounds from the command line; a person listens,
386
+ compares, tunes, and notes them on a local page in the pixel fixer's look.
387
+ Both use one renderer, so what the person hears, what the agent measures,
388
+ and what the game plays are the same samples.
389
+
390
+ Its page is built from the pixel fixer's kit, so the two tools look and
391
+ work alike.
392
+
393
+ ### Recipes and banks
394
+
395
+ A recipe is a few layers built from small instruments, mixed into a room:
396
+
397
+ ```json
398
+ {
399
+ "about": "A blow lands: a short thud and a crack.",
400
+ "reverb": 0.12,
401
+ "layers": [
402
+ { "type": "click", "gain": 0.45 },
403
+ { "type": "tone", "wave": "sine", "from": 160, "to": 55, "duration": 0.12, "attack": 0.002, "gain": 0.65 },
404
+ { "type": "crunch", "from": 1500, "to": 500, "duration": 0.07, "gain": 0.3 }
405
+ ]
406
+ }
407
+ ```
408
+
409
+ | Layer | What it is |
410
+ | --- | --- |
411
+ | `tone` | A wave (`sine`, `triangle`, `square`, `sawtooth`) gliding `from` → `to` Hz: thuds, notes, hums. |
412
+ | `noise` | Noise through a filter (`lowpass`, `highpass`, `bandpass`) gliding `from` → `to` Hz, with `q`: air, hiss, rumble. |
413
+ | `swish` | A band of noise rising from `low` to `peak` Hz, loudest at `peakAt`: a swing. |
414
+ | `crunch` | Distorted band-passed noise: the crack of a blow. |
415
+ | `metal` | Inharmonic partials of `pitch` Hz, the higher dying first: a ring. |
416
+ | `click` | A short tick at the very start, for punch. |
417
+
418
+ Every layer has `gain` and an optional `delay` in seconds. A recipe may
419
+ carry `heavy` (it ducks the others in the game's mixer), `interval` (the
420
+ least time between two plays), and a `tune` the person set with sliders
421
+ (`pitch`, `length`, `brightness`, `echo`, `volume`), kept apart from the
422
+ layers so it reads at a glance.
423
+
424
+ A bank is one file of named recipes and the game's master volume:
425
+ `{"version": 1, "master": 0.6, "sounds": {...}}`. Its review lives next to
426
+ it (`game.sounds.json` → `game.sounds.review.json`): each sound's status
427
+ (`new`, `approved`, `changes`), the person's note, and the takes offered.
428
+ `examples/spirit-call.sounds.json` in the repository is Spirit call's 46 sounds.
429
+
430
+ ### The command line
431
+
432
+ `npx @typeonce/ritmo-tools sound <command>` (inside the Ritmo repository, `node packages/tools/bin/ritmo-tools.mjs sound <command>`).
433
+ Every command prints JSON and exits 0 (clean), 2 (usage error, missing
434
+ file, broken bank), or 3 (a sound has errors).
435
+
436
+ | Command | Does |
437
+ | --- | --- |
438
+ | `ritmo-tools sound init [bank]` | Writes a starting bank of three sounds. |
439
+ | `ritmo-tools sound check <bank> [names...]` | Measures each sound (length, peak, loudness, attack, brightness, ending) and lists what breaks the rules: clipping, silence, a late start, a click at the end, much louder or quieter than its kind. |
440
+ | `ritmo-tools sound render <bank> [names...] [-o dir] [--sequence 0.25]` | Writes WAV files, and with `--sequence` the sounds in a row. |
441
+ | `ritmo-tools sound vary <bank> <name> --toward heavier [--count 4] [--seed 1]` | Makes takes nudged one way (`heavier`, `lighter`, `brighter`, `darker`, `punchier`, `softer`, `shorter`, `longer`, or `wider` for anything) and offers them in the review. |
442
+ | `ritmo-tools sound review <bank> [--port 5190] [--host]` | Serves the review page and prints its address; runs until stopped. |
443
+ | `ritmo-tools sound notes <bank>` | What the person said: status and note for each sound, changes asked first. |
444
+
445
+ ### The review page
446
+
447
+ `ritmo-tools sound review` opens the bank on a local page: the sounds on the left with
448
+ their status, the selected sound's wave and layers on the stage with the
449
+ takes beside each other, and on the right its review, its check, the takes
450
+ maker, tune sliders, and the layers' numbers (takes and layers start closed).
451
+ Space plays, the arrows move through the list, 1 to 9 play the takes, and
452
+ Ctrl/Cmd+Z undoes. Every change is written to the bank and its review as it
453
+ happens, and changes the agent writes while the page is open show up in it.
454
+ `--host` serves it on the network too, to listen on a phone.
455
+
456
+ ### Code
457
+
458
+ - `src/sound/core/`: recipes (`recipe.ts`), the renderer (`render.ts`, plain
459
+ TypeScript that plays the same in Node and the browser), the measures and
460
+ checks (`measure.ts`), takes (`takes.ts`), reviews (`review.ts`), and WAV
461
+ files (`wav.ts`).
462
+ - `src/sound/cli/`: the `sound` command (Effect), and the review page's
463
+ server (`serve.ts`).
464
+ - `src/sound/ui/`: the review page, built from the pixel fixer's kit. The
465
+ published package carries it built (`dist/sound/page`, made by `prepack`);
466
+ inside the repository Vite serves it from source.
467
+
468
+ ## Music lab (`ritmo-tools music`)
469
+
470
+ Game music as JSON songs, for agents and the people they make games with,
471
+ most of whom are not musicians. The person hums the tune they have in mind
472
+ (or gives a recording); the tool listens and writes down its tempo, key, and
473
+ melody; the agent builds a song around it; the person plays it, edits notes
474
+ on a grid, swaps instruments, and comments on bars, on a local page in the
475
+ pixel fixer's look. One renderer serves the page, the checks, and the WAV,
476
+ so what the person hears is what the agent measured.
477
+
478
+ ### Songs
479
+
480
+ ```json
481
+ {
482
+ "version": 1,
483
+ "about": "Village at dusk: the hum's tune on a flute over picked guitar.",
484
+ "tempo": 93,
485
+ "key": "D minor",
486
+ "bars": 8,
487
+ "master": 0.65,
488
+ "tracks": [
489
+ { "name": "Melody", "instrument": "flute", "volume": 0.6, "reverb": 0.35, "notes": [
490
+ { "at": 0, "length": 1, "pitch": "D5" },
491
+ { "at": 1, "length": 0.5, "pitch": "F5", "velocity": 0.7 }
492
+ ] },
493
+ { "name": "Drums", "instrument": "drums", "volume": 0.9, "reverb": 0.15, "notes": [
494
+ { "at": 0, "length": 0.5, "pitch": "kick" }
495
+ ] }
496
+ ]
497
+ }
498
+ ```
499
+
500
+ Time is in beats from the start (`at: 4` is bar 2 in 4/4); `length` is in
501
+ beats; `pitch` is a note name (`"C4"` is middle C, `"F#3"`, `"Bb2"`) or, on a
502
+ drum track, a drum (`kick`, `snare`, `hat`, `openHat`, `clap`, `tomLow`,
503
+ `tomHigh`, `crash`, `rim`, `shaker`); `velocity` (0 to 1, default 0.8) is how
504
+ hard a note is played. A song has `beatsPerBar` (default 4), `loop` (default
505
+ true: the WAV is one pass long and its tail wraps into its start, so it
506
+ repeats without a seam), `master`, and `compressor` (default false: true
507
+ squeezes the whole mix louder and punchier, its peaks held under -1 dBFS,
508
+ for rock, drum & bass, and electronic songs; stems stay uncompressed, for
509
+ the game to mix); a track has `volume`, `pan` (-1 to
510
+ 1), `reverb` (0 to 1), and `mute`. The key is a note and a scale: `major`,
511
+ `minor`, `harmonic minor`, `dorian`, `phrygian`, `lydian`, `mixolydian`,
512
+ `major pentatonic`, `minor pentatonic`, `blues`.
513
+
514
+ The instruments are synthesized from small models of the real ones (a
515
+ plucked string, struck strings, a breath through a tube), so they need no
516
+ sample files and play the same bits everywhere:
517
+
518
+ | Instrument | For |
519
+ | --- | --- |
520
+ | `piano`, `epiano` | Chords, melodies; the electric piano for mellow, lo-fi tunes. |
521
+ | `guitar`, `bass` | Picked or strummed chords; the low line under them. |
522
+ | `rockGuitar` | An overdriven electric guitar: riffs and power chords (a note and the one seven semitones up). |
523
+ | `reese` | The drum & bass bass: detuned saws, a sub, and grit; long notes. |
524
+ | `strings`, `pad` | Long held chords that swell in. |
525
+ | `flute`, `marimba`, `bells` | Melodies; short bright patterns; sparkle. |
526
+ | `synthLead` | A supersaw: modern electronic leads and riffs. |
527
+ | `chipLead`, `chipBass` | Chiptune: a retro console's square and triangle waves. |
528
+ | `drums`, `breaks` | A clean drum kit; a punchy, driven one for rock and drum & bass (the same drums). |
529
+
530
+ A song's `reference` is what `music listen` heard: its `tempo`, `key`, the
531
+ melody as `notes` rounded to the grid, the raw pitch line (`contour`), and the
532
+ audio file it came from. The review lives next to the song
533
+ (`theme.song.json` → `theme.song.review.json`): the status (`new`,
534
+ `approved`, `changes`), a note, comments pinned to bars (and a track), and
535
+ the person's edits since the agent last wrote the song, a line each.
536
+ `examples/village.song.json` in the repository is a song built around a
537
+ hum (`examples/village.song.reference.wav`, a synthesized test hum).
538
+
539
+ ### Listening
540
+
541
+ `music listen` follows the strongest pitch moment to moment (YIN, the
542
+ standard pitch tracker for one voice), cuts it into notes where the pitch
543
+ jumps or the voice restarts, finds the tempo whose beats line up best with
544
+ where the notes start, and the key whose notes match how long each pitch
545
+ sounds (Krumhansl's key profiles, over the whole spectrum). A voice that
546
+ sits off pitch the whole way, as most people's do, is heard in tune: the
547
+ offset (`tuning`, in cents, in the report) is measured over all its notes
548
+ and taken out before they are named. It hears one voice (a hum, a
549
+ whistle, a lead line): on a full song or in a noisy room it finds a tempo
550
+ and a key but few notes, and says so (hum the part alone instead). A tapped
551
+ beat with no tune gives a tempo and no notes. When the song already has a
552
+ tempo and the hum was sung along to it, `--tempo` puts the notes on the
553
+ song's grid.
554
+
555
+ ### The command line
556
+
557
+ `npx @typeonce/ritmo-tools music <command>` (inside the Ritmo repository,
558
+ `node packages/tools/bin/ritmo-tools.mjs music <command>`). Every command
559
+ prints JSON and exits 0 (clean), 2 (usage error, missing file, broken song),
560
+ or 3 (the song clips or is silent).
561
+
562
+ | Command | Does |
563
+ | --- | --- |
564
+ | `ritmo-tools music listen <song> <audio.wav> [--tempo 96]` | Hears a hum, a whistle, or a tapped beat and writes its tempo, key, and melody into the song as its reference; makes the song if it does not exist. |
565
+ | `ritmo-tools music instruments` | Lists the instruments (what each is for, its range), the drums, and the scales. |
566
+ | `ritmo-tools music init [song]` | Writes a starting song: four bars of piano chords, a bass line, and a beat. |
567
+ | `ritmo-tools music check <song>` | Measures the song (length, peak, loudness, each track's loudness and range) and lists what usually sounds wrong: clipping, silence, notes outside the key or an instrument's range, a buried track, a doubled note, and with a reference, how closely the melody follows it. |
568
+ | `ritmo-tools music render <song> [-o file.wav] [--stems]` | Writes the song as a stereo WAV and a MIDI file, and with `--stems` each track alone. |
569
+ | `ritmo-tools music review <song> [--port 5191] [--host]` | Serves the review page and prints its address; runs until stopped. |
570
+ | `ritmo-tools music notes <song>` | What the person said: the status, the note, comments in song order with their bars, and their edits. |
571
+
572
+ ### The review page
573
+
574
+ `ritmo-tools music review` opens the song on a local page: the reference and
575
+ the tracks on the left (mute and solo each), the arrangement (every track
576
+ as a lane under the bar numbers, with the hum's pitch line above them) and
577
+ the chosen track's note grid on the stage, and on the right the review, the
578
+ comments, the track (instrument, volume, pan, room), the song (tempo, key,
579
+ bars, loop, compressor, master), the reference, and the check. On the grid, a click
580
+ places a note (and plays it), a drag moves it, dragging its end stretches
581
+ it, and a double-click or right-click removes it; the key's notes are a
582
+ shade lighter, and the hum's notes show as dashed outlines with its pitch
583
+ line through them. A click on the bar numbers, or on a lane of the
584
+ arrangement, puts the start marker there (while playing, the bar numbers
585
+ jump the music there); dragging across the bar numbers picks bars to
586
+ comment on, and Play starts from them. Record a hum records from the microphone (the page must be on
587
+ localhost or HTTPS); Audio file, or dropping a file on the stage, takes any
588
+ audio the browser plays. Space plays and pauses (Play goes on from where
589
+ it paused), Stop goes back to the start, the microphone
590
+ button plays the hum along with the song, Delete removes the chosen note,
591
+ and Ctrl/Cmd+Z undoes. MIDI and WAV download the song. Every change is
592
+ written to the song and its review as it happens, and a song the agent
593
+ rewrites while the page is open shows up in it.
594
+
595
+ ### Code
596
+
597
+ - `src/music/core/`: songs (`song.ts`), the instruments (`instruments.ts`),
598
+ the renderer (`render.ts`), listening (`listen.ts`), checks (`check.ts`),
599
+ reviews and edits in words (`review.ts`), WAV (`audio.ts`) and MIDI
600
+ (`midi.ts`) files. Plain TypeScript that runs the same in Node and the
601
+ browser.
602
+ - `src/music/cli/`: the `music` command (Effect), and the review page's
603
+ server (`serve.ts`).
604
+ - `src/music/ui/`: the review page: the canvases (`grid.ts`), playing
605
+ (`player.ts`), recording (`record.ts`). The published package carries it
606
+ built (`dist/music/page`, made by `prepack`); inside the repository Vite
607
+ serves it from source.
package/dist/cli.js CHANGED
@@ -3,6 +3,8 @@
3
3
  * `ritmo-tools`: the asset tools for games made with Ritmo, run by agents.
4
4
  *
5
5
  * ritmo-tools pixel <command> checks, fixes, and makes pixel art and animations
6
+ * ritmo-tools sound <command> checks, renders, and varies game sounds, and opens the review page
7
+ * ritmo-tools music <command> listens to a hum, checks and renders songs, and opens the review page
6
8
  *
7
9
  * Every command prints JSON on stdout and exits with what to do next (see
8
10
  * each tool's `--help`). Built on Effect; nothing a game imports depends on it.
@@ -13,7 +15,9 @@ import { CliError, Command } from "effect/cli";
13
15
  import { toolsVersion } from "./internal/version.js";
14
16
  import { Exit, pixel } from "./pixel/cli/commands.js";
15
17
  import { printJson } from "./pixel/cli/io.js";
16
- const tools = Command.make("ritmo-tools").pipe(Command.withDescription("Ritmo's asset tools for agents: `pixel` for pixel art and animations. Each prints JSON and exits with what to do next."), Command.withSubcommands([pixel]));
18
+ import { music } from "./music/cli/commands.js";
19
+ import { sound } from "./sound/cli/commands.js";
20
+ const tools = Command.make("ritmo-tools").pipe(Command.withDescription("Ritmo's asset tools for agents: `pixel` for pixel art and animations, `sound` for sound effects, `music` for game music. Each prints JSON and exits with what to do next."), Command.withSubcommands([pixel, sound, music]));
17
21
  const program = Command.run(tools, { version: toolsVersion }).pipe(Effect.catchTag("CliFailure", (failure) => Effect.gen(function* () {
18
22
  yield* Console.error(`ritmo-tools: ${failure.message}`);
19
23
  if (failure.details !== undefined)
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAE7C,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,IAAI,CAC5C,OAAO,CAAC,eAAe,CAAC,wHAAwH,CAAC,EACjJ,OAAO,CAAC,eAAe,CAAC,CAAC,KAAK,CAAC,CAAC,CACjC,CAAA;AAED,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC,IAAI,CAChE,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC,OAAO,EAAE,EAAE,CACxC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,gBAAgB,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;IACvD,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS;QAAE,KAAK,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;IACnG,OAAO,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAA;AACrC,CAAC,CAAC,CACH;AACD,6EAA6E;AAC7E,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAC9E,CAAA;AAED,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAA"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG;AACH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAE/C,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,IAAI,CAC5C,OAAO,CAAC,eAAe,CAAC,2KAA2K,CAAC,EACpM,OAAO,CAAC,eAAe,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAC/C,CAAA;AAED,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC,IAAI,CAChE,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,CAAC,OAAO,EAAE,EAAE,CACxC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,gBAAgB,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;IACvD,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS;QAAE,KAAK,CAAC,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;IACnG,OAAO,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAA;AACrC,CAAC,CAAC,CACH;AACD,6EAA6E;AAC7E,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAC9E,CAAA;AAED,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAA"}
@@ -3,5 +3,5 @@
3
3
  * `package.json` by `scripts/sync-version.ts` (run by `pnpm version-packages`)
4
4
  * and checked by a test.
5
5
  */
6
- export declare const toolsVersion = "0.18.0";
6
+ export declare const toolsVersion = "0.19.0";
7
7
  //# sourceMappingURL=version.d.ts.map
@@ -3,5 +3,5 @@
3
3
  * `package.json` by `scripts/sync-version.ts` (run by `pnpm version-packages`)
4
4
  * and checked by a test.
5
5
  */
6
- export const toolsVersion = "0.18.0";
6
+ export const toolsVersion = "0.19.0";
7
7
  //# sourceMappingURL=version.js.map
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The `music` commands: the music lab for agents. The same core as the
3
+ * review page (one renderer, one song format), driven from the command
4
+ * line, printing JSON and exiting with a code that says what to do next:
5
+ *
6
+ * - 0: clean (warnings are listed, and are the agent's call)
7
+ * - 2: a usage problem, a missing file, a broken song
8
+ * - 3: errors: the song clips or is silent; fix it
9
+ */
10
+ import { Path } from "effect";
11
+ import { Command } from "effect/cli";
12
+ import { Exit } from "../../sound/cli/commands.ts";
13
+ import { CliFailure } from "../../sound/cli/io.ts";
14
+ export declare const music: Command.Command<"music", {}, {}, CliFailure | Exit, import("effect/FileSystem").FileSystem | Path.Path>;
15
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../src/music/cli/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAkB,IAAI,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAY,OAAO,EAAQ,MAAM,YAAY,CAAA;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAA;AAClD,OAAO,EAAE,UAAU,EAA8C,MAAM,uBAAuB,CAAA;AA4N9F,eAAO,MAAM,KAAK,yGAKjB,CAAA"}