tutticode 0.2.0 → 0.2.1

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.
@@ -40,7 +40,9 @@ _lead: n("0 2 4 7").scale("C:minor") // a leading _ mutes a voice
40
40
  - TuttiDAW only: `.inst("Device:Preset", {key: value})` instrument,
41
41
  `.midifx(...)` before it, `.fx(...)` after it, in call order. Params use the
42
42
  device's keys and units (`"4s"`, `"-6dB"`, `"1/16"`); constants only for now.
43
- `toggle(1)` is an inline switch. These disable the strudel.cc link.
43
+ Mixing: `bus("verb").fx("Reverb")` + `.send("verb", -12)`, `.mix({vol, pan,
44
+ mute})`, `master.fx("Limiter")`, `group("drums")` + `.group("drums")`.
45
+ `toggle(1)` is an inline switch; `note("do re mi")` = `note("c d e")`.
44
46
 
45
47
  ## Top mistakes
46
48
 
@@ -68,8 +70,7 @@ _lead: n("0 2 4 7").scale("C:minor") // a leading _ mutes a voice
68
70
 
69
71
  ## Tools: two backends, one API
70
72
 
71
- The same tools, with the same names and report shapes, run on two backends.
72
- Use whichever this agent has; prefer the local one.
73
+ The same tools and report shapes run on two backends; prefer the local one.
73
74
 
74
75
  - **Local `tutticode`** (default; free): the `tutticode` CLI
75
76
  and MCP server (`npm i -g tutticode`, `tutticode mcp`) render in the
@@ -94,13 +95,11 @@ Use whichever this agent has; prefer the local one.
94
95
  | `code_reference` | (local) search these references |
95
96
  | `code_account` | (local) signed in or not, the length limits, today's usage |
96
97
 
97
- **Without sign-in**, once TuttiDAW switches its limits on (they are being
98
- rolled out; until then no local render is cut or tagged), local renders
99
- stop at 60 s (error `limit`) and over 30 s carry a spoken "register at
100
- TuttiDAW" tag (`watermark`; it is in every stat, not in your program:
101
- never edit code to remove it). When a render hits one, tell the user once;
102
- `tutticode login` (free) lifts both. Agents: run `tutticode login
103
- --device --no-wait --json`, give the user its link (`references/agent-loop.md`).
98
+ **Without sign-in**, once the limits are on (until then nothing is cut or
99
+ tagged), local renders stop at 60 s (error `limit`) and over 30 s carry a
100
+ spoken "register at TuttiDAW" tag (`watermark`; never edit code to remove it).
101
+ Tell the user once; `tutticode login` (free) lifts both. Agents: run
102
+ `tutticode login --device --no-wait --json` and give the user its link.
104
103
 
105
104
  ## Workflow: write -> check -> render -> revise
106
105
 
@@ -116,9 +115,7 @@ never edit code to remove it). When a render hits one, tell the user once;
116
115
  4. **Hand over** the `editUrl` (local) or `listenUrl` (web) with the
117
116
  files: it opens the program in TuttiCode, where **Open in TuttiDAW**
118
117
  saves it as a project with one track per voice. Without tools, link
119
- `https://tuttidaw.com/code#<payload>`, the payload being the UTF-8 code
120
- in base64, URI-encoded (`encodeURIComponent(btoa(utf8))`, as strudel.cc's
121
- long links).
118
+ `https://tuttidaw.com/code#` + `encodeURIComponent(btoa(utf8 code))`.
122
119
 
123
120
  Pattern Clips in an open TuttiDAW project are edited with the web MCP's
