pycodedj 0.1.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.
@@ -0,0 +1,46 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+ id-token: write
10
+
11
+ jobs:
12
+ publish:
13
+ runs-on: ubuntu-latest
14
+
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ with:
18
+ ref: ${{ github.event.release.tag_name }}
19
+
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: "3.12"
23
+
24
+ - name: Verify tag matches pyproject.toml version
25
+ run: |
26
+ TAG="${{ github.event.release.tag_name }}"
27
+ PKG_VERSION=$(python -c "
28
+ import tomllib
29
+ with open('pyproject.toml', 'rb') as f:
30
+ print(tomllib.load(f)['project']['version'])
31
+ ")
32
+ EXPECTED="v${PKG_VERSION}"
33
+ if [ "$TAG" != "$EXPECTED" ]; then
34
+ echo "Tag '$TAG' does not match pyproject.toml version '$EXPECTED'" >&2
35
+ exit 1
36
+ fi
37
+ echo "Version check passed: $TAG"
38
+
39
+ - name: Install build dependencies
40
+ run: pip install build hatchling
41
+
42
+ - name: Build wheel and sdist
43
+ run: python -m build
44
+
45
+ - name: Publish to PyPI
46
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,79 @@
1
+ # OS
2
+ .DS_Store
3
+ Thumbs.db
4
+
5
+ # Editor
6
+ .vscode/
7
+ .idea/
8
+ *.swp
9
+ *.swo
10
+
11
+ # Environment
12
+ .env
13
+ .env.local
14
+ .env.*.local
15
+
16
+ # Secrets / Keys
17
+ *.pem
18
+ *.key
19
+ *.p12
20
+ *.pfx
21
+ secrets/
22
+ credentials/
23
+
24
+ # Python
25
+ __pycache__/
26
+ *.py[cod]
27
+ *.pyo
28
+ .venv/
29
+ venv/
30
+ env/
31
+ *.egg-info/
32
+ dist/
33
+ build/
34
+ .pytest_cache/
35
+ .mypy_cache/
36
+ .ruff_cache/
37
+
38
+ # Node.js
39
+ node_modules/
40
+ npm-debug.log*
41
+ yarn-debug.log*
42
+ yarn-error.log*
43
+ .pnpm-debug.log*
44
+
45
+ # Logs
46
+ *.log
47
+ logs/
48
+
49
+ # AI Agent
50
+ .claude/settings.local.json
51
+ .claude/todos/
52
+ .claude/mcp*.json
53
+ .codex
54
+ .claude/
55
+ AGENT.md
56
+
57
+ # Claude Code
58
+ CLAUDE.local.md
59
+ CLAUDE.md
60
+
61
+ # LLM / Vector DB
62
+ *.chroma/
63
+ chroma_db/
64
+ vector_store/
65
+ embeddings/
66
+ *.faiss
67
+ *.pkl
68
+
69
+ # Agent state / memory
70
+ agent_state/
71
+ agent_memory/
72
+ sessions/
73
+ checkpoints/
74
+ runs/
75
+
76
+ # Temporary
77
+ tmp/
78
+ temp/
79
+ .cache/
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+
3
+ ## [0.1.0] - 2026-05-07
4
+
5
+ ### Features
6
+
7
+ - `pycodedj eval FILE::LOOP` — evaluate a loop block and send OSC parameters to SuperCollider; prints feedback (`cutoff`, `lfo_rate`, `reverb_mix`, `voice_count`) on success and exits with code 1 on failure
8
+ - `pycodedj watch FILE` — watch a file and re-evaluate all loops on every save; handles atomic saves (vim/Emacs rename-based writes) via `on_moved` and `on_created` in addition to `on_modified`; stops loops that are removed from the file between reloads
9
+ - OSC bridge sends `voice_count` first so SuperCollider creates synths before receiving parameter updates
10
+ - AST-based code analysis maps block nesting depth → filter cutoff, control-flow count → LFO rate, function count → polyphony voices, comment ratio → reverb depth
11
+ - `sc/synths.scd` — SuperCollider synth definitions with a single `OSCFunc(nil path)` handler dispatching all `/pycodedj/loop/<name>/<param>` messages
12
+ - `examples/demo.py` and `examples/club_set.py` bundled in the wheel as package data
13
+
14
+ ### Docs
15
+
16
+ - `README.ja.md` and `README.md` with architecture overview, quick start, and OSC address reference
17
+ - `docs/manual.ja.md` and `docs/manual.md` — beginner-friendly full manual covering setup, watch mode, code-to-sound mapping, club set example, and troubleshooting
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: pycodedj
3
+ Version: 0.1.0
4
+ Requires-Python: >=3.10
5
+ Requires-Dist: python-osc>=1.8
6
+ Provides-Extra: dev
7
+ Requires-Dist: mypy; extra == 'dev'
8
+ Requires-Dist: pytest; extra == 'dev'
9
+ Requires-Dist: ruff; extra == 'dev'
10
+ Provides-Extra: watch
11
+ Requires-Dist: watchdog>=3.0; extra == 'watch'
@@ -0,0 +1,211 @@
1
+ # PyCodeDJ
2
+
3
+ Pythonコードの構造をリアルタイムに音楽へ変換するライブコーディング環境。ファイルを保存するたびに演奏が変わる。
4
+
5
+ ---
6
+
7
+ ## コンセプト
8
+
9
+ PyCodeDJ は「コードを書くこと」と「音を鳴らすこと」を直結させます。
10
+
11
+ 関数を増やせばポリフォニーが広がり、ネストを深くすればフィルターが開き、コメントを書き込めば空間が広がる。コードの構造そのものが楽器です。
12
+
13
+ 既存の Python ↔ SuperCollider ブリッジ(sc3nb・supriya)との違いは2点です。
14
+
15
+ - **ホットリロード演奏** — ループを止めずにファイルを差し替える。保存が即座に音の変化になる。
16
+ - **コード構造の可聴化** — AST解析で抽出した構造的特徴量(深さ・分岐数・関数数など)を音楽パラメーターへ自動変換する。
17
+
18
+ ---
19
+
20
+ ## アーキテクチャ
21
+
22
+ ```
23
+ [Python エンジン] →OSC→ [SuperCollider] →音響出力→ スピーカー
24
+ ↓ OSC
25
+ [Hydra 等] →映像出力→ スクリーン
26
+ ```
27
+
28
+ | 層 | 役割 | 技術 |
29
+ | :--- | :--- | :--- |
30
+ | 制御層 | コード解析・スケジューリング・OSC送出 | Python 3.10+, python-osc, watchdog |
31
+ | 音響層 | リアルタイム音響合成 | SuperCollider (scsynth) |
32
+ | 視覚層 | 音楽データに同期した映像生成 | Hydra または Pyxel |
33
+
34
+ BPMクロックは SuperCollider 側の `TempoClock` が保持します。Python は「次のループで使う設定の更新」をOSCで送るだけに徹し、タイミング精度は SuperCollider に委ねます。
35
+
36
+ ---
37
+
38
+ ## コード構造 → 音楽パラメーターのマッピング
39
+
40
+ | コード特徴量 | 音楽パラメーター | 音楽的根拠 |
41
+ | :--- | :--- | :--- |
42
+ | ネストの深さ(最大) | フィルター Cutoff (200–4000 Hz) | 深い構造=複雑さ=音色の明るさ |
43
+ | 制御フロー数(if/for/while) | LFO レート (0.1–5.0 Hz) | 分岐の多さ=揺らぎの速さ |
44
+ | 関数定義数 | ポリフォニー声部数 (1–4) | 関数=独立した声部 |
45
+ | コメント率 | リバーブ Depth (0.0–0.8) | 余白の多さ=空間の広さ |
46
+
47
+ テンポ(BPM)と基音(Pitch)は演奏者が明示的に制御します。保存のたびに楽曲全体の土台が変わることを防ぐためです。
48
+
49
+ ---
50
+
51
+ ## インストール
52
+
53
+ **必要なもの**
54
+
55
+ - Python 3.10 以上
56
+ - SuperCollider(scsynth が起動できる環境)
57
+
58
+ ```bash
59
+ pip install 'pycodedj[watch]'
60
+ ```
61
+
62
+ `[watch]` を付けると `pycodedj watch` コマンドも使えるようになります。
63
+
64
+ 開発用:
65
+
66
+ ```bash
67
+ git clone https://github.com/yourname/pycodedj
68
+ cd pycodedj
69
+ pip install -e ".[dev]"
70
+ ```
71
+
72
+ ---
73
+
74
+ ## クイックスタート
75
+
76
+ **1. SuperCollider を起動し、シンセを読み込む**
77
+
78
+ SuperCollider IDE で `sc/synths.scd` を開いて実行します。
79
+
80
+ **2. ライブコーディングファイルを用意する**
81
+
82
+ ```python
83
+ # @loop bass interval=2.0
84
+ def bass():
85
+ for i in range(8):
86
+ if i % 2 == 0:
87
+ pass
88
+
89
+ # @loop melody interval=0.5
90
+ def melody():
91
+ x = 1
92
+ y = 2
93
+ return x + y
94
+
95
+ # @loop pad interval=4.0
96
+ def pad():
97
+ # 空間を作る
98
+ # もう少し余白
99
+ pass
100
+ ```
101
+
102
+ **3. ブロックを評価する**
103
+
104
+ ```bash
105
+ pycodedj eval demo.py::bass
106
+ ```
107
+
108
+ 成功すると次のフィードバックが表示されます。
109
+
110
+ ```
111
+ [pycodedj] bass cutoff=418Hz lfo=1.08Hz reverb=0.00 voices=1
112
+ ```
113
+
114
+ 他のループはそのまま鳴り続けます。
115
+
116
+ **4. watch モードでライブコーディング**
117
+
118
+ 毎回 eval を打つ代わりに、ファイル保存で全ループを自動再評価できます。
119
+
120
+ ```bash
121
+ pycodedj watch demo.py
122
+ ```
123
+
124
+ あとはエディタでコードを書いて保存するだけです。
125
+
126
+ > **`interval` について(現在の MVP):** `interval=2.0` のような値はパーサーが読み取りますが、現時点では OSC では送信されません。ループの繰り返し周期は SuperCollider 側の `TempoClock` で管理します。将来的に SC 側に interval を渡す仕組みを追加する予定です。
127
+
128
+ ---
129
+
130
+ ## サンプルファイル
131
+
132
+ | ファイル | 内容 |
133
+ | :--- | :--- |
134
+ | `examples/demo.py` | bass / melody / pad の 3 ループ入門デモ |
135
+ | `examples/club_set.py` | sub_bass / hat_engine / neon_stab / acid_lead / warehouse_air のクラブスタイルデモ |
136
+
137
+ ---
138
+
139
+ ## ライブコーディング例
140
+
141
+ ### ネストを深くするとフィルターが開く
142
+
143
+ ```python
144
+ # @loop bass interval=2.0
145
+ def bass():
146
+ for i in range(4): # 制御フロー +1
147
+ for j in range(4): # ネスト深さ +1、制御フロー +1
148
+ if i == j: # ネスト深さ +1、制御フロー +1
149
+ pass
150
+ ```
151
+
152
+ ### 関数を増やすとポリフォニーが広がる
153
+
154
+ ```python
155
+ # @loop chord interval=1.0
156
+ def voice_a(): pass
157
+ def voice_b(): pass
158
+ def voice_c(): pass
159
+ def voice_d(): pass
160
+ ```
161
+
162
+ ### コメントを増やすと空間(リバーブ)が広がる
163
+
164
+ ```python
165
+ # @loop pad interval=4.0
166
+ # ここに余白を置く
167
+ # もう少し置く
168
+ # 静寂も音楽
169
+ def pad(): pass
170
+ ```
171
+
172
+ ---
173
+
174
+ ## OSC アドレス仕様
175
+
176
+ SuperCollider との通信に使うアドレスです。
177
+
178
+ | アドレス | 型 | 値域 | 対応パラメーター |
179
+ | :--- | :--- | :--- | :--- |
180
+ | `/pycodedj/loop/<name>/cutoff` | float | 200–4000 Hz | フィルター Cutoff |
181
+ | `/pycodedj/loop/<name>/lfo_rate` | float | 0.1–5.0 Hz | LFO レート |
182
+ | `/pycodedj/loop/<name>/reverb` | float | 0.0–0.8 | リバーブ Depth |
183
+ | `/pycodedj/loop/<name>/voice_count` | int | 1–4 | ポリフォニー声部数 |
184
+
185
+ `<name>` はブロック名(`bass`、`melody` など)です。ループごとに独立したアドレスを持つため、複数ループが同じパラメーターを上書きしません。
186
+
187
+ Hydra 等の外部ビジュアライザーへは同じパラメーターを別ポートに送信します。
188
+
189
+ ---
190
+
191
+ ## 動作環境
192
+
193
+ - **推奨OS:** macOS(Core Audio の低遅延性を活用)または Linux(Raspberry Pi 5 等)
194
+ - **Python:** 3.10 以上
195
+ - **SuperCollider:** 3.12 以上
196
+
197
+ ---
198
+
199
+ ## ロードマップ
200
+
201
+ - [x] 仕様設計・マッピング設計
202
+ - [ ] フェーズ0: マッピング仮説の聴取検証
203
+ - [x] フェーズ1: Python → SuperCollider OSC プロトタイプ
204
+ - [x] フェーズ2: ホットリロード・ライブループ実装(`pycodedj watch`)
205
+ - [ ] フェーズ3: Hydra ビジュアライザー統合
206
+
207
+ ---
208
+
209
+ ## ライセンス
210
+
211
+ MIT
@@ -0,0 +1,211 @@
1
+ # PyCodeDJ
2
+
3
+ A live-coding environment that translates Python code structure into music in real time. Every save changes the performance.
4
+
5
+ ---
6
+
7
+ ## Concept
8
+
9
+ PyCodeDJ connects "writing code" directly to "making sound."
10
+
11
+ 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.
12
+
13
+ Two things set it apart from existing Python ↔ SuperCollider bridges (sc3nb, supriya):
14
+
15
+ - **Hot-reload performance** — swap out a loop without stopping it. Saving a file becomes an immediate sound change.
16
+ - **Audible code structure** — structural features extracted via AST analysis (depth, branch count, function count, etc.) are automatically mapped to musical parameters.
17
+
18
+ ---
19
+
20
+ ## Architecture
21
+
22
+ ```
23
+ [Python engine] →OSC→ [SuperCollider] →audio out→ speakers
24
+ ↓ OSC
25
+ [Hydra etc.] →video out→ screen
26
+ ```
27
+
28
+ | Layer | Role | Technology |
29
+ | :--- | :--- | :--- |
30
+ | Control | Code analysis, scheduling, OSC dispatch | Python 3.10+, python-osc, watchdog |
31
+ | Audio | Real-time sound synthesis | SuperCollider (scsynth) |
32
+ | Visual | Music-synced visuals | Hydra or Pyxel |
33
+
34
+ BPM clock is held by SuperCollider's `TempoClock`. Python only sends parameter updates over OSC; timing accuracy is delegated to SuperCollider.
35
+
36
+ ---
37
+
38
+ ## Code Structure → Music Parameter Mapping
39
+
40
+ | Code feature | Music parameter | Musical rationale |
41
+ | :--- | :--- | :--- |
42
+ | Max nesting depth | Filter Cutoff (200–4000 Hz) | Deep structure = complexity = brightness |
43
+ | Control-flow count (if/for/while) | LFO rate (0.1–5.0 Hz) | More branches = faster modulation |
44
+ | Function definition count | Polyphony voice count (1–4) | Functions = independent voices |
45
+ | Comment ratio | Reverb depth (0.0–0.8) | More whitespace = more space |
46
+
47
+ Tempo (BPM) and root pitch are controlled explicitly by the performer, to prevent the foundation of the piece from shifting on every save.
48
+
49
+ ---
50
+
51
+ ## Installation
52
+
53
+ **Requirements**
54
+
55
+ - Python 3.10 or later
56
+ - SuperCollider (with scsynth available)
57
+
58
+ ```bash
59
+ pip install 'pycodedj[watch]'
60
+ ```
61
+
62
+ The `[watch]` extra enables the `pycodedj watch` command.
63
+
64
+ Development install:
65
+
66
+ ```bash
67
+ git clone https://github.com/yourname/pycodedj
68
+ cd pycodedj
69
+ pip install -e ".[dev]"
70
+ ```
71
+
72
+ ---
73
+
74
+ ## Quick Start
75
+
76
+ **1. Boot SuperCollider and load the synths**
77
+
78
+ Open `sc/synths.scd` in the SuperCollider IDE and evaluate it.
79
+
80
+ **2. Write a live-coding file**
81
+
82
+ ```python
83
+ # @loop bass interval=2.0
84
+ def bass():
85
+ for i in range(8):
86
+ if i % 2 == 0:
87
+ pass
88
+
89
+ # @loop melody interval=0.5
90
+ def melody():
91
+ x = 1
92
+ y = 2
93
+ return x + y
94
+
95
+ # @loop pad interval=4.0
96
+ def pad():
97
+ # make space
98
+ # a little more
99
+ pass
100
+ ```
101
+
102
+ **3. Evaluate a block**
103
+
104
+ ```bash
105
+ pycodedj eval demo.py::bass
106
+ ```
107
+
108
+ On success, feedback is printed immediately:
109
+
110
+ ```
111
+ [pycodedj] bass cutoff=418Hz lfo=1.08Hz reverb=0.00 voices=1
112
+ ```
113
+
114
+ Other loops keep playing without interruption.
115
+
116
+ **4. Live-code with watch mode**
117
+
118
+ Instead of running eval manually, use watch to re-evaluate all loops on every save:
119
+
120
+ ```bash
121
+ pycodedj watch demo.py
122
+ ```
123
+
124
+ From here, just write code and save.
125
+
126
+ > **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.
127
+
128
+ ---
129
+
130
+ ## Example Files
131
+
132
+ | File | Contents |
133
+ | :--- | :--- |
134
+ | `examples/demo.py` | Intro demo with bass / melody / pad |
135
+ | `examples/club_set.py` | Club-style demo: sub_bass / hat_engine / neon_stab / acid_lead / warehouse_air |
136
+
137
+ ---
138
+
139
+ ## Live-Coding Examples
140
+
141
+ ### Deeper nesting opens the filter
142
+
143
+ ```python
144
+ # @loop bass interval=2.0
145
+ def bass():
146
+ for i in range(4): # control flow +1
147
+ for j in range(4): # depth +1, control flow +1
148
+ if i == j: # depth +1, control flow +1
149
+ pass
150
+ ```
151
+
152
+ ### More functions = more polyphony
153
+
154
+ ```python
155
+ # @loop chord interval=1.0
156
+ def voice_a(): pass
157
+ def voice_b(): pass
158
+ def voice_c(): pass
159
+ def voice_d(): pass
160
+ ```
161
+
162
+ ### More comments = more space (reverb)
163
+
164
+ ```python
165
+ # @loop pad interval=4.0
166
+ # leave space here
167
+ # a little more
168
+ # silence is music
169
+ def pad(): pass
170
+ ```
171
+
172
+ ---
173
+
174
+ ## OSC Address Reference
175
+
176
+ Addresses used to communicate with SuperCollider.
177
+
178
+ | Address | Type | Range | Parameter |
179
+ | :--- | :--- | :--- | :--- |
180
+ | `/pycodedj/loop/<name>/cutoff` | float | 200–4000 Hz | Filter Cutoff |
181
+ | `/pycodedj/loop/<name>/lfo_rate` | float | 0.1–5.0 Hz | LFO rate |
182
+ | `/pycodedj/loop/<name>/reverb` | float | 0.0–0.8 | Reverb depth |
183
+ | `/pycodedj/loop/<name>/voice_count` | int | 1–4 | Polyphony voice count |
184
+
185
+ `<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.
186
+
187
+ External visualisers such as Hydra can receive the same parameters on a separate port.
188
+
189
+ ---
190
+
191
+ ## Requirements
192
+
193
+ - **Recommended OS:** macOS (low-latency Core Audio) or Linux (Raspberry Pi 5, etc.)
194
+ - **Python:** 3.10 or later
195
+ - **SuperCollider:** 3.12 or later
196
+
197
+ ---
198
+
199
+ ## Roadmap
200
+
201
+ - [x] Spec design and mapping design
202
+ - [ ] Phase 0: Listening validation of mapping hypotheses
203
+ - [x] Phase 1: Python → SuperCollider OSC prototype
204
+ - [x] Phase 2: Hot-reload live loop implementation (`pycodedj watch`)
205
+ - [ ] Phase 3: Hydra visualiser integration
206
+
207
+ ---
208
+
209
+ ## License
210
+
211
+ MIT