pycodedj 0.1.4__tar.gz → 0.2.1__tar.gz

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 (41) hide show
  1. {pycodedj-0.1.4 → pycodedj-0.2.1}/CHANGELOG.md +31 -0
  2. {pycodedj-0.1.4 → pycodedj-0.2.1}/PKG-INFO +40 -27
  3. {pycodedj-0.1.4 → pycodedj-0.2.1}/README.ja.md +39 -26
  4. {pycodedj-0.1.4 → pycodedj-0.2.1}/README.md +39 -26
  5. {pycodedj-0.1.4 → pycodedj-0.2.1}/docs/index.html +58 -46
  6. pycodedj-0.2.1/docs/manual.html +1584 -0
  7. pycodedj-0.2.1/docs/manual.ja.md +1072 -0
  8. pycodedj-0.2.1/docs/manual.md +1070 -0
  9. pycodedj-0.2.1/examples/club_set.py +150 -0
  10. {pycodedj-0.1.4 → pycodedj-0.2.1}/examples/demo.py +14 -9
  11. {pycodedj-0.1.4 → pycodedj-0.2.1}/examples/hello_sc.py +1 -1
  12. pycodedj-0.2.1/examples/sound_showcase.py +295 -0
  13. {pycodedj-0.1.4 → pycodedj-0.2.1}/pyproject.toml +1 -1
  14. pycodedj-0.2.1/sc/synths.scd +724 -0
  15. {pycodedj-0.1.4 → pycodedj-0.2.1}/src/pycodedj/__init__.py +4 -1
  16. {pycodedj-0.1.4 → pycodedj-0.2.1}/src/pycodedj/__main__.py +1 -0
  17. pycodedj-0.2.1/src/pycodedj/_loop.py +9 -0
  18. pycodedj-0.2.1/src/pycodedj/block_parser.py +90 -0
  19. {pycodedj-0.1.4 → pycodedj-0.2.1}/src/pycodedj/engine.py +8 -1
  20. pycodedj-0.2.1/src/pycodedj/mapper.py +95 -0
  21. {pycodedj-0.1.4 → pycodedj-0.2.1}/src/pycodedj/osc_bridge.py +23 -9
  22. {pycodedj-0.1.4 → pycodedj-0.2.1}/src/pycodedj/watcher.py +5 -0
  23. pycodedj-0.2.1/tests/test_block_parser.py +135 -0
  24. {pycodedj-0.1.4 → pycodedj-0.2.1}/tests/test_mapper.py +41 -0
  25. {pycodedj-0.1.4 → pycodedj-0.2.1}/tests/test_osc_bridge.py +14 -9
  26. {pycodedj-0.1.4 → pycodedj-0.2.1}/tests/test_watcher.py +28 -16
  27. pycodedj-0.1.4/docs/manual.ja.md +0 -815
  28. pycodedj-0.1.4/docs/manual.md +0 -813
  29. pycodedj-0.1.4/examples/club_set.py +0 -71
  30. pycodedj-0.1.4/sc/synths.scd +0 -154
  31. pycodedj-0.1.4/src/pycodedj/block_parser.py +0 -45
  32. pycodedj-0.1.4/src/pycodedj/mapper.py +0 -46
  33. pycodedj-0.1.4/tests/test_block_parser.py +0 -66
  34. {pycodedj-0.1.4 → pycodedj-0.2.1}/.github/workflows/workflow.yml +0 -0
  35. {pycodedj-0.1.4 → pycodedj-0.2.1}/.gitignore +0 -0
  36. {pycodedj-0.1.4 → pycodedj-0.2.1}/LICENSE +0 -0
  37. {pycodedj-0.1.4 → pycodedj-0.2.1}/docs/CNAME +0 -0
  38. {pycodedj-0.1.4 → pycodedj-0.2.1}/src/pycodedj/analyzer.py +0 -0
  39. {pycodedj-0.1.4 → pycodedj-0.2.1}/tests/__init__.py +0 -0
  40. {pycodedj-0.1.4 → pycodedj-0.2.1}/tests/test_analyzer.py +0 -0
  41. {pycodedj-0.1.4 → pycodedj-0.2.1}/tests/test_engine.py +0 -0
