pycodedj 0.2.1__tar.gz → 0.4.0__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.2.1 → pycodedj-0.4.0}/CHANGELOG.md +46 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/PKG-INFO +80 -97
- pycodedj-0.4.0/README.ja.md +209 -0
- pycodedj-0.4.0/README.md +209 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/docs/manual.html +251 -149
- pycodedj-0.4.0/docs/manual.ja.md +1208 -0
- pycodedj-0.4.0/docs/manual.md +1175 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/pyproject.toml +1 -1
- {pycodedj-0.2.1 → pycodedj-0.4.0}/sc/synths.scd +141 -20
- {pycodedj-0.2.1 → pycodedj-0.4.0}/src/pycodedj/__init__.py +3 -3
- pycodedj-0.4.0/src/pycodedj/__main__.py +256 -0
- pycodedj-0.4.0/src/pycodedj/_loop.py +20 -0
- pycodedj-0.4.0/src/pycodedj/block_parser.py +153 -0
- pycodedj-0.4.0/src/pycodedj/engine.py +137 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/src/pycodedj/osc_bridge.py +29 -0
- pycodedj-0.4.0/src/pycodedj/pattern.py +47 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/src/pycodedj/watcher.py +8 -4
- pycodedj-0.4.0/tests/test_block_parser.py +278 -0
- pycodedj-0.4.0/tests/test_engine.py +510 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/tests/test_osc_bridge.py +70 -0
- pycodedj-0.4.0/tests/test_pattern.py +137 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/tests/test_watcher.py +17 -0
- pycodedj-0.2.1/README.ja.md +0 -226
- pycodedj-0.2.1/README.md +0 -226
- pycodedj-0.2.1/docs/manual.ja.md +0 -1072
- pycodedj-0.2.1/docs/manual.md +0 -1070
- pycodedj-0.2.1/src/pycodedj/__main__.py +0 -131
- pycodedj-0.2.1/src/pycodedj/_loop.py +0 -9
- pycodedj-0.2.1/src/pycodedj/block_parser.py +0 -90
- pycodedj-0.2.1/src/pycodedj/engine.py +0 -43
- pycodedj-0.2.1/tests/test_block_parser.py +0 -135
- pycodedj-0.2.1/tests/test_engine.py +0 -50
- {pycodedj-0.2.1 → pycodedj-0.4.0}/.github/workflows/workflow.yml +0 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/.gitignore +0 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/LICENSE +0 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/docs/CNAME +0 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/docs/index.html +0 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/examples/club_set.py +0 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/examples/demo.py +0 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/examples/hello_sc.py +0 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/examples/sound_showcase.py +0 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/src/pycodedj/analyzer.py +0 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/src/pycodedj/mapper.py +0 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/tests/__init__.py +0 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/tests/test_analyzer.py +0 -0
- {pycodedj-0.2.1 → pycodedj-0.4.0}/tests/test_mapper.py +0 -0
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.0] - 2026-05-08
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
- `pattern()` ヘルパー — `x`(トリガー)・`.`(休符)・整数(スケール度数)で構成したパターン文字列を SuperCollider に送信し `Pdef + Pbind` で再生する
|
|
8
|
+
- `@loop` デコレータに `synth=`, `root=`, `scale=`, `dur=` 引数を追加。`pattern()` ループでシンセ・ルートノート・スケール・ステップ長を指定できる
|
|
9
|
+
- `pycodedj_note` SynthDef を追加。音程パターン(度数を含む場合)は自動的にこのシンセで再生される
|
|
10
|
+
- `send_pattern` / `send_pattern_stop` / `send_synth` を `OscBridge` に追加
|
|
11
|
+
- SC 側: `~patterns` 辞書でアクティブな `Pdef` を追跡。`~startLoop` でパターン中のループに対して従来のシンセループが二重起動されないよう保護
|
|
12
|
+
|
|
13
|
+
### Improvements
|
|
14
|
+
|
|
15
|
+
- `block_parser._extract_pattern_call` を `ast.walk`(全ノード走査)から DFS preorder + ネストスコープ除外に変更。ネストされたヘルパー関数内の `pattern()` 呼び出しを誤検出しなくなった
|
|
16
|
+
- パターン引数の検証を OSC 送信より前に行い、検証失敗時に状態が更新されないよう修正(アトミック更新)
|
|
17
|
+
- パターンループは `voice_count=0` を送信し従来のシンセループを抑制。ミュート状態でも Pdef を停止しないよう `~applyLoopParams` を修正
|
|
18
|
+
- 度数トークンがある場合は `synth=` 指定の有無にかかわらず `pycodedj_note` を使用(既存シンセは `freq` を消費しないため)
|
|
19
|
+
|
|
20
|
+
### Docs
|
|
21
|
+
|
|
22
|
+
- `docs/manual.ja.md` / `docs/manual.md` / `docs/manual.html` — マニュアル全面改訂。`pattern()` の使い方(第 7 章)を新設し、初心者向けに丁寧に再構成
|
|
23
|
+
- `README.ja.md` / `README.md` — Sprint 2 機能(`pattern()`, `@loop` 拡張)の説明を追加、ロードマップの Sprint 2 を完了に更新
|
|
24
|
+
|
|
25
|
+
## [0.3.0] - 2026-05-08
|
|
26
|
+
|
|
27
|
+
### Features
|
|
28
|
+
|
|
29
|
+
- `pycodedj panic` — 全アクティブループを即時停止。SuperCollider 側でシンセを解放し `~loops` / `~loopParams` を初期化する
|
|
30
|
+
- `pycodedj mute <name>` — ループを消音(停止しない)。再評価時もミュート状態を維持
|
|
31
|
+
- `pycodedj unmute <name>` — ミュート解除、音量を復元
|
|
32
|
+
- `pycodedj solo <name>` — 対象ループ以外を全ミュート(CLI は OSC 直送、完全な状態管理は watch セッション内の `Engine.solo()` 推奨)
|
|
33
|
+
- `pycodedj unsolo` — ソロ解除、solo 前のミュート状態に戻す
|
|
34
|
+
- `pycodedj status` — アクティブループの名前・ミュート状態・音量・カットオフを表示
|
|
35
|
+
|
|
36
|
+
### Improvements
|
|
37
|
+
|
|
38
|
+
- `ParseResult` dataclass 導入 — `parse_blocks()` が SyntaxError 時に `ParseResult(ok=False, error=...)` を返すようになり、watch モードでコード編集中に構文エラーがあっても演奏中のループが止まらなくなった
|
|
39
|
+
- `Engine` 内部状態を `LoopState` dataclass で管理 — `muted` / `muted_before_solo` フラグを保持し、mute/solo/unsolo の状態を正確に追跡
|
|
40
|
+
- `eval_block` が OSC 送信成功後に内部状態を更新するよう修正(送信失敗時の状態不整合を解消)
|
|
41
|
+
- `Engine.solo()` に未知のループ名ガードを追加(存在しない名前を渡しても全ループがミュートされない)
|
|
42
|
+
- CLI `unmute` が SuperCollider に永続化された `amp=0` を上書きしてから voice_count を送るよう修正
|
|
43
|
+
|
|
44
|
+
### Docs
|
|
45
|
+
|
|
46
|
+
- README.md / README.ja.md — panic / mute / unmute / status のクイックリファレンスを追加、ロードマップの Sprint 1 を完了に更新
|
|
47
|
+
- `docs/manual.md` / `docs/manual.ja.md` / `docs/manual.html` — セクション 12 に新コマンド 6 つの全リファレンスを追加
|
|
48
|
+
|
|
3
49
|
## [0.2.1] - 2026-05-08
|
|
4
50
|
|
|
5
51
|
### Docs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pycodedj
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
License: MIT License with Commons Clause
|
|
5
5
|
|
|
6
6
|
"Commons Clause" License Condition v1.0
|
|
@@ -72,12 +72,12 @@ A live-coding environment that translates Python code structure into music in re
|
|
|
72
72
|
|
|
73
73
|
PyCodeDJ connects "writing code" directly to "making sound."
|
|
74
74
|
|
|
75
|
-
Add more
|
|
75
|
+
Add more `for` loops and the modulation speeds up. Deepen nesting and the filter opens up. Fill in comments and the space grows. Write `pattern("x . x .")` and that rhythm plays. Write `pattern("0 . 3 . 5 .")` and those pitches ring out.
|
|
76
76
|
|
|
77
77
|
Two things set it apart from existing Python ↔ SuperCollider bridges (sc3nb, supriya):
|
|
78
78
|
|
|
79
|
-
- **Hot-reload performance** — swap out a loop without stopping it. Saving a file
|
|
80
|
-
- **
|
|
79
|
+
- **Hot-reload performance** — swap out a loop without stopping it. Saving a file is an immediate sound change.
|
|
80
|
+
- **Two performance styles** — auto-generation from code structure, and explicit `pattern()` notation for rhythm and pitch. Mix them freely in the same file.
|
|
81
81
|
|
|
82
82
|
---
|
|
83
83
|
|
|
@@ -101,16 +101,14 @@ BPM clock is held by SuperCollider's `TempoClock`. Python only sends parameter u
|
|
|
101
101
|
|
|
102
102
|
## Code Structure → Music Parameter Mapping
|
|
103
103
|
|
|
104
|
-
| Code feature | Music parameter |
|
|
105
|
-
| :--- | :--- |
|
|
106
|
-
| Max nesting depth | Filter Cutoff (200–4000 Hz) |
|
|
107
|
-
| Control-flow count (if/for/while) | LFO rate (0.1–5.0 Hz) |
|
|
108
|
-
| Function definition count | Polyphony voice count (1–4) |
|
|
109
|
-
| Comment ratio | Reverb depth (0.0–0.8) |
|
|
110
|
-
| `volume=` argument | Amplitude (0.0–1.0) |
|
|
111
|
-
| `eq=` / `low=` / `mid=` / `high=` arguments | Simple 3-band EQ |
|
|
112
|
-
|
|
113
|
-
Tempo (BPM) and root pitch are controlled explicitly by the performer, to prevent the foundation of the piece from shifting on every save.
|
|
104
|
+
| Code feature | Music parameter |
|
|
105
|
+
| :--- | :--- |
|
|
106
|
+
| Max nesting depth | Filter Cutoff (200–4000 Hz) |
|
|
107
|
+
| Control-flow count (if/for/while) | LFO rate (0.1–5.0 Hz) |
|
|
108
|
+
| Function definition count | Polyphony voice count (1–4) |
|
|
109
|
+
| Comment ratio | Reverb depth (0.0–0.8) |
|
|
110
|
+
| `volume=` argument | Amplitude (0.0–1.0) |
|
|
111
|
+
| `eq=` / `low=` / `mid=` / `high=` arguments | Simple 3-band EQ |
|
|
114
112
|
|
|
115
113
|
---
|
|
116
114
|
|
|
@@ -130,7 +128,7 @@ The `[watch]` extra enables the `pycodedj watch` command.
|
|
|
130
128
|
Development install:
|
|
131
129
|
|
|
132
130
|
```bash
|
|
133
|
-
git clone https://github.com/
|
|
131
|
+
git clone https://github.com/kanekoyuichi/pycodedj
|
|
134
132
|
cd pycodedj
|
|
135
133
|
pip install -e ".[dev]"
|
|
136
134
|
```
|
|
@@ -141,127 +139,111 @@ pip install -e ".[dev]"
|
|
|
141
139
|
|
|
142
140
|
**1. Boot SuperCollider and load the synths**
|
|
143
141
|
|
|
144
|
-
Open `sc/synths.scd` in the SuperCollider IDE
|
|
142
|
+
Open `sc/synths.scd` in the SuperCollider IDE. Press Ctrl+A (Cmd+A on Mac) to select all, then Ctrl+Enter (Cmd+Enter on Mac) to run. When the Post window shows this, you're ready:
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
PyCodeDJ synths loaded. Ready. OSC port: 57120
|
|
146
|
+
```
|
|
145
147
|
|
|
146
148
|
**2. Write a live-coding file**
|
|
147
149
|
|
|
148
150
|
```python
|
|
149
|
-
from pycodedj import loop
|
|
151
|
+
from pycodedj import loop, pattern
|
|
150
152
|
|
|
153
|
+
# Code-structure mode: the shape of your code maps to sound
|
|
151
154
|
@loop("bass", interval=2.0)
|
|
152
155
|
def bass(volume=0.4):
|
|
153
156
|
for i in range(8):
|
|
154
157
|
if i % 2 == 0:
|
|
155
158
|
pass
|
|
156
159
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return x + y
|
|
160
|
+
# pattern() mode: specify rhythm and pitch explicitly
|
|
161
|
+
@loop("kick", synth="floor_kick", dur=0.25)
|
|
162
|
+
def kick():
|
|
163
|
+
pattern("x . x .")
|
|
162
164
|
|
|
165
|
+
@loop("melody", synth="acid_lead", root="A3", scale="minor", dur=0.25)
|
|
166
|
+
def melody():
|
|
167
|
+
pattern("0 . 3 . 5 .")
|
|
168
|
+
|
|
169
|
+
# Comments create space (reverb)
|
|
163
170
|
@loop("pad", interval=4.0)
|
|
164
|
-
def pad(volume=0.
|
|
165
|
-
#
|
|
166
|
-
#
|
|
171
|
+
def pad(volume=0.1):
|
|
172
|
+
# ambient space
|
|
173
|
+
# silence is music
|
|
167
174
|
pass
|
|
168
175
|
```
|
|
169
176
|
|
|
170
|
-
**3.
|
|
177
|
+
**3. Start watch mode**
|
|
171
178
|
|
|
172
179
|
```bash
|
|
173
|
-
pycodedj
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
On success, feedback is printed immediately:
|
|
177
|
-
|
|
178
|
-
```
|
|
179
|
-
[pycodedj] bass cutoff=418Hz lfo=1.08Hz reverb=0.00 voices=1 amp=0.40
|
|
180
|
+
pycodedj watch demo.py
|
|
180
181
|
```
|
|
181
182
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
**4. Live-code with watch mode**
|
|
183
|
+
From here, just write code and save. Every save re-evaluates all loops.
|
|
185
184
|
|
|
186
|
-
|
|
185
|
+
**4. Emergency stop**
|
|
187
186
|
|
|
188
187
|
```bash
|
|
189
|
-
pycodedj
|
|
188
|
+
pycodedj panic
|
|
190
189
|
```
|
|
191
190
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
---
|
|
195
|
-
|
|
196
|
-
## Example Files
|
|
191
|
+
**5. Mute / unmute**
|
|
197
192
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
| `examples/sound_showcase.py` | All 30 synths — evaluate one at a time to audition each sound |
|
|
193
|
+
```bash
|
|
194
|
+
pycodedj mute bass
|
|
195
|
+
pycodedj unmute bass
|
|
196
|
+
```
|
|
203
197
|
|
|
204
198
|
---
|
|
205
199
|
|
|
206
|
-
##
|
|
200
|
+
## Using pattern()
|
|
207
201
|
|
|
208
|
-
|
|
202
|
+
`pattern()` lets you specify rhythm and pitch explicitly.
|
|
209
203
|
|
|
210
204
|
```python
|
|
211
|
-
from pycodedj import loop
|
|
212
|
-
|
|
213
|
-
@loop("bass", interval=2.0)
|
|
214
|
-
def bass(volume=0.4):
|
|
215
|
-
for i in range(4): # control flow +1
|
|
216
|
-
for j in range(4): # depth +1, control flow +1
|
|
217
|
-
if i == j: # depth +1, control flow +1
|
|
218
|
-
pass
|
|
219
|
-
```
|
|
205
|
+
from pycodedj import loop, pattern
|
|
220
206
|
|
|
221
|
-
|
|
207
|
+
# Trigger pattern (x = hit, . = rest)
|
|
208
|
+
@loop("kick", synth="floor_kick", dur=0.25)
|
|
209
|
+
def kick():
|
|
210
|
+
pattern("x . x .")
|
|
222
211
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
|
212
|
+
# Pitch pattern (integer = scale degree)
|
|
213
|
+
@loop("bass", synth="bass_acid", root="A1", scale="minor", dur=0.25)
|
|
214
|
+
def bass():
|
|
215
|
+
pattern("0 . 3 . 5 .")
|
|
232
216
|
```
|
|
233
217
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
```python
|
|
237
|
-
from pycodedj import loop
|
|
218
|
+
`@loop` arguments for pattern mode:
|
|
238
219
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
```
|
|
220
|
+
| Argument | Description |
|
|
221
|
+
| :--- | :--- |
|
|
222
|
+
| `synth=` | Synth name to use |
|
|
223
|
+
| `root=` | Root note (e.g. `"A3"`, `"C4"`) |
|
|
224
|
+
| `scale=` | Scale name (e.g. `"minor"`, `"major"`, `"pentatonicMinor"`) |
|
|
225
|
+
| `dur=` | Step length in seconds. `0.25` = sixteenth note at 60 BPM |
|
|
246
226
|
|
|
247
227
|
---
|
|
248
228
|
|
|
249
|
-
##
|
|
229
|
+
## Example Files
|
|
250
230
|
|
|
251
|
-
|
|
231
|
+
| File | Contents |
|
|
232
|
+
| :--- | :--- |
|
|
233
|
+
| `examples/demo.py` | Intro demo: bass / melody / pad |
|
|
234
|
+
| `examples/club_set.py` | EDM groove: 8 loops (kick, bass, hat, chords, pad) |
|
|
235
|
+
| `examples/sound_showcase.py` | All 30 synths — evaluate one at a time to audition |
|
|
252
236
|
|
|
253
|
-
|
|
254
|
-
| :--- | :--- | :--- | :--- |
|
|
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) |
|
|
237
|
+
---
|
|
261
238
|
|
|
262
|
-
|
|
239
|
+
## OSC Address Reference
|
|
263
240
|
|
|
264
|
-
|
|
241
|
+
| Address | Type | Parameter |
|
|
242
|
+
| :--- | :--- | :--- |
|
|
243
|
+
| `/pycodedj/loop/<name>/params` | int, float, float, float, float | `voice_count`, `cutoff`, `lfo_rate`, `reverb`, `amp` |
|
|
244
|
+
| `/pycodedj/loop/<name>/pattern` | int, str, float, str, int… | Pattern data |
|
|
245
|
+
| `/pycodedj/loop/<name>/pattern_stop` | — | Stop pattern |
|
|
246
|
+
| `/pycodedj/loop/<name>/amp` | float | Amplitude (compatibility) |
|
|
265
247
|
|
|
266
248
|
---
|
|
267
249
|
|
|
@@ -275,11 +257,12 @@ External visualisers such as Hydra can receive the same parameters on a separate
|
|
|
275
257
|
|
|
276
258
|
## Roadmap
|
|
277
259
|
|
|
278
|
-
- [x]
|
|
279
|
-
- [
|
|
280
|
-
- [x]
|
|
281
|
-
- [x]
|
|
282
|
-
- [ ]
|
|
260
|
+
- [x] Python → SuperCollider OSC prototype
|
|
261
|
+
- [x] Hot-reload live loop implementation (`pycodedj watch`)
|
|
262
|
+
- [x] Sprint 1: Live stability (`panic`, SyntaxError recovery, `mute`/`solo`, `status`)
|
|
263
|
+
- [x] Sprint 2: Music DSL (`pattern()`, `@loop` parameter expansion: `synth`, `root`, `scale`, `dur`)
|
|
264
|
+
- [ ] Sprint 3: Sound design and playability (SynthDef cleanup, `bpm`, `list-synths`, `sample()`)
|
|
265
|
+
- [ ] Sprint 4: Hydra visualiser integration
|
|
283
266
|
|
|
284
267
|
---
|
|
285
268
|
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# PyCodeDJ
|
|
2
|
+
|
|
3
|
+
[English README](https://github.com/kanekoyuichi/pycodedj/blob/main/README.md) · [マニュアル (JA)](https://github.com/kanekoyuichi/pycodedj/blob/main/docs/manual.ja.md) · [Full Manual (EN)](https://github.com/kanekoyuichi/pycodedj/blob/main/docs/manual.md)
|
|
4
|
+
|
|
5
|
+
Python のコードを書くと、リアルタイムに音が変わるライブコーディング環境。ファイルを保存するたびに演奏が変わる。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## コンセプト
|
|
10
|
+
|
|
11
|
+
PyCodeDJ は「コードを書くこと」と「音を鳴らすこと」を直結させます。
|
|
12
|
+
|
|
13
|
+
`for` ループを増やすと音の揺らぎが速くなり、ネストを深くするとフィルターが開き、コメントを書き込むと空間が広がります。`pattern("x . x .")` と書けばそのリズムで音が鳴り、`pattern("0 . 3 . 5 .")` と書けば指定した音程で演奏されます。
|
|
14
|
+
|
|
15
|
+
既存の Python ↔ SuperCollider ブリッジ(sc3nb・supriya)との違いは 2 点です。
|
|
16
|
+
|
|
17
|
+
- **ホットリロード演奏** — ループを止めずにファイルを差し替える。保存が即座に音の変化になる
|
|
18
|
+
- **2 つの演奏スタイル** — コードの構造から自動生成するモードと、`pattern()` で音程とリズムを明示指定するモードを自由に混在させられる
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## アーキテクチャ
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
[Python エンジン] →OSC→ [SuperCollider] →音響出力→ スピーカー
|
|
26
|
+
↓ OSC
|
|
27
|
+
[Hydra 等] →映像出力→ スクリーン
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
| 層 | 役割 | 技術 |
|
|
31
|
+
| :--- | :--- | :--- |
|
|
32
|
+
| 制御層 | コード解析・スケジューリング・OSC 送出 | Python 3.10+, python-osc, watchdog |
|
|
33
|
+
| 音響層 | リアルタイム音響合成 | SuperCollider (scsynth) |
|
|
34
|
+
| 視覚層 | 音楽データに同期した映像生成 | Hydra または Pyxel |
|
|
35
|
+
|
|
36
|
+
BPM クロックは SuperCollider 側の `TempoClock` が保持します。Python は「次のループで使う設定の更新」を OSC で送るだけで、タイミング精度は SuperCollider に委ねます。
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## コード構造 → 音楽パラメーターのマッピング
|
|
41
|
+
|
|
42
|
+
| コード特徴量 | 音楽パラメーター |
|
|
43
|
+
| :--- | :--- |
|
|
44
|
+
| ネストの深さ(最大) | フィルター Cutoff (200–4000 Hz) |
|
|
45
|
+
| 制御フロー数(if/for/while) | LFO レート (0.1–5.0 Hz) |
|
|
46
|
+
| 関数定義数 | ポリフォニー声部数 (1–4) |
|
|
47
|
+
| コメント率 | リバーブ Depth (0.0–0.8) |
|
|
48
|
+
| `volume=` 引数 | Amplitude (0.0–1.0) |
|
|
49
|
+
| `eq=` / `low=` / `mid=` / `high=` 引数 | 簡易 3 バンド EQ |
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## インストール
|
|
54
|
+
|
|
55
|
+
**必要なもの**
|
|
56
|
+
|
|
57
|
+
- Python 3.10 以上
|
|
58
|
+
- SuperCollider(scsynth が起動できる環境)
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install 'pycodedj[watch]'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`[watch]` を付けると `pycodedj watch` コマンドも使えます。
|
|
65
|
+
|
|
66
|
+
開発用:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git clone https://github.com/kanekoyuichi/pycodedj
|
|
70
|
+
cd pycodedj
|
|
71
|
+
pip install -e ".[dev]"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## クイックスタート
|
|
77
|
+
|
|
78
|
+
**1. SuperCollider を起動してシンセを読み込む**
|
|
79
|
+
|
|
80
|
+
SuperCollider IDE で `sc/synths.scd` を開き、Ctrl+A(Mac は Cmd+A)→ Ctrl+Enter(Mac は Cmd+Enter)で実行します。Post window に次が出れば準備完了です。
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
PyCodeDJ synths loaded. Ready. OSC port: 57120
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**2. ライブコーディングファイルを用意する**
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from pycodedj import loop, pattern
|
|
90
|
+
|
|
91
|
+
# コードの「構造」が音楽パラメーターになるモード
|
|
92
|
+
@loop("bass", interval=2.0)
|
|
93
|
+
def bass(volume=0.4):
|
|
94
|
+
for i in range(8):
|
|
95
|
+
if i % 2 == 0:
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
# pattern() でリズムと音程を明示指定するモード
|
|
99
|
+
@loop("kick", synth="floor_kick", dur=0.25)
|
|
100
|
+
def kick():
|
|
101
|
+
pattern("x . x .")
|
|
102
|
+
|
|
103
|
+
@loop("melody", synth="acid_lead", root="A3", scale="minor", dur=0.25)
|
|
104
|
+
def melody():
|
|
105
|
+
pattern("0 . 3 . 5 .")
|
|
106
|
+
|
|
107
|
+
# コメントで空間を作るモード
|
|
108
|
+
@loop("pad", interval=4.0)
|
|
109
|
+
def pad(volume=0.1):
|
|
110
|
+
# 背景の空気
|
|
111
|
+
# 余白
|
|
112
|
+
pass
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**3. watch モードで起動する**
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
pycodedj watch demo.py
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
あとはエディタでコードを書いて保存するだけです。保存のたびに全ループが再評価されます。
|
|
122
|
+
|
|
123
|
+
**4. 緊急停止**
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pycodedj panic
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**5. ミュート / アンミュート**
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
pycodedj mute bass
|
|
133
|
+
pycodedj unmute bass
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## pattern() の使い方
|
|
139
|
+
|
|
140
|
+
`pattern()` を使うと、リズムと音程を明示的に指定できます。
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from pycodedj import loop, pattern
|
|
144
|
+
|
|
145
|
+
# トリガーパターン(x=鳴らす、.=休符)
|
|
146
|
+
@loop("kick", synth="floor_kick", dur=0.25)
|
|
147
|
+
def kick():
|
|
148
|
+
pattern("x . x .")
|
|
149
|
+
|
|
150
|
+
# 音程パターン(数字=スケール度数)
|
|
151
|
+
@loop("bass", synth="bass_acid", root="A1", scale="minor", dur=0.25)
|
|
152
|
+
def bass():
|
|
153
|
+
pattern("0 . 3 . 5 .")
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`@loop` に渡す引数:
|
|
157
|
+
|
|
158
|
+
| 引数 | 説明 |
|
|
159
|
+
| :--- | :--- |
|
|
160
|
+
| `synth=` | 使うシンセ名 |
|
|
161
|
+
| `root=` | ルートノート(例: `"A3"`, `"C4"`) |
|
|
162
|
+
| `scale=` | スケール(例: `"minor"`, `"major"`, `"pentatonicMinor"`) |
|
|
163
|
+
| `dur=` | 1 ステップの長さ(秒)。`0.25` で 16 分音符相当 |
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## サンプルファイル
|
|
168
|
+
|
|
169
|
+
| ファイル | 内容 |
|
|
170
|
+
| :--- | :--- |
|
|
171
|
+
| `examples/demo.py` | bass / melody / pad の 3 ループ入門デモ |
|
|
172
|
+
| `examples/club_set.py` | EDM クラブグルーヴ(キック・ベース・ハット・コード・パッドを含む 8 ループ) |
|
|
173
|
+
| `examples/sound_showcase.py` | 全 30 音色 — 1 音ずつ eval して確認できる |
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## OSC アドレス仕様
|
|
178
|
+
|
|
179
|
+
| アドレス | 型 | 対応パラメーター |
|
|
180
|
+
| :--- | :--- | :--- |
|
|
181
|
+
| `/pycodedj/loop/<name>/params` | int, float, float, float, float | `voice_count`, `cutoff`, `lfo_rate`, `reverb`, `amp` |
|
|
182
|
+
| `/pycodedj/loop/<name>/pattern` | int, str, float, str, int… | パターンデータ |
|
|
183
|
+
| `/pycodedj/loop/<name>/pattern_stop` | — | パターン停止 |
|
|
184
|
+
| `/pycodedj/loop/<name>/amp` | float | 音量(互換用) |
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 動作環境
|
|
189
|
+
|
|
190
|
+
- **推奨 OS:** macOS(Core Audio の低遅延性を活用)または Linux(Raspberry Pi 5 等)
|
|
191
|
+
- **Python:** 3.10 以上
|
|
192
|
+
- **SuperCollider:** 3.12 以上
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## ロードマップ
|
|
197
|
+
|
|
198
|
+
- [x] Python → SuperCollider OSC プロトタイプ
|
|
199
|
+
- [x] ホットリロード・ライブループ実装(`pycodedj watch`)
|
|
200
|
+
- [x] Sprint 1: ライブ安定性(`panic`, SyntaxError 維持, `mute`/`solo`, `status`)
|
|
201
|
+
- [x] Sprint 2: 音楽 DSL(`pattern()`, `@loop` パラメータ拡張: `synth`, `root`, `scale`, `dur`)
|
|
202
|
+
- [ ] Sprint 3: 音色・演奏性(SynthDef 整理, `bpm`, `list-synths`, `sample()`)
|
|
203
|
+
- [ ] Sprint 4: Hydra ビジュアライザー統合
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## ライセンス
|
|
208
|
+
|
|
209
|
+
MIT + Commons Clause — 個人利用・改変・ライブパフォーマンス(有料公演含む)は自由です。ソフトウェアそのものの販売・有償サービス化は禁止しています。詳細は [LICENSE](LICENSE) を参照してください。
|