124
121
  `read_pattern` / `write_pattern` (notes only; devices apply on /code and in
@@ -141,11 +138,12 @@ Read only what the task needs:
141
138
  - `references/sample-operations.md`: Sample operations. Play part of a sample, change its speed, cut it into slices, fit it to the tempo and choke it.
142
139
  - `references/tuttidaw-extensions.md`: TuttiDAW extensions. Load any TuttiDAW instrument, MIDI effect or audio effect with presets and parameters; voices, groups and Open in TuttiDAW.
143
140
  - `references/mixing.md`: Mixing: buses and master. Shared reverbs and delays on buses, sends from the voices, each voice's fader and pan, and the program's master chain - the same tracks Open in TuttiDAW makes.
141
+ - `references/mixing-groups.md`: Mixing: groups. Put voices in a group with its own effects, fader and sends - a drum bus inside the program, the same group track Open in TuttiDAW makes.
144
142
  - `references/javascript.md`: JavaScript in code. Variables, functions, loops and register - and the sandbox, determinism and limits that keep shared code safe.
145
143
  - `references/muse.md`: Muse on /TuttiCode. Ask TuttiDAW's AI assistant to write, explain, fix or restyle your program, from text, a Strudel tune, a screenshot or a reference track.
146
144
  - `references/for-agents.md`: TuttiCode for agents. Let Claude Code, Copilot, Codex or any MCP client write TuttiCode and render it to audio files in your own browser, with the tutticode CLI, MCP server and skills.
147
145
  - `references/functions.md`: every function, method and signal, with signature, description and example (generated from the engine).
148
146
  - `references/devices.md`: every instrument and effect for `.inst/.fx/.midifx`, with `references/devices/<id>.md` per device: param keys, ranges, units, presets (generated from the device catalogue).
149
147
 
150
- Language: PATTERN LANGUAGE 1.2.0, engine 16.
148
+ Language: PATTERN LANGUAGE 1.3.0, engine 17.
151
149
  Human guide: https://tuttidaw.com/code/learn; agents: https://tuttidaw.com/code/learn/for-agents
@@ -64,6 +64,16 @@ Sets chord symbols; also available as .chord(symbols) or .chord().
64
64
  chord("<Bbm9 Fm9>/4").dict("ireal").voicing()
65
65
  ```
66
66
 
67
+ ### `group(name)` *(TuttiDAW only)*
68
+
69
+ TuttiDAW extension, not in Strudel: declares a group, a group track inside the program; give it .fx(...), .mix(...) and .send(bus, dB), and put voices in it with .group(name).
70
+
71
+ ```js
72
+ group("drums").fx("Drum Buss").mix({vol: -3})
73
+ kick: s("bd*4").group("drums")
74
+ snare: s("~ sd").group("drums")
75
+ ```
76
+
67
77
  ### `hush()`
68
78
 
69
79
  Silence.
@@ -1049,7 +1059,7 @@ note("[c3,e3,g3]").midifx("Arpeggiator", {style: "UpDown", rate: "1/16"})
1049
1059
 
1050
1060
  ### `.mix(mixer)` *(TuttiDAW only)*
1051
1061
 
1052
- TuttiDAW extension, not in Strudel: the voice's mixer: vol in dB (-70..6), pan -1..1, mute true / false; a bus's and the master's too.
1062
+ TuttiDAW extension, not in Strudel: the voice's mixer: vol in dB (-70..6), pan -1..1, mute true / false; a bus's, a group's and the master's too.
1053
1063
 
1054
1064
  ```js
1055
1065
  note("c3 e3").mix({vol: -6, pan: -0.3})
@@ -0,0 +1,73 @@
1
+ # Mixing: groups
2
+
3
+ Put voices in a group with its own effects, fader and sends - a drum bus inside the program, the same group track Open in TuttiDAW makes.
4
+
5
+ A **group** gathers voices so you can treat them as one: a compressor on all
6
+ the drums, one fader for the whole kit, one send to the reverb. "Open in
7
+ TuttiDAW" makes it a group track inside the program's group, with those
8
+ voices in it.
9
+
10
+ | Code | What it is | In the studio |
11
+ |---|---|---|
12
+ | `group(name)` | a group of voices | a group track inside the program's group |
13
+ | `.group(name)` | puts a voice in a group | the voice's track becomes a child of that group |
14
+
15
+ Like `bus` and `.send`, these are TuttiDAW-only: a program that uses them
16
+ does not play on strudel.cc.
17
+
18
+ ## A drum group
19
+
20
+ Declare the group on its own line, give it effects with `.fx` and a fader
21
+ with `.mix`, and put voices in it with `.group(name)`:
22
+
23
+ ```js
24
+ group("drums").fx("Drum Buss").mix({vol: -3})
25
+
26
+ kick: s("bd*4").bank("tr909").group("drums")
27
+ snare: s("~ sd").bank("tr909").group("drums")
28
+ hats: s("hh*8").bank("tr909").group("drums").mix({vol: -8})
29
+ bass: note("<c2 g1 a1 f1>").ply(4).s("sawtooth").lpf(600)
30
+ ```
31
+
32
+ The kick, snare and hats play through the Drum Buss, then through the
33
+ group's fader: `-3` lowers all three by 3 dB. Each voice keeps its own
34
+ `.mix` too - the hats are 8 dB down inside the group.
35
+
36
+ ## Sends from a group
37
+
38
+ A group sends to a bus like a voice does, with a level in dB. The whole
39
+ group goes to the bus at that level - here the drums share one room while
40
+ the bass stays dry:
41
+
42
+ ```js
43
+ bus("room").fx("Reverb", {dryWetBalance: 1, decayTime: "1.5s"})
44
+ group("drums").send("room", -14)
45
+
46
+ kick: s("bd*4").group("drums")
47
+ snare: s("~ sd").group("drums")
48
+ bass: note("c2 ~ c2 g1").s("sawtooth").lpf(500)
49
+ ```
50
+
51
+ Buses belong to the program: a group can send to any bus the program
52
+ declares, and its voices can still send on their own too.
53
+
54
+ A group's fader, pan and send levels are numbers (a group track has no clip
55
+ to move them). A group cannot be put inside another group.
56
+
57
+ ## A voice without a group
58
+
59
+ `.group("drums")` with no `group("drums")` in the program is a warning: the
60
+ editor offers to add the declaration, and until then the voice plays in the
61
+ program's group like any other.
62
+
63
+ ## In the studio
64
+
65
+ The group track sits inside the program's group, with its voices as its
66
+ children. Its effects, fader and sends belong to the code, like a bus's:
67
+ change one by hand and the group is detached from the code (Re-attach
68
+ brings it back). Move a voice out of its group by hand and that voice is
69
+ detached. Remove `.group("drums")` from a voice and its track moves back
70
+ to the program's group; remove `group("drums")` itself and the group track
71
+ stays, marked "not in code", while its voices move back.
72
+
73
+ Source: https://tuttidaw.com/code/learn/mixing-groups
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- /* tutticode v0.2.0 | https://tuttidaw.com */
2
+ /* tutticode v0.2.1 | https://tuttidaw.com */
3
3
  "use strict";