@@ -1,5 +1,36 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.1] - 2026-05-08
4
+
5
+ ### Docs
6
+
7
+ - `docs/manual.html` — Python シンタックスハイライターのプレースホルダー衝突バグを修正。`PY_NUMBER` がプレースホルダーのインデックス数字にマッチしてコメント・文字列が正しく表示されない問題を解消
8
+
9
+ ## [0.2.0] - 2026-05-08
10
+
11
+ ### Breaking
12
+
13
+ - `# @loop <name> interval=<sec>` コメント構文を廃止。`@loop("name", interval=sec)` デコレータ構文に移行
14
+ - 既存のライブコーディングファイルはデコレータ構文への書き換えが必要
15
+
16
+ ### Features
17
+
18
+ - `@loop("name", interval=sec)` デコレータ — ファイルをそのまま `python` で実行できる no-op デコレータとして `from pycodedj import loop` で提供
19
+ - `volume=` 引数 — ループ関数のデフォルト引数として音量を直接指定できる(例: `def my_loop(volume=0.4):`)
20
+ - Amplitude パラメーター(`amp`)を OSC で SuperCollider に送出。`/pycodedj/loop/<name>/params` は `voice_count, cutoff, lfo_rate, reverb, amp` の 5 値に拡張
21
+
22
+ ### Improvements
23
+
24
+ - `sc/synths.scd` — `amp` パラメーターをすべての SynthDef と OSC ハンドラーに追加
25
+ - `examples/club_set.py` — 6 層 14 ループ構成のクラブセットに刷新(foundation / movement / body / harmonic / space / texture)
26
+ - `examples/demo.py` — 新デコレータ構文に更新
27
+
28
+ ### Docs
29
+
30
+ - README.md / README.ja.md — 新デコレータ構文・`volume=` 引数・`amp` OSC パラメーターに全面更新
31
+ - `docs/manual.md` / `docs/manual.ja.md` — 同上
32
+ - `docs/index.html` — ライブデモアニメーションとコード例を新構文に更新
33
+
3
34
  ## [0.1.4] - 2026-05-07
4
35
 
5
36
  ### Fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycodedj
3
- Version: 0.1.4
3
+ Version: 0.2.1
4
4
  License: MIT License with Commons Clause
5
5
 
6
6
  "Commons Clause" License Condition v1.0
@@ -107,6 +107,8 @@ BPM clock is held by SuperCollider's `TempoClock`. Python only sends parameter u
107
107
  | Control-flow count (if/for/while) | LFO rate (0.1–5.0 Hz) | More branches = faster modulation |
108
108
  | Function definition count | Polyphony voice count (1–4) | Functions = independent voices |
109
109
  | Comment ratio | Reverb depth (0.0–0.8) | More whitespace = more space |
110
+ | `volume=` argument | Amplitude (0.0–1.0) | Direct performer control over loudness |
111
+ | `eq=` / `low=` / `mid=` / `high=` arguments | Simple 3-band EQ | Per-loop tone shaping |
110
112
 
111
113
  Tempo (BPM) and root pitch are controlled explicitly by the performer, to prevent the foundation of the piece from shifting on every save.
112
114
 
@@ -144,20 +146,22 @@ Open `sc/synths.scd` in the SuperCollider IDE and evaluate it.
144
146
  **2. Write a live-coding file**
145
147
 
146
148
  ```python
147
- # @loop bass interval=2.0
148
- def bass():
149
+ from pycodedj import loop
150
+
151
+ @loop("bass", interval=2.0)
152
+ def bass(volume=0.4):
149
153
  for i in range(8):
150
154
  if i % 2 == 0:
151
155
  pass
152
156
 
153
- # @loop melody interval=0.5
154
- def melody():
157
+ @loop("melody", interval=0.5)
158
+ def melody(volume=0.3):
155
159
  x = 1
156
160
  y = 2
157
161
  return x + y
158
162
 
159
- # @loop pad interval=4.0
160
- def pad():
163
+ @loop("pad", interval=4.0)
164
+ def pad(volume=0.15):
161
165
  # make space
162
166
  # a little more
163
167
  pass
@@ -172,7 +176,7 @@ pycodedj eval demo.py::bass
172
176
  On success, feedback is printed immediately:
173
177
 
174
178
  ```
175
- [pycodedj] bass cutoff=418Hz lfo=1.08Hz reverb=0.00 voices=1
179
+ [pycodedj] bass cutoff=418Hz lfo=1.08Hz reverb=0.00 voices=1 amp=0.40
176
180
  ```
177
181
 
178
182
  Other loops keep playing without interruption.
@@ -187,8 +191,6 @@ pycodedj watch demo.py
187
191
 
188
192
  From here, just write code and save.
189
193
 
190
- > **Note on `interval` (current MVP):** Values like `interval=2.0` are parsed and stored, but are not yet sent over OSC. Loop repeat timing is managed by SuperCollider's `TempoClock`. A mechanism to pass interval to SC is planned for a future release.
191
-
192
194
  ---
193
195
 
194
196
  ## Example Files
@@ -196,7 +198,8 @@ From here, just write code and save.
196
198
  | File | Contents |
197
199
  | :--- | :--- |
198
200
  | `examples/demo.py` | Intro demo with bass / melody / pad |
199
- | `examples/club_set.py` | Club-style demo: sub_bass / hat_engine / neon_stab / acid_lead / warehouse_air |
201
+ | `examples/club_set.py` | EDM groove (8 loops, kick acid bass rave stabs → hoover → shimmer) |
202
+ | `examples/sound_showcase.py` | All 30 synths — evaluate one at a time to audition each sound |
200
203
 
201
204
  ---
202
205
 
@@ -205,8 +208,10 @@ From here, just write code and save.
205
208
  ### Deeper nesting opens the filter
206
209
 
207
210
  ```python
