index.mdpreviewindex.mdsource197 lines · 7.2 KB · raw
1This document introduces you to Strudel in a technical sense. 
2
3It is rather out of date, but there might still be useful info below.
4
5If you just want to *use* Strudel, have a look at the [Tutorial](https://strudel.tidalcycles.org/tutorial/).
6
7## Strudel Packages
8
9There are different packages for different purposes. They..
10
11- split up the code into smaller chunks
12- can be selectively used to implement some sort of time based system
13
14Please refer to the individual README files in the [packages folder](https://codeberg.org/uzu/strudel/src/branch/main/packages)
15
16## REPL
17
18The [REPL](https://strudel.tidalcycles.org/) is the place where all packages come together to form a live coding system. It can also be seen as a reference implementation for users of the library.
19
20More info in the [REPL README](https://codeberg.org/uzu/strudel/src/branch/main/packages/repl/README.md)
21
22# High Level Overview
23
24<img src="strudelflow.png" width="600" />
25
26## 1. End User Code
27
28The End User Code is written in JavaScript with added syntax sugar. The [eval package](https://github.com/tidalcycles/strudel/tree/main/packages/eval#strudelcycleseval) evaluates the user code
29after a transpilation step, which resolves the syntax sugar. If you don't want the syntax sugar, you can omit the eval package and call the native javascript `eval` instead.
30
31### 🍭 Syntax Sugar
32
33JavaScript Transpilation = converting valid JavaScript to valid JavaScript:
34
35```js
36"c3 [e3 g3]".fast(2)
37```
38
39becomes
40
41```js
42mini('c3 [e3 g3]')
43  .withMiniLocation([1, 0, 0], [1, 11, 11]) // source location
44  .fast(2);
45```
46
47- double quoted strings and backtick strings are turned into `mini` calls (single quoted strings are left as is)
48- The source location is added by chaining `withMiniLocation`, which enables the real time highlighting later
49- (psuedo) variable names that look like notes (like `c4`, `bb2` or `fs3`) are turned into strings
50- support for top level await
51- operator overloading could be implemented in the future
52
53This is how it works:
54
55<img src="https://github.com/tidalcycles/strudel/blob/talk/talk/public/shiftflow.png?raw=true" width="800" />
56
57- The user code is parsed with a [shift parser](https://github.com/shapesecurity/shift-parser-js), generating an AST
58- The AST is transformed to resolve the syntax sugar
59- The AST is used to generate code again (shift-codegen)
60
61Shift will most likely be replaced with acorn in the future, see https://github.com/tidalcycles/strudel/issues/174
62
63### Mini Notation
64
65Another important part of the user code is the mini notation, which allows to express rhythms in a short manner.
66
67- the mini notation is [implemented as a PEG grammar](https://github.com/tidalcycles/strudel/blob/main/packages/mini/krill.pegjs), living in the [mini package](https://github.com/tidalcycles/strudel/tree/main/packages/mini)
68- it is based on [krill](https://github.com/Mdashdotdashn/krill) by Mdashdotdashn
69- the peg grammar is used to generate a parser with [peggyjs](https://peggyjs.org/)
70- the generated parser takes a mini notation string and outputs an AST
71- the AST can then be used to construct a pattern using the regular Strudel API
72
73Here's an example AST:
74
75```json
76{
77  "type_": "pattern",
78  "arguments_": { "alignment": "h" },
79  "source_": [
80    {
81      "type_": "element", "source_": "c3",
82      "location_": { "start": { "offset": 1, "line": 1, "column": 2 }, "end": { "offset": 4, "line": 1, "column": 5 } }
83    },
84    {
85      "type_": "element",
86      "location_": { "start": { "offset": 4, "line": 1, "column": 5 }, "end": { "offset": 11, "line": 1, "column": 12 } }
87      "source_": {
88        "type_": "pattern", "arguments_": { "alignment": "h" },
89        "source_": [
90          {
91            "type_": "element", "source_": "e3",
92            "location_": { "start": { "offset": 5, "line": 1, "column": 6 }, "end": { "offset": 8, "line": 1, "column": 9 } }
93          },
94          {
95            "type_": "element", "source_": "g3",
96            "location_": { "start": { "offset": 8, "line": 1, "column": 9 }, "end": { "offset": 10, "line": 1, "column": 11 } }
97          }
98        ]
99      },
100    }
101  ]
102}
103```
104
105which translates to `seq(c3, seq(e3, g3))`
106
107## 2. Querying & Scheduling
108
109When the user code has been evaluated, we hopefully get a Pattern instance, which we can use to query events from. 
110These events can then be used to trigger side effects in the real world. On that note, Events are mostly called Hap(s) in the codebase, because JS already has a built in `Event` class.
111
112### Querying
113
114> Querying = Asking a Pattern for Events within a certain time span
115
116```js
117seq('c3', ['e3', 'g3']) // <--- Pattern
118  .queryArc(0, 2) // query events within 0 and 2 cycles
119  .map((hap) => hap.showWhole()); // make readable
120```
121
122yields 
123
124```js
125[
126  '0/1 -> 1/2: c3', // cycle 0
127  '1/2 -> 3/4: e3',
128  '3/4 -> 1/1: g3',
129  '1/1 -> 3/2: c3', // cycle 1
130  '3/2 -> 7/4: e3',
131  '7/4 -> 2/1: g3',
132];
133```
134
135### 🗓️ Scheduling
136
137The scheduler will query events repeatedly, creating a possibly endless loop of time slices.
138Here is a simplified example of how it works 
139
140```js
141let step = 0.5; // query interval in seconds
142let tick = 0; // how many intervals have passed
143let pattern = seq('c3', ['e3', 'g3']); // pattern from user
144setInterval(() => {
145  const events = pattern.queryArc(tick * step, ++tick * step);
146  events.forEach((event) => {
147    console.log(event.showWhole());
148    const o = getAudioContext().createOscillator();
149    o.frequency.value = getFreq(event.value);
150    o.start(event.whole.begin);
151    o.stop(event.whole.begin + event.duration);
152    o.connect(getAudioContext().destination);
153  });
154}, step * 1000); // query each "step" seconds
155```
156
157## 3. Sound Output
158
159The third and last step is to use the scheduled events to make sound. 
160Patterns are wrapped with param functions to compose different properties of the sound.
161
162```js
163note("[c2(3,8) [<eb2 g1> bb1]]") // sets frequency
164  .s("<sawtooth square>") // sound source
165  .gain(.5) // turn down volume
166  .cutoff(sine.range(200,1000).slow(4)) // modulated cutoff
167  .slow(2)
168  .out().logValues()`,
169  ]}
170/>
171```
172
173Here is an example Hap value with different properties:
174
175```js
176{ note: 'a4', s: 'sawtooth', gain: 0.5, cutoff: 267 }
177```
178<img src="waa-nodes.png" width="600" />
179
180<div className="text-left">
181
182- Patterns represent just values in time!
183- Suitable for any time based output (music, visuals, movement, .. ?)
184
185### Supported Outputs
186
187At the time of writing this doc, the following outputs are supported:
188
189- Web Audio API `.out()` see [/webaudio](https://github.com/tidalcycles/strudel/tree/main/packages/webaudio)
190- MIDI `.midi()` see [/midi](https://github.com/tidalcycles/strudel/tree/main/packages/midi)
191- OSC `.osc()` see [/osc](https://github.com/tidalcycles/strudel/tree/main/packages/osc)
192- Serial `.serial()` see [/serial](https://github.com/tidalcycles/strudel/tree/main/packages/serial)
193- Tone.js `.tone()` (deprecated?) [/tone](https://github.com/tidalcycles/strudel/tree/main/packages/tone)
194- WebDirt `.webdirt()` (deprecated?) [/webdirt](https://github.com/tidalcycles/strudel/tree/main/packages/webdirt)
195- Speech `.speak()` (experimental) part of [/core](https://github.com/tidalcycles/strudel/tree/main/packages/core)
196
197These could change, so make sure to check the [packages folder](https://github.com/tidalcycles/strudel/tree/main/packages).