pycodedj 0.3.0__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.3.0 → pycodedj-0.4.0}/CHANGELOG.md +22 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/PKG-INFO +73 -118
- pycodedj-0.4.0/README.ja.md +209 -0
- pycodedj-0.4.0/README.md +209 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/docs/manual.html +191 -147
- pycodedj-0.4.0/docs/manual.ja.md +1208 -0
- pycodedj-0.4.0/docs/manual.md +1175 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/pyproject.toml +1 -1
- {pycodedj-0.3.0 → pycodedj-0.4.0}/sc/synths.scd +126 -20
- {pycodedj-0.3.0 → pycodedj-0.4.0}/src/pycodedj/__init__.py +3 -3
- pycodedj-0.4.0/src/pycodedj/_loop.py +20 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/src/pycodedj/block_parser.py +62 -6
- {pycodedj-0.3.0 → pycodedj-0.4.0}/src/pycodedj/engine.py +36 -2
- {pycodedj-0.3.0 → pycodedj-0.4.0}/src/pycodedj/osc_bridge.py +24 -0
- pycodedj-0.4.0/src/pycodedj/pattern.py +47 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/tests/test_block_parser.py +123 -0
- pycodedj-0.4.0/tests/test_engine.py +510 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/tests/test_osc_bridge.py +43 -0
- pycodedj-0.4.0/tests/test_pattern.py +137 -0
- pycodedj-0.3.0/README.ja.md +0 -254
- pycodedj-0.3.0/README.md +0 -254
- pycodedj-0.3.0/docs/manual.ja.md +0 -1156
- pycodedj-0.3.0/docs/manual.md +0 -1154
- pycodedj-0.3.0/src/pycodedj/_loop.py +0 -9
- pycodedj-0.3.0/tests/test_engine.py +0 -239
- {pycodedj-0.3.0 → pycodedj-0.4.0}/.github/workflows/workflow.yml +0 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/.gitignore +0 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/LICENSE +0 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/docs/CNAME +0 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/docs/index.html +0 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/examples/club_set.py +0 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/examples/demo.py +0 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/examples/hello_sc.py +0 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/examples/sound_showcase.py +0 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/src/pycodedj/__main__.py +0 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/src/pycodedj/analyzer.py +0 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/src/pycodedj/mapper.py +0 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/src/pycodedj/watcher.py +0 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/tests/__init__.py +0 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/tests/test_analyzer.py +0 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/tests/test_mapper.py +0 -0
- {pycodedj-0.3.0 → pycodedj-0.4.0}/tests/test_watcher.py +0 -0
|
@@ -1,5 +1,27 @@
|
|
|
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
|
+
|
|
3
25
|
## [0.3.0] - 2026-05-08
|
|
4
26
|
|
|
5
27
|
### Features
|
|
@@ -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,154 +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
|
-
|
|
160
|
+
# pattern() mode: specify rhythm and pitch explicitly
|
|
161
|
+
@loop("kick", synth="floor_kick", dur=0.25)
|
|
162
|
+
def kick():
|
|
163
|
+
pattern("x . x .")
|
|
164
|
+
|
|
165
|
+
@loop("melody", synth="acid_lead", root="A3", scale="minor", dur=0.25)
|
|
166
|
+
def melody():
|
|
167
|
+
pattern("0 . 3 . 5 .")
|
|
162
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.
|
|
171
|
-
|
|
172
|
-
```bash
|
|
173
|
-
pycodedj eval demo.py::bass
|
|
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
|
-
```
|
|
181
|
-
|
|
182
|
-
Other loops keep playing without interruption.
|
|
183
|
-
|
|
184
|
-
**4. Live-code with watch mode**
|
|
185
|
-
|
|
186
|
-
Instead of running eval manually, use watch to re-evaluate all loops on every save:
|
|
177
|
+
**3. Start watch mode**
|
|
187
178
|
|
|
188
179
|
```bash
|
|
189
180
|
pycodedj watch demo.py
|
|
190
181
|
```
|
|
191
182
|
|
|
192
|
-
From here, just write code and save.
|
|
183
|
+
From here, just write code and save. Every save re-evaluates all loops.
|
|
193
184
|
|
|
194
|
-
**
|
|
185
|
+
**4. Emergency stop**
|
|
195
186
|
|
|
196
187
|
```bash
|
|
197
188
|
pycodedj panic
|
|
198
189
|
```
|
|
199
190
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
**6. Mute / solo**
|
|
191
|
+
**5. Mute / unmute**
|
|
203
192
|
|
|
204
193
|
```bash
|
|
205
|
-
pycodedj mute bass
|
|
206
|
-
pycodedj unmute bass
|
|
207
|
-
pycodedj solo pad # mute all loops except pad
|
|
208
|
-
pycodedj unsolo # release solo, restore previous mute state
|
|
194
|
+
pycodedj mute bass
|
|
195
|
+
pycodedj unmute bass
|
|
209
196
|
```
|
|
210
197
|
|
|
211
|
-
Note: `mute`, `unmute`, `solo`, and `unsolo` send OSC directly to SuperCollider. For full state management, call `Engine.mute()` / `Engine.solo()` from within a watch session.
|
|
212
|
-
|
|
213
|
-
**7. Loop status**
|
|
214
|
-
|
|
215
|
-
```bash
|
|
216
|
-
pycodedj status
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
Prints the name, mute state, amplitude, and filter cutoff of each active loop.
|
|
220
|
-
|
|
221
198
|
---
|
|
222
199
|
|
|
223
|
-
##
|
|
200
|
+
## Using pattern()
|
|
224
201
|
|
|
225
|
-
|
|
226
|
-
| :--- | :--- |
|
|
227
|
-
| `examples/demo.py` | Intro demo with bass / melody / pad |
|
|
228
|
-
| `examples/club_set.py` | EDM groove (8 loops, kick → acid bass → rave stabs → hoover → shimmer) |
|
|
229
|
-
| `examples/sound_showcase.py` | All 30 synths — evaluate one at a time to audition each sound |
|
|
230
|
-
|
|
231
|
-
---
|
|
232
|
-
|
|
233
|
-
## Live-Coding Examples
|
|
234
|
-
|
|
235
|
-
### Deeper nesting opens the filter
|
|
202
|
+
`pattern()` lets you specify rhythm and pitch explicitly.
|
|
236
203
|
|
|
237
204
|
```python
|
|
238
|
-
from pycodedj import loop
|
|
205
|
+
from pycodedj import loop, pattern
|
|
239
206
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if i == j: # depth +1, control flow +1
|
|
245
|
-
pass
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### More functions = more polyphony
|
|
207
|
+
# Trigger pattern (x = hit, . = rest)
|
|
208
|
+
@loop("kick", synth="floor_kick", dur=0.25)
|
|
209
|
+
def kick():
|
|
210
|
+
pattern("x . x .")
|
|
249
211
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
def chord(volume=0.2):
|
|
255
|
-
def voice_a(): pass
|
|
256
|
-
def voice_b(): pass
|
|
257
|
-
def voice_c(): pass
|
|
258
|
-
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 .")
|
|
259
216
|
```
|
|
260
217
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
```python
|
|
264
|
-
from pycodedj import loop
|
|
218
|
+
`@loop` arguments for pattern mode:
|
|
265
219
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
```
|
|
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 |
|
|
273
226
|
|
|
274
227
|
---
|
|
275
228
|
|
|
276
|
-
##
|
|
229
|
+
## Example Files
|
|
277
230
|
|
|
278
|
-
|
|
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 |
|
|
279
236
|
|
|
280
|
-
|
|
281
|
-
| :--- | :--- | :--- | :--- |
|
|
282
|
-
| `/pycodedj/loop/<name>/params` | int, float, float, float, float | see parameter order | `voice_count`, `cutoff`, `lfo_rate`, `reverb`, `amp` |
|
|
283
|
-
| `/pycodedj/loop/<name>/cutoff` | float | 200–4000 Hz | Filter Cutoff (compatibility) |
|
|
284
|
-
| `/pycodedj/loop/<name>/lfo_rate` | float | 0.1–5.0 Hz | LFO rate (compatibility) |
|
|
285
|
-
| `/pycodedj/loop/<name>/reverb` | float | 0.0–0.8 | Reverb depth (compatibility) |
|
|
286
|
-
| `/pycodedj/loop/<name>/voice_count` | int | 1–4 | Polyphony voice count (compatibility) |
|
|
287
|
-
| `/pycodedj/loop/<name>/amp` | float | 0.0–1.0 | Amplitude (compatibility) |
|
|
237
|
+
---
|
|
288
238
|
|
|
289
|
-
|
|
239
|
+
## OSC Address Reference
|
|
290
240
|
|
|
291
|
-
|
|
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) |
|
|
292
247
|
|
|
293
248
|
---
|
|
294
249
|
|
|
@@ -305,9 +260,9 @@ External visualisers such as Hydra can receive the same parameters on a separate
|
|
|
305
260
|
- [x] Python → SuperCollider OSC prototype
|
|
306
261
|
- [x] Hot-reload live loop implementation (`pycodedj watch`)
|
|
307
262
|
- [x] Sprint 1: Live stability (`panic`, SyntaxError recovery, `mute`/`solo`, `status`)
|
|
308
|
-
- [
|
|
309
|
-
- [ ] Sprint 3: Sound design and playability (SynthDef cleanup, `bpm`, `list-synths`)
|
|
310
|
-
- [ ] Sprint 4:
|
|
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
|
|
311
266
|
|
|
312
267
|
---
|
|
313
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) を参照してください。
|