208
- # @loop bass interval=2.0
209
- def bass():
211
+ from pycodedj import loop
212
+
213
+ @loop("bass", interval=2.0)
214
+ def bass(volume=0.4):
210
215
  for i in range(4): # control flow +1
211
216
  for j in range(4): # depth +1, control flow +1
212
217
  if i == j: # depth +1, control flow +1
@@ -216,21 +221,27 @@ def bass():
216
221
  ### More functions = more polyphony
217
222
 
218
223
  ```python
219
- # @loop chord interval=1.0
220
- def voice_a(): pass
221
- def voice_b(): pass
222
- def voice_c(): pass
223
- def voice_d(): pass
224
+ from pycodedj import loop
225
+
226
+ @loop("chord", interval=1.0)
227
+ def chord(volume=0.2):
228
+ def voice_a(): pass
229
+ def voice_b(): pass
230
+ def voice_c(): pass
231
+ def voice_d(): pass
224
232
  ```
225
233
 
226
234
  ### More comments = more space (reverb)
227
235
 
228
236
  ```python
229
- # @loop pad interval=4.0
230
- # leave space here
231
- # a little more
232
- # silence is music
233
- def pad(): pass
237
+ from pycodedj import loop
238
+
239
+ @loop("pad", interval=4.0)
240
+ def pad(volume=0.15):
241
+ # leave space here
242
+ # a little more
243
+ # silence is music
244
+ pass
234
245
  ```
235
246
 
236
247
  ---
@@ -241,10 +252,12 @@ Addresses used to communicate with SuperCollider.
241
252
 
242
253
  | Address | Type | Range | Parameter |
243
254
  | :--- | :--- | :--- | :--- |
244
- | `/pycodedj/loop/<name>/cutoff` | float | 200–4000 Hz | Filter Cutoff |
245
- | `/pycodedj/loop/<name>/lfo_rate` | float | 0.15.0 Hz | LFO rate |
246
- | `/pycodedj/loop/<name>/reverb` | float | 0.0–0.8 | Reverb depth |
247
- | `/pycodedj/loop/<name>/voice_count` | int | 14 | Polyphony voice count |
255
+ | `/pycodedj/loop/<name>/params` | int, float, float, float, float | see parameter order | `voice_count`, `cutoff`, `lfo_rate`, `reverb`, `amp` |
256
+ | `/pycodedj/loop/<name>/cutoff` | float | 2004000 Hz | Filter Cutoff (compatibility) |
257
+ | `/pycodedj/loop/<name>/lfo_rate` | float | 0.15.0 Hz | LFO rate (compatibility) |
258
+ | `/pycodedj/loop/<name>/reverb` | float | 0.00.8 | Reverb depth (compatibility) |
259
+ | `/pycodedj/loop/<name>/voice_count` | int | 1–4 | Polyphony voice count (compatibility) |
260
+ | `/pycodedj/loop/<name>/amp` | float | 0.0–1.0 | Amplitude (compatibility) |
248
261
 
249
262
  `<name>` is the loop name (e.g. `bass`, `melody`). Each loop has its own address namespace, so multiple loops never overwrite each other's parameters.
250
263
 
@@ -45,6 +45,8 @@ BPMクロックは SuperCollider 側の `TempoClock` が保持します。Python
45
45
  | 制御フロー数(if/for/while) | LFO レート (0.1–5.0 Hz) | 分岐の多さ=揺らぎの速さ |
46
46
  | 関数定義数 | ポリフォニー声部数 (1–4) | 関数=独立した声部 |
47
47
  | コメント率 | リバーブ Depth (0.0–0.8) | 余白の多さ=空間の広さ |
48
+ | `volume=` 引数 | Amplitude (0.0–1.0) | 演奏者が直接音量を制御する |
49
+ | `eq=` / `low=` / `mid=` / `high=` 引数 | 簡易 3 バンド EQ | ループごとの音質補正 |
48
50
 
49
51
  テンポ(BPM)と基音(Pitch)は演奏者が明示的に制御します。保存のたびに楽曲全体の土台が変わることを防ぐためです。
50
52
 
@@ -82,20 +84,22 @@ SuperCollider IDE で `sc/synths.scd` を開いて実行します。
82
84
  **2. ライブコーディングファイルを用意する**
83
85
 
84
86
  ```python
85
- # @loop bass interval=2.0
86
- def bass():
87
+ from pycodedj import loop
88
+
89
+ @loop("bass", interval=2.0)
90
+ def bass(volume=0.4):
87
91
  for i in range(8):
88
92
  if i % 2 == 0:
89
93
  pass
90
94
 
91
- # @loop melody interval=0.5
92
- def melody():
95
+ @loop("melody", interval=0.5)
96
+ def melody(volume=0.3):
93
97
  x = 1
94
98
  y = 2
95
99
  return x + y
96
100
 
97
- # @loop pad interval=4.0
98
- def pad():
101
+ @loop("pad", interval=4.0)
102
+ def pad(volume=0.15):
99
103
  # 空間を作る
100
104
  # もう少し余白
101
105
  pass
@@ -110,7 +114,7 @@ pycodedj eval demo.py::bass
110
114
  成功すると次のフィードバックが表示されます。
111
115
 
112
116
  ```