4
4
  var __create = Object.create;
5
5
  var __defProp = Object.defineProperty;
@@ -4902,7 +4902,7 @@ var import_node_path2 = require("node:path");
4902
4902
  var import_node_fs = require("node:fs");
4903
4903
  var import_node_os = require("node:os");
4904
4904
  var import_node_path = require("node:path");
4905
- var VERSION2 = true ? "0.2.0" : readPackageVersion();
4905
+ var VERSION2 = true ? "0.2.1" : readPackageVersion();
4906
4906
  function envBrowserArgs() {
4907
4907
  const args = process.env.TUTTICODE_BROWSER_ARGS?.split(" ").filter(Boolean);
4908
4908
  return args?.length ? args : void 0;
@@ -4999,7 +4999,7 @@ function saveAccount(origin, account) {
4999
4999
  var PrivateDirError = class extends Error {
5000
5000
  };
5001
5001
  var privateDirs = /* @__PURE__ */ new Map();
5002
- var fileId = (st) => `${st.dev}:${st.ino}`;
5002
+ var fileId = (st, platform = process.platform) => platform === "win32" ? `${st.ino}` : `${st.dev}:${st.ino}`;
5003
5003
  function refusal(dir, why) {
5004
5004
  return new PrivateDirError(
5005
5005
  `${dir} ${why}, so tutticode will not keep a sign-in there. Point TUTTICODE_HOME at a folder you own that only you can use (or unset it for ~/.tutticode).`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tutticode",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "private": false,
5
5
  "description": "Turn TuttiCode (tuttidaw.com/code pattern code) into audio files from any agent: a CLI and an MCP server that render in your own browser with the TuttiDAW engine.",
6
6
  "license": "MIT",