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.
- {pycodedj-0.1.4 → pycodedj-0.2.1}/CHANGELOG.md +31 -0
- {pycodedj-0.1.4 → pycodedj-0.2.1}/PKG-INFO +40 -27
- {pycodedj-0.1.4 → pycodedj-0.2.1}/README.ja.md +39 -26
- {pycodedj-0.1.4 → pycodedj-0.2.1}/README.md +39 -26
- {pycodedj-0.1.4 → pycodedj-0.2.1}/docs/index.html +58 -46
- pycodedj-0.2.1/docs/manual.html +1584 -0
- pycodedj-0.2.1/docs/manual.ja.md +1072 -0
- pycodedj-0.2.1/docs/manual.md +1070 -0
- pycodedj-0.2.1/examples/club_set.py +150 -0
- {pycodedj-0.1.4 → pycodedj-0.2.1}/examples/demo.py +14 -9
- {pycodedj-0.1.4 → pycodedj-0.2.1}/examples/hello_sc.py +1 -1
- pycodedj-0.2.1/examples/sound_showcase.py +295 -0
- {pycodedj-0.1.4 → pycodedj-0.2.1}/pyproject.toml +1 -1
- pycodedj-0.2.1/sc/synths.scd +724 -0
- {pycodedj-0.1.4 → pycodedj-0.2.1}/src/pycodedj/__init__.py +4 -1
- {pycodedj-0.1.4 → pycodedj-0.2.1}/src/pycodedj/__main__.py +1 -0
- pycodedj-0.2.1/src/pycodedj/_loop.py +9 -0
- pycodedj-0.2.1/src/pycodedj/block_parser.py +90 -0
- {pycodedj-0.1.4 → pycodedj-0.2.1}/src/pycodedj/engine.py +8 -1
- pycodedj-0.2.1/src/pycodedj/mapper.py +95 -0
- {pycodedj-0.1.4 → pycodedj-0.2.1}/src/pycodedj/osc_bridge.py +23 -9
- {pycodedj-0.1.4 → pycodedj-0.2.1}/src/pycodedj/watcher.py +5 -0
- pycodedj-0.2.1/tests/test_block_parser.py +135 -0
- {pycodedj-0.1.4 → pycodedj-0.2.1}/tests/test_mapper.py +41 -0
- {pycodedj-0.1.4 → pycodedj-0.2.1}/tests/test_osc_bridge.py +14 -9
- {pycodedj-0.1.4 → pycodedj-0.2.1}/tests/test_watcher.py +28 -16
- pycodedj-0.1.4/docs/manual.ja.md +0 -815
- pycodedj-0.1.4/docs/manual.md +0 -813
- pycodedj-0.1.4/examples/club_set.py +0 -71
- pycodedj-0.1.4/sc/synths.scd +0 -154
- pycodedj-0.1.4/src/pycodedj/block_parser.py +0 -45
- pycodedj-0.1.4/src/pycodedj/mapper.py +0 -46
- pycodedj-0.1.4/tests/test_block_parser.py +0 -66
- {pycodedj-0.1.4 → pycodedj-0.2.1}/.github/workflows/workflow.yml +0 -0
- {pycodedj-0.1.4 → pycodedj-0.2.1}/.gitignore +0 -0
- {pycodedj-0.1.4 → pycodedj-0.2.1}/LICENSE +0 -0
- {pycodedj-0.1.4 → pycodedj-0.2.1}/docs/CNAME +0 -0
- {pycodedj-0.1.4 → pycodedj-0.2.1}/src/pycodedj/analyzer.py +0 -0
- {pycodedj-0.1.4 → pycodedj-0.2.1}/tests/__init__.py +0 -0
- {pycodedj-0.1.4 → pycodedj-0.2.1}/tests/test_analyzer.py +0 -0
- {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
|
|
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
|
-
|
|
148
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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` |
|
|
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
|
-
|
|
209
|
-
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
def
|
|
223
|
-
def
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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>/
|
|
245
|
-
| `/pycodedj/loop/<name>/
|
|
246
|
-
| `/pycodedj/loop/<name>/
|
|
247
|
-
| `/pycodedj/loop/<name>/
|
|
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 | 200–4000 Hz | Filter Cutoff (compatibility) |
|
|
257
|
+
| `/pycodedj/loop/<name>/lfo_rate` | float | 0.1–5.0 Hz | LFO rate (compatibility) |
|
|
258
|
+
| `/pycodedj/loop/<name>/reverb` | float | 0.0–0.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
|
-
|
|
86
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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` |
|
|
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
|
-
|
|
147
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
def
|
|
161
|
-
def
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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>/
|
|
183
|
-
| `/pycodedj/loop/<name>/
|
|
184
|
-
| `/pycodedj/loop/<name>/
|
|
185
|
-
| `/pycodedj/loop/<name>/
|
|
193
|
+
| `/pycodedj/loop/<name>/params` | int, float, float, float, float | パラメーター順を参照 | `voice_count`, `cutoff`, `lfo_rate`, `reverb`, `amp` |
|
|
194
|
+
| `/pycodedj/loop/<name>/cutoff` | float | 200–4000 Hz | フィルター Cutoff(互換用) |
|
|
195
|
+
| `/pycodedj/loop/<name>/lfo_rate` | float | 0.1–5.0 Hz | LFO レート(互換用) |
|
|
196
|
+
| `/pycodedj/loop/<name>/reverb` | float | 0.0–0.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
|
-
|
|
86
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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` |
|
|
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
|
-
|
|
147
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
def
|
|
161
|
-
def
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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>/
|
|
183
|
-
| `/pycodedj/loop/<name>/
|
|
184
|
-
| `/pycodedj/loop/<name>/
|
|
185
|
-
| `/pycodedj/loop/<name>/
|
|
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 | 200–4000 Hz | Filter Cutoff (compatibility) |
|
|
195
|
+
| `/pycodedj/loop/<name>/lfo_rate` | float | 0.1–5.0 Hz | LFO rate (compatibility) |
|
|
196
|
+
| `/pycodedj/loop/<name>/reverb` | float | 0.0–0.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
|
|
202
|
-
.c-keyword
|
|
203
|
-
.c-fn
|
|
204
|
-
.c-string
|
|
205
|
-
.c-
|
|
206
|
-
.c-
|
|
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-
|
|
723
|
-
|
|
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-
|
|
735
|
-
|
|
736
|
-
<span class="c-
|
|
737
|
-
<span class="c-keyword">def</span> <span class="c-fn">
|
|
738
|
-
<span class="c-keyword">def</span> <span class="c-fn">
|
|
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-
|
|
746
|
-
|
|
747
|
-
<span class="c-
|
|
748
|
-
<span class="c-
|
|
749
|
-
<span class="c-comment">#
|
|
750
|
-
<span class="c-
|
|
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-
|
|
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.
|
|
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:
|
|
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-
|
|
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.
|
|
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:
|
|
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-
|
|
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.
|
|
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:
|
|
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-
|
|
822
|
-
'<span class="c-keyword">def</span> <span class="c-fn">
|
|
823
|
-
'<span class="c-keyword">def</span> <span class="c-fn">
|
|
824
|
-
'<span class="c-keyword">def</span> <span class="c-fn">
|
|
825
|
-
'<span class="c-keyword">def</span> <span class="c-fn">
|
|
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.
|
|
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:
|
|
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-
|
|
836
|
-
'<span class="c-
|
|
837
|
-
'<span class="c-comment">#
|
|
838
|
-
'<span class="c-comment">#
|
|
839
|
-
'<span class="c-comment">#
|
|
840
|
-
'<span class="c-
|
|
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.
|
|
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:
|
|
857
|
+
reverb: { pct: 71, val: '0.57' },
|
|
846
858
|
voices: { pct: 25, val: '1 voice' },
|
|
847
859
|
},
|
|
848
860
|
];
|