113
- [pycodedj] bass cutoff=418Hz lfo=1.08Hz reverb=0.00 voices=1
117
+ [pycodedj] bass cutoff=418Hz lfo=1.08Hz reverb=0.00 voices=1 amp=0.40
114
118
  ```
115
119
 
116
120
  他のループはそのまま鳴り続けます。
@@ -125,8 +129,6 @@ pycodedj watch demo.py
125
129
 
126
130
  あとはエディタでコードを書いて保存するだけです。
127
131
 
128
- > **`interval` について(現在の MVP):** `interval=2.0` のような値はパーサーが読み取りますが、現時点では OSC では送信されません。ループの繰り返し周期は SuperCollider 側の `TempoClock` で管理します。将来的に SC 側に interval を渡す仕組みを追加する予定です。
129
-
130
132
  ---
131
133
 
132
134
  ## サンプルファイル
@@ -134,7 +136,8 @@ pycodedj watch demo.py
134
136
  | ファイル | 内容 |
135
137
  | :--- | :--- |
136
138
  | `examples/demo.py` | bass / melody / pad の 3 ループ入門デモ |
137
- | `examples/club_set.py` | sub_bass / hat_engine / neon_stab / acid_lead / warehouse_air のクラブスタイルデモ |
139
+ | `examples/club_set.py` | EDM クラブグルーヴ(8 ループ、キック アシッドベース レイブスタブ Hoover シマー) |
140
+ | `examples/sound_showcase.py` | 全 30 音色を収録 — 1 音ずつ eval して確認できる |
138
141
 
139
142
  ---
140
143
 
@@ -143,8 +146,10 @@ pycodedj watch demo.py
143
146
  ### ネストを深くするとフィルターが開く
144
147
 
145
148
  ```python
146
- # @loop bass interval=2.0
147
- def bass():
149
+ from pycodedj import loop
150
+
151
+ @loop("bass", interval=2.0)
152
+ def bass(volume=0.4):
148
153
  for i in range(4): # 制御フロー +1
149
154
  for j in range(4): # ネスト深さ +1、制御フロー +1
150
155
  if i == j: # ネスト深さ +1、制御フロー +1
@@ -154,21 +159,27 @@ def bass():
154
159
  ### 関数を増やすとポリフォニーが広がる
155
160
 
156
161
  ```python
157
- # @loop chord interval=1.0
158
- def voice_a(): pass
159
- def voice_b(): pass
160
- def voice_c(): pass
161
- def voice_d(): pass
162
+ from pycodedj import loop
163
+
164
+ @loop("chord", interval=1.0)
165
+ def chord(volume=0.2):
166
+ def voice_a(): pass
167
+ def voice_b(): pass
168
+ def voice_c(): pass
169
+ def voice_d(): pass
162
170
  ```
163
171
 
164
172
  ### コメントを増やすと空間(リバーブ)が広がる
165
173
 
166
174
  ```python
167
- # @loop pad interval=4.0
168
- # ここに余白を置く
169
- # もう少し置く
170
- # 静寂も音楽
171
- def pad(): pass
175
+ from pycodedj import loop
176
+
177
+ @loop("pad", interval=4.0)
178
+ def pad(volume=0.15):
179
+ # ここに余白を置く
180
+ # もう少し置く
181
+ # 静寂も音楽
182
+ pass
172
183
  ```
173
184
 
174
185
  ---
@@ -179,10 +190,12 @@ SuperCollider との通信に使うアドレスです。
179
190
 
180
191
  | アドレス | 型 | 値域 | 対応パラメーター |
181
192
  | :--- | :--- | :--- | :--- |
182
- | `/pycodedj/loop/<name>/cutoff` | float | 200–4000 Hz | フィルター Cutoff |
183
- | `/pycodedj/loop/<name>/lfo_rate` | float | 0.15.0 Hz | LFO レート |
184
- | `/pycodedj/loop/<name>/reverb` | float | 0.0–0.8 | リバーブ Depth |
185
- | `/pycodedj/loop/<name>/voice_count` | int | 14 | ポリフォニー声部数 |
193
+ | `/pycodedj/loop/<name>/params` | int, float, float, float, float | パラメーター順を参照 | `voice_count`, `cutoff`, `lfo_rate`, `reverb`, `amp` |
194
+ | `/pycodedj/loop/<name>/cutoff` | float | 2004000 Hz | フィルター Cutoff(互換用) |
195
+ | `/pycodedj/loop/<name>/lfo_rate` | float | 0.15.0 Hz | LFO レート(互換用) |
196
+ | `/pycodedj/loop/<name>/reverb` | float | 0.00.8 | リバーブ Depth(互換用) |
197
+ | `/pycodedj/loop/<name>/voice_count` | int | 1–4 | ポリフォニー声部数(互換用) |
198
+ | `/pycodedj/loop/<name>/amp` | float | 0.0–1.0 | Amplitude(互換用) |
186
199
 
