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.
Files changed (46) hide show
  1. {pycodedj-0.2.1 → pycodedj-0.4.0}/CHANGELOG.md +46 -0
  2. {pycodedj-0.2.1 → pycodedj-0.4.0}/PKG-INFO +80 -97
  3. pycodedj-0.4.0/README.ja.md +209 -0
  4. pycodedj-0.4.0/README.md +209 -0
  5. {pycodedj-0.2.1 → pycodedj-0.4.0}/docs/manual.html +251 -149
  6. pycodedj-0.4.0/docs/manual.ja.md +1208 -0
  7. pycodedj-0.4.0/docs/manual.md +1175 -0
  8. {pycodedj-0.2.1 → pycodedj-0.4.0}/pyproject.toml +1 -1
  9. {pycodedj-0.2.1 → pycodedj-0.4.0}/sc/synths.scd +141 -20
  10. {pycodedj-0.2.1 → pycodedj-0.4.0}/src/pycodedj/__init__.py +3 -3
  11. pycodedj-0.4.0/src/pycodedj/__main__.py +256 -0
  12. pycodedj-0.4.0/src/pycodedj/_loop.py +20 -0
  13. pycodedj-0.4.0/src/pycodedj/block_parser.py +153 -0
  14. pycodedj-0.4.0/src/pycodedj/engine.py +137 -0
  15. {pycodedj-0.2.1 → pycodedj-0.4.0}/src/pycodedj/osc_bridge.py +29 -0
  16. pycodedj-0.4.0/src/pycodedj/pattern.py +47 -0
  17. {pycodedj-0.2.1 → pycodedj-0.4.0}/src/pycodedj/watcher.py +8 -4
  18. pycodedj-0.4.0/tests/test_block_parser.py +278 -0
  19. pycodedj-0.4.0/tests/test_engine.py +510 -0
  20. {pycodedj-0.2.1 → pycodedj-0.4.0}/tests/test_osc_bridge.py +70 -0
  21. pycodedj-0.4.0/tests/test_pattern.py +137 -0
  22. {pycodedj-0.2.1 → pycodedj-0.4.0}/tests/test_watcher.py +17 -0
  23. pycodedj-0.2.1/README.ja.md +0 -226
  24. pycodedj-0.2.1/README.md +0 -226
  25. pycodedj-0.2.1/docs/manual.ja.md +0 -1072
  26. pycodedj-0.2.1/docs/manual.md +0 -1070
  27. pycodedj-0.2.1/src/pycodedj/__main__.py +0 -131
  28. pycodedj-0.2.1/src/pycodedj/_loop.py +0 -9
  29. pycodedj-0.2.1/src/pycodedj/block_parser.py +0 -90
  30. pycodedj-0.2.1/src/pycodedj/engine.py +0 -43
  31. pycodedj-0.2.1/tests/test_block_parser.py +0 -135
  32. pycodedj-0.2.1/tests/test_engine.py +0 -50
  33. {pycodedj-0.2.1 → pycodedj-0.4.0}/.github/workflows/workflow.yml +0 -0
  34. {pycodedj-0.2.1 → pycodedj-0.4.0}/.gitignore +0 -0
  35. {pycodedj-0.2.1 → pycodedj-0.4.0}/LICENSE +0 -0
  36. {pycodedj-0.2.1 → pycodedj-0.4.0}/docs/CNAME +0 -0
  37. {pycodedj-0.2.1 → pycodedj-0.4.0}/docs/index.html +0 -0
  38. {pycodedj-0.2.1 → pycodedj-0.4.0}/examples/club_set.py +0 -0
  39. {pycodedj-0.2.1 → pycodedj-0.4.0}/examples/demo.py +0 -0
  40. {pycodedj-0.2.1 → pycodedj-0.4.0}/examples/hello_sc.py +0 -0
  41. {pycodedj-0.2.1 → pycodedj-0.4.0}/examples/sound_showcase.py +0 -0
  42. {pycodedj-0.2.1 → pycodedj-0.4.0}/src/pycodedj/analyzer.py +0 -0
  43. {pycodedj-0.2.1 → pycodedj-0.4.0}/src/pycodedj/mapper.py +0 -0
  44. {pycodedj-0.2.1 → pycodedj-0.4.0}/tests/__init__.py +0 -0
  45. {pycodedj-0.2.1 → pycodedj-0.4.0}/tests/test_analyzer.py +0 -0
  46. {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.2.1
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 functions and the polyphony widens. Deepen nesting and the filter opens up. Fill in comments and the space grows. The structure of your code is the instrument.
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 becomes an immediate sound change.
80
- - **Audible code structure** — structural features extracted via AST analysis (depth, branch count, function count, etc.) are automatically mapped to musical parameters.
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 | Musical rationale |
105
- | :--- | :--- | :--- |
106
- | Max nesting depth | Filter Cutoff (200–4000 Hz) | Deep structure = complexity = brightness |
107
- | Control-flow count (if/for/while) | LFO rate (0.1–5.0 Hz) | More branches = faster modulation |
108
- | Function definition count | Polyphony voice count (1–4) | Functions = independent voices |
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 |
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/yourname/pycodedj
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 and evaluate it.
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
- @loop("melody", interval=0.5)
158
- def melody(volume=0.3):
159
- x = 1
160
- y = 2
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.15):
165
- # make space
166
- # a little more
171
+ def pad(volume=0.1):
172
+ # ambient space
173
+ # silence is music
167
174
  pass
168
175
  ```
169
176
 
170
- **3. Evaluate a block**
177
+ **3. Start watch mode**
171
178
 
172
179
  ```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
+ pycodedj watch demo.py
180
181
  ```
181
182
 
182
- Other loops keep playing without interruption.
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
- Instead of running eval manually, use watch to re-evaluate all loops on every save:
185
+ **4. Emergency stop**
187
186
 
188
187
  ```bash
189
- pycodedj watch demo.py
188
+ pycodedj panic
190
189
  ```
191
190
 
192
- From here, just write code and save.
193
-
194
- ---
195
-
196
- ## Example Files
191
+ **5. Mute / unmute**
197
192
 
198
- | File | Contents |
199
- | :--- | :--- |
200
- | `examples/demo.py` | Intro demo with bass / melody / pad |
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 |
193
+ ```bash
194
+ pycodedj mute bass
195
+ pycodedj unmute bass
196
+ ```
203
197
 
204
198
  ---
205
199
 
206
- ## Live-Coding Examples
200
+ ## Using pattern()
207
201
 
208
- ### Deeper nesting opens the filter
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
- ### 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 .")
222
211
 
223
- ```python
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
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
- ### More comments = more space (reverb)
235
-
236
- ```python
237
- from pycodedj import loop
218
+ `@loop` arguments for pattern mode:
238
219
 
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
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
- ## OSC Address Reference
229
+ ## Example Files
250
230
 
251
- Addresses used to communicate with SuperCollider.
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
- | Address | Type | Range | Parameter |
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
- `<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.
239
+ ## OSC Address Reference
263
240
 
264
- External visualisers such as Hydra can receive the same parameters on a separate port.
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] Spec design and mapping design
279
- - [ ] Phase 0: Listening validation of mapping hypotheses
280
- - [x] Phase 1: Python SuperCollider OSC prototype
281
- - [x] Phase 2: Hot-reload live loop implementation (`pycodedj watch`)
282
- - [ ] Phase 3: Hydra visualiser integration
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) を参照してください。