187
200
  `<name>` はブロック名(`bass`、`melody` など)です。ループごとに独立したアドレスを持つため、複数ループが同じパラメーターを上書きしません。
188
201
 
@@ -45,6 +45,8 @@ BPM clock is held by SuperCollider's `TempoClock`. Python only sends parameter u
45
45
  | Control-flow count (if/for/while) | LFO rate (0.1–5.0 Hz) | More branches = faster modulation |
46
46
  | Function definition count | Polyphony voice count (1–4) | Functions = independent voices |
47
47
  | Comment ratio | Reverb depth (0.0–0.8) | More whitespace = more space |
48
+ | `volume=` argument | Amplitude (0.0–1.0) | Direct performer control over loudness |
49
+ | `eq=` / `low=` / `mid=` / `high=` arguments | Simple 3-band EQ | Per-loop tone shaping |
48
50
 
49
51
  Tempo (BPM) and root pitch are controlled explicitly by the performer, to prevent the foundation of the piece from shifting on every save.
50
52
 
@@ -82,20 +84,22 @@ Open `sc/synths.scd` in the SuperCollider IDE and evaluate it.
82
84
  **2. Write a live-coding file**
83
85
 
84
86
  ```python
85
- # @loop bass interval=2.0
86
- def bass():
87
+ from pycodedj import loop
88
+
89
+ @loop("bass", interval=2.0)
90
+ def bass(volume=0.4):
87
91
  for i in range(8):
88
92
  if i % 2 == 0:
89
93
  pass
90
94
 
91
- # @loop melody interval=0.5
92
- def melody():
95
+ @loop("melody", interval=0.5)
96
+ def melody(volume=0.3):
93
97
  x = 1
94
98
  y = 2
95
99
  return x + y
96
100
 
97
- # @loop pad interval=4.0
98
- def pad():
101
+ @loop("pad", interval=4.0)
102
+ def pad(volume=0.15):
99
103
  # make space
100
104
  # a little more
101
105
  pass
@@ -110,7 +114,7 @@ pycodedj eval demo.py::bass
110
114
  On success, feedback is printed immediately:
111
115
 
112
116
  ```
113
- [pycodedj] bass cutoff=418Hz lfo=1.08Hz reverb=0.00 voices=1
117
+ [pycodedj] bass cutoff=418Hz lfo=1.08Hz reverb=0.00 voices=1 amp=0.40
114
118
  ```
115
119
 
116
120
  Other loops keep playing without interruption.
@@ -125,8 +129,6 @@ pycodedj watch demo.py
125
129
 
126
130
  From here, just write code and save.
127
131
 
128
- > **Note on `interval` (current MVP):** Values like `interval=2.0` are parsed and stored, but are not yet sent over OSC. Loop repeat timing is managed by SuperCollider's `TempoClock`. A mechanism to pass interval to SC is planned for a future release.
129
-
130
132
  ---
131
133
 
132
134
  ## Example Files
@@ -134,7 +136,8 @@ From here, just write code and save.
134
136
  | File | Contents |
135
137
  | :--- | :--- |
136
138
  | `examples/demo.py` | Intro demo with bass / melody / pad |
137
- | `examples/club_set.py` | Club-style demo: sub_bass / hat_engine / neon_stab / acid_lead / warehouse_air |
139
+ | `examples/club_set.py` | EDM groove (8 loops, kick acid bass rave stabs → hoover → shimmer) |
140
+ | `examples/sound_showcase.py` | All 30 synths — evaluate one at a time to audition each sound |
138
141
 
139
142
  ---
140
143
 
@@ -143,8 +146,10 @@ From here, just write code and save.
143
146
  ### Deeper nesting opens the filter
144
147
 
145
148
  ```python
146
- # @loop bass interval=2.0
147
- def bass():
149
+ from pycodedj import loop
150
+
151
+ @loop("bass", interval=2.0)
152
+ def bass(volume=0.4):
148
153
  for i in range(4): # control flow +1
149
154
  for j in range(4): # depth +1, control flow +1
150
155
  if i == j: # depth +1, control flow +1
@@ -154,21 +159,27 @@ def bass():
154
159
  ### More functions = more polyphony
155
160
 
156
161
  ```python
157
- # @loop chord interval=1.0
158
- def voice_a(): pass
159
- def voice_b(): pass
160
- def voice_c(): pass
161
- def voice_d(): pass
162
+ from pycodedj import loop
163
+
164
+ @loop("chord", interval=1.0)
165
+ def chord(volume=0.2):
166
+ def voice_a(): pass
167
+ def voice_b(): pass
168
+ def voice_c(): pass
169
+ def voice_d(): pass
162
170
  ```
163
171
 
164
172
  ### More comments = more space (reverb)
165
173
 
166
174
  ```python
167
- # @loop pad interval=4.0
168
- # leave space here
169
- # a little more
170
- # silence is music
171
- def pad(): pass
175
+ from pycodedj import loop
176
+
177
+ @loop("pad", interval=4.0)
178
+ def pad(volume=0.15):
179
+ # leave space here
180
+ # a little more
181
+ # silence is music
182
+ pass
172
183
  ```
173
184
 
174
185
  ---
@@ -179,10 +190,12 @@ Addresses used to communicate with SuperCollider.
179
190
 
180
191
  | Address | Type | Range | Parameter |
181
192
  | :--- | :--- | :--- | :--- |
182
- | `/pycodedj/loop/<name>/cutoff` | float | 200–4000 Hz | Filter Cutoff |
183
- | `/pycodedj/loop/<name>/lfo_rate` | float | 0.15.0 Hz | LFO rate |
184
- | `/pycodedj/loop/<name>/reverb` | float | 0.0–0.8 | Reverb depth |
185
- | `/pycodedj/loop/<name>/voice_count` | int | 14 | Polyphony voice count |
193
+ | `/pycodedj/loop/<name>/params` | int, float, float, float, float | see parameter order | `voice_count`, `cutoff`, `lfo_rate`, `reverb`, `amp` |
194
+ | `/pycodedj/loop/<name>/cutoff` | float | 2004000 Hz | Filter Cutoff (compatibility) |
195
+ | `/pycodedj/loop/<name>/lfo_rate` | float | 0.15.0 Hz | LFO rate (compatibility) |
196
+ | `/pycodedj/loop/<name>/reverb` | float | 0.00.8 | Reverb depth (compatibility) |
197
+ | `/pycodedj/loop/<name>/voice_count` | int | 1–4 | Polyphony voice count (compatibility) |
198
+ | `/pycodedj/loop/<name>/amp` | float | 0.0–1.0 | Amplitude (compatibility) |
186
199
 
187
200
  `<name>` is the loop name (e.g. `bass`, `melody`). Each loop has its own address namespace, so multiple loops never overwrite each other's parameters.
188
201
 
@@ -198,12 +198,14 @@
198
198
  min-height: 200px;
199
199
  white-space: pre-wrap;
200
200
  }
201
- .c-comment { color: #585b70; font-style: italic; }
202
- .c-keyword { color: var(--mauve); }
203
- .c-fn { color: var(--blue); }
204
- .c-string { color: var(--green); }
205
- .c-num { color: var(--yellow); }
206
- .c-op { color: var(--muted); }
201
+ .c-comment { color: #585b70; font-style: italic; }
202
+ .c-keyword { color: var(--mauve); }
203
+ .c-fn { color: var(--blue); }
204
+ .c-string { color: var(--green); }
205
+ .c-str { color: var(--green); }
206
+ .c-num { color: var(--yellow); }
207
+ .c-op { color: var(--muted); }
208
+ .c-decorator { color: var(--pink); }
207
209
 
208
210
  .demo-params {
209
211
  padding: 1.25rem;
@@ -719,8 +721,10 @@
719
721
  <span class="example-tag"># bright + fast</span>
720
722
  <span class="example-effect">cutoff ↑ lfo ↑</span>
721
723
  </div>
722
- <pre><span class="c-comment"># @loop bass interval=2.0</span>
723
- <span class="c-keyword">def</span> <span class="c-fn">bass</span>():
724
+ <pre><span class="c-keyword">from</span> pycodedj <span class="c-keyword">import</span> loop
725
+
726
+ <span class="c-decorator">@loop</span>(<span class="c-str">"bass"</span>, interval=<span class="c-num">2.0</span>)
727
+ <span class="c-keyword">def</span> <span class="c-fn">bass</span>(volume=<span class="c-num">0.4</span>):
724
728
  <span class="c-keyword">for</span> i <span class="c-keyword">in</span> <span class="c-fn">range</span>(<span class="c-num">8</span>):
725
729
  <span class="c-keyword">for</span> j <span class="c-keyword">in</span> <span class="c-fn">range</span>(<span class="c-num">4</span>):
726
730
  <span class="c-keyword">if</span> i <span class="c-op">==</span> j:
@@ -731,23 +735,29 @@
731
735
  <span class="example-tag"># 4-voice chord</span>
732
736
  <span class="example-effect">voices = 4</span>
733
737
  </div>
734
- <pre><span class="c-comment"># @loop chord interval=1.0</span>
735
- <span class="c-keyword">def</span> <span class="c-fn">voice_a</span>(): <span class="c-keyword">pass</span>
736
- <span class="c-keyword">def</span> <span class="c-fn">voice_b</span>(): <span class="c-keyword">pass</span>
737
- <span class="c-keyword">def</span> <span class="c-fn">voice_c</span>(): <span class="c-keyword">pass</span>
738
- <span class="c-keyword">def</span> <span class="c-fn">voice_d</span>(): <span class="c-keyword">pass</span></pre>
738
+ <pre><span class="c-keyword">from</span> pycodedj <span class="c-keyword">import</span> loop
739
+
740
+ <span class="c-decorator">@loop</span>(<span class="c-str">"chord"</span>, interval=<span class="c-num">1.0</span>)
741
+ <span class="c-keyword">def</span> <span class="c-fn">chord</span>(volume=<span class="c-num">0.2</span>):
742
+ <span class="c-keyword">def</span> <span class="c-fn">voice_a</span>(): <span class="c-keyword">pass</span>
743
+ <span class="c-keyword">def</span> <span class="c-fn">voice_b</span>(): <span class="c-keyword">pass</span>
744
+ <span class="c-keyword">def</span> <span class="c-fn">voice_c</span>(): <span class="c-keyword">pass</span>
745
+ <span class="c-keyword">def</span> <span class="c-fn">voice_d</span>(): <span class="c-keyword">pass</span></pre>
739
746
  </div>
740
747
  <div class="example-card">
741
748
  <div class="example-card-header">
742
749
  <span class="example-tag"># deep space</span>
743
750
  <span class="example-effect">reverb ↑↑</span>
744
751
  </div>
745
- <pre><span class="c-comment"># @loop pad interval=4.0</span>
746
- <span class="c-comment"># smoke above the kick</span>
747
- <span class="c-comment"># late reflections</span>
748
- <span class="c-comment"># concrete room tail</span>
749
- <span class="c-comment"># crowd heat</span>
750
- <span class="c-keyword">def</span> <span class="c-fn">pad</span>(): <span class="c-keyword">pass</span></pre>
752
+ <pre><span class="c-keyword">from</span> pycodedj <span class="c-keyword">import</span> loop
753
+
754
+ <span class="c-decorator">@loop</span>(<span class="c-str">"pad"</span>, interval=<span class="c-num">4.0</span>)
755
+ <span class="c-keyword">def</span> <span class="c-fn">pad</span>(volume=<span class="c-num">0.15</span>):
756
+ <span class="c-comment"># smoke above the kick</span>
757
+ <span class="c-comment"># late reflections</span>
758
+ <span class="c-comment"># concrete room tail</span>
759
+ <span class="c-comment"># crowd heat</span>
760
+ <span class="c-keyword">pass</span></pre>
751
761
  </div>
752
762
  </div>
753
763
  </div>
@@ -777,72 +787,74 @@
777
787
  const frames = [
778
788
  {
779
789
  code: [
780
- '<span class="c-comment"># @loop bass interval=2.0</span>',
781
- '<span class="c-keyword">def</span> <span class="c-fn">bass</span>():',
790
+ '<span class="c-decorator">@loop</span>(<span class="c-str">"bass"</span>, interval=<span class="c-num">2.0</span>)',
791
+ '<span class="c-keyword">def</span> <span class="c-fn">bass</span>(volume=<span class="c-num">0.4</span>):',
782
792
  ' <span class="c-keyword">pass</span>',
783
793
  ],
784
- // depth=1 cf=0 fns=1 comment_ratio=0.33
794
+ // depth=1 cf=0 fns=1 comment_ratio=0.00
785
795
  cutoff: { pct: 10, val: '580 Hz' },
786
796
  lfo: { pct: 0, val: '0.10 Hz' },
787
- reverb: { pct: 34, val: '0.27' },
797
+ reverb: { pct: 0, val: '0.00' },
788
798
  voices: { pct: 25, val: '1 voice' },
789
799
  },
790
800
  {
791
801
  code: [
792
- '<span class="c-comment"># @loop bass interval=2.0</span>',
793
- '<span class="c-keyword">def</span> <span class="c-fn">bass</span>():',
802
+ '<span class="c-decorator">@loop</span>(<span class="c-str">"bass"</span>, interval=<span class="c-num">2.0</span>)',
803
+ '<span class="c-keyword">def</span> <span class="c-fn">bass</span>(volume=<span class="c-num">0.4</span>):',
794
804
  ' <span class="c-keyword">for</span> i <span class="c-keyword">in</span> <span class="c-fn">range</span>(<span class="c-num">8</span>):',
795
805
  ' <span class="c-keyword">if</span> i <span class="c-op">%</span> <span class="c-num">2</span> <span class="c-op">==</span> <span class="c-num">0</span>:',
796
806
  ' <span class="c-keyword">pass</span>',
797
807
  ],
798
- // depth=3 cf=2 fns=1 comment_ratio=0.20
808
+ // depth=3 cf=2 fns=1 comment_ratio=0.00
799
809
  cutoff: { pct: 30, val: '1340 Hz' },
800
810
  lfo: { pct: 20, val: '1.08 Hz' },
801
- reverb: { pct: 20, val: '0.16' },
811
+ reverb: { pct: 0, val: '0.00' },
802
812
  voices: { pct: 25, val: '1 voice' },
803
813
  },
804
814
  {
805
815
  code: [
806
- '<span class="c-comment"># @loop bass interval=2.0</span>',
807
- '<span class="c-keyword">def</span> <span class="c-fn">bass</span>():',
816
+ '<span class="c-decorator">@loop</span>(<span class="c-str">"bass"</span>, interval=<span class="c-num">2.0</span>)',
817
+ '<span class="c-keyword">def</span> <span class="c-fn">bass</span>(volume=<span class="c-num">0.4</span>):',
808
818
  ' <span class="c-keyword">for</span> i <span class="c-keyword">in</span> <span class="c-fn">range</span>(<span class="c-num">8</span>):',
809
819
  ' <span class="c-keyword">for</span> j <span class="c-keyword">in</span> <span class="c-fn">range</span>(<span class="c-num">4</span>):',
810
820
  ' <span class="c-keyword">if</span> i <span class="c-op">==</span> j:',
811
821
  ' <span class="c-keyword">pass</span>',
812
822
  ],
813
- // depth=4 cf=3 fns=1 comment_ratio=0.17
823
+ // depth=4 cf=3 fns=1 comment_ratio=0.00
814
824
  cutoff: { pct: 40, val: '1720 Hz' },
815
825
  lfo: { pct: 30, val: '1.57 Hz' },
816
- reverb: { pct: 16, val: '0.13' },
826
+ reverb: { pct: 0, val: '0.00' },
817
827
  voices: { pct: 25, val: '1 voice' },
818
828
  },
819
829
  {
820
830
  code: [
821
- '<span class="c-comment"># @loop chord interval=1.0</span>',
822
- '<span class="c-keyword">def</span> <span class="c-fn">voice_a</span>(): <span class="c-keyword">pass</span>',
823
- '<span class="c-keyword">def</span> <span class="c-fn">voice_b</span>(): <span class="c-keyword">pass</span>',
824
- '<span class="c-keyword">def</span> <span class="c-fn">voice_c</span>(): <span class="c-keyword">pass</span>',
825
- '<span class="c-keyword">def</span> <span class="c-fn">voice_d</span>(): <span class="c-keyword">pass</span>',
831
+ '<span class="c-decorator">@loop</span>(<span class="c-str">"chord"</span>, interval=<span class="c-num">1.0</span>)',
832
+ '<span class="c-keyword">def</span> <span class="c-fn">chord</span>(volume=<span class="c-num">0.2</span>):',
833
+ ' <span class="c-keyword">def</span> <span class="c-fn">voice_a</span>(): <span class="c-keyword">pass</span>',
834
+ ' <span class="c-keyword">def</span> <span class="c-fn">voice_b</span>(): <span class="c-keyword">pass</span>',
835
+ ' <span class="c-keyword">def</span> <span class="c-fn">voice_c</span>(): <span class="c-keyword">pass</span>',
836
+ ' <span class="c-keyword">def</span> <span class="c-fn">voice_d</span>(): <span class="c-keyword">pass</span>',
826
837
  ],
827
- // depth=1 cf=0 fns=4 comment_ratio=0.20
838
+ // depth=1 cf=0 fns=4 comment_ratio=0.00
828
839
  cutoff: { pct: 10, val: '580 Hz' },
829
840
  lfo: { pct: 0, val: '0.10 Hz' },
830
- reverb: { pct: 20, val: '0.16' },
841
+ reverb: { pct: 0, val: '0.00' },
831
842
  voices: { pct: 100, val: '4 voices' },
832
843
  },
833
844
  {
834
845
  code: [
835
- '<span class="c-comment"># @loop pad interval=4.0</span>',
836
- '<span class="c-comment"># smoke above the kick</span>',
837
- '<span class="c-comment"># late reflections</span>',
838
- '<span class="c-comment"># concrete room tail</span>',
839
- '<span class="c-comment"># crowd heat</span>',
840
- '<span class="c-keyword">def</span> <span class="c-fn">pad</span>(): <span class="c-keyword">pass</span>',
846
+ '<span class="c-decorator">@loop</span>(<span class="c-str">"pad"</span>, interval=<span class="c-num">4.0</span>)',
847
+ '<span class="c-keyword">def</span> <span class="c-fn">pad</span>(volume=<span class="c-num">0.15</span>):',
848
+ ' <span class="c-comment"># smoke above the kick</span>',
849
+ ' <span class="c-comment"># late reflections</span>',
850
+ ' <span class="c-comment"># concrete room tail</span>',
851
+ ' <span class="c-comment"># crowd heat</span>',
852
+ ' <span class="c-keyword">pass</span>',
841
853
  ],
842
- // depth=1 cf=0 fns=1 comment_ratio=0.83
854
+ // depth=1 cf=0 fns=1 comment_ratio=0.67
843
855
  cutoff: { pct: 10, val: '580 Hz' },
844
856
  lfo: { pct: 0, val: '0.10 Hz' },
845
- reverb: { pct: 84, val: '0.67' },
857
+ reverb: { pct: 71, val: '0.57' },
846
858
  voices: { pct: 25, val: '1 voice' },
847
859
  },
848
860
  ];