pycodedj 0.5.0__tar.gz → 0.5.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.
Files changed (37) hide show
  1. {pycodedj-0.5.0 → pycodedj-0.5.1}/CHANGELOG.md +13 -0
  2. {pycodedj-0.5.0 → pycodedj-0.5.1}/PKG-INFO +2 -2
  3. {pycodedj-0.5.0 → pycodedj-0.5.1}/README.ja.md +1 -1
  4. {pycodedj-0.5.0 → pycodedj-0.5.1}/README.md +1 -1
  5. {pycodedj-0.5.0 → pycodedj-0.5.1}/docs/manual.html +59 -14
  6. {pycodedj-0.5.0 → pycodedj-0.5.1}/docs/manual.ja.md +47 -10
  7. {pycodedj-0.5.0 → pycodedj-0.5.1}/docs/manual.md +47 -10
  8. pycodedj-0.5.1/examples/club_set.py +107 -0
  9. {pycodedj-0.5.0 → pycodedj-0.5.1}/pyproject.toml +1 -1
  10. {pycodedj-0.5.0 → pycodedj-0.5.1}/src/pycodedj/__init__.py +1 -1
  11. {pycodedj-0.5.0 → pycodedj-0.5.1}/src/pycodedj/__main__.py +2 -0
  12. pycodedj-0.5.0/examples/club_set.py +0 -150
  13. {pycodedj-0.5.0 → pycodedj-0.5.1}/.github/workflows/workflow.yml +0 -0
  14. {pycodedj-0.5.0 → pycodedj-0.5.1}/.gitignore +0 -0
  15. {pycodedj-0.5.0 → pycodedj-0.5.1}/LICENSE +0 -0
  16. {pycodedj-0.5.0 → pycodedj-0.5.1}/docs/CNAME +0 -0
  17. {pycodedj-0.5.0 → pycodedj-0.5.1}/docs/index.html +0 -0
  18. {pycodedj-0.5.0 → pycodedj-0.5.1}/examples/demo.py +0 -0
  19. {pycodedj-0.5.0 → pycodedj-0.5.1}/examples/hello_sc.py +0 -0
  20. {pycodedj-0.5.0 → pycodedj-0.5.1}/examples/sound_showcase.py +0 -0
  21. {pycodedj-0.5.0 → pycodedj-0.5.1}/sc/synths.scd +0 -0
  22. {pycodedj-0.5.0 → pycodedj-0.5.1}/src/pycodedj/_loop.py +0 -0
  23. {pycodedj-0.5.0 → pycodedj-0.5.1}/src/pycodedj/analyzer.py +0 -0
  24. {pycodedj-0.5.0 → pycodedj-0.5.1}/src/pycodedj/block_parser.py +0 -0
  25. {pycodedj-0.5.0 → pycodedj-0.5.1}/src/pycodedj/engine.py +0 -0
  26. {pycodedj-0.5.0 → pycodedj-0.5.1}/src/pycodedj/mapper.py +0 -0
  27. {pycodedj-0.5.0 → pycodedj-0.5.1}/src/pycodedj/osc_bridge.py +0 -0
  28. {pycodedj-0.5.0 → pycodedj-0.5.1}/src/pycodedj/pattern.py +0 -0
  29. {pycodedj-0.5.0 → pycodedj-0.5.1}/src/pycodedj/watcher.py +0 -0
  30. {pycodedj-0.5.0 → pycodedj-0.5.1}/tests/__init__.py +0 -0
  31. {pycodedj-0.5.0 → pycodedj-0.5.1}/tests/test_analyzer.py +0 -0
  32. {pycodedj-0.5.0 → pycodedj-0.5.1}/tests/test_block_parser.py +0 -0
  33. {pycodedj-0.5.0 → pycodedj-0.5.1}/tests/test_engine.py +0 -0
  34. {pycodedj-0.5.0 → pycodedj-0.5.1}/tests/test_mapper.py +0 -0
  35. {pycodedj-0.5.0 → pycodedj-0.5.1}/tests/test_osc_bridge.py +0 -0
  36. {pycodedj-0.5.0 → pycodedj-0.5.1}/tests/test_pattern.py +0 -0
  37. {pycodedj-0.5.0 → pycodedj-0.5.1}/tests/test_watcher.py +0 -0
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.1] - 2026-05-08
4
+
5
+ ### Improvements
6
+
7
+ - `examples/club_set.py` を `pattern()` / `synth=` ベースの重低音クラブセットに刷新。4つ打ちキック、ランブル、サブ、アシッド、ハット、フィル、空間ノイズを含む 11 ループ構成に変更
8
+ - `pycodedj --version` を追加し、公開前チェックでパッケージバージョンを確認できるようにした
9
+
10
+ ### Docs
11
+
12
+ - `docs/manual.ja.md` / `docs/manual.md` / `docs/manual.html` — ループ名と音色名の違いを明確化し、音色は `synth=` で指定することを追記
13
+ - `docs/manual.ja.md` / `docs/manual.md` / `docs/manual.html` — `examples/club_set.py` の重低音クラブセットの使い方と主要レイヤー一覧を追加
14
+ - `README.ja.md` / `README.md` — `examples/club_set.py` の説明を現在の 11 ループ構成に更新
15
+
3
16
  ## [0.5.0] - 2026-05-08
4
17
 
5
18
  ### Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycodedj
3
- Version: 0.5.0
3
+ Version: 0.5.1
4
4
  License: MIT License with Commons Clause
5
5
 
6
6
  "Commons Clause" License Condition v1.0
@@ -247,7 +247,7 @@ Token reference:
247
247
  | File | Contents |
248
248
  | :--- | :--- |
249
249
  | `examples/demo.py` | Intro demo: bass / melody / pad |
250
- | `examples/club_set.py` | EDM groove: 8 loops (kick, bass, hat, chords, pad) |
250
+ | `examples/club_set.py` | Sub-heavy club set: 11 loops with kick, rumble, sub, acid, hats, and room noise |
251
251
  | `examples/sound_showcase.py` | All 30 synths — evaluate one at a time to audition |
252
252
 
253
253
  ---
@@ -185,7 +185,7 @@ def chord():
185
185
  | ファイル | 内容 |
186
186
  | :--- | :--- |
187
187
  | `examples/demo.py` | bass / melody / pad の 3 ループ入門デモ |
188
- | `examples/club_set.py` | EDM クラブグルーヴ(キック・ベース・ハット・コード・パッドを含む 8 ループ) |
188
+ | `examples/club_set.py` | 重低音クラブセット(キック、ランブル、サブ、アシッド、ハット、空間ノイズを含む 11 ループ) |
189
189
  | `examples/sound_showcase.py` | 全 30 音色 — 1 音ずつ eval して確認できる |
190
190
 
191
191
  ---
@@ -185,7 +185,7 @@ Token reference:
185
185
  | File | Contents |
186
186
  | :--- | :--- |
187
187
  | `examples/demo.py` | Intro demo: bass / melody / pad |
188
- | `examples/club_set.py` | EDM groove: 8 loops (kick, bass, hat, chords, pad) |
188
+ | `examples/club_set.py` | Sub-heavy club set: 11 loops with kick, rumble, sub, acid, hats, and room noise |
189
189
  | `examples/sound_showcase.py` | All 30 synths — evaluate one at a time to audition |
190
190
 
191
191
  ---
@@ -808,6 +808,8 @@ def kick_drum():
808
808
 
809
809
  <h3>@loop のパラメーター(pattern 用)</h3>
810
810
 
811
+ <p><strong>音色は <code>synth=</code> で指定します。</strong> <code>@loop("bass", synth="floor_kick")</code> と書くと、ループ名は <code>bass</code> のまま、鳴る音色だけが <code>floor_kick</code> になります。</p>
812
+
811
813
  <table>
812
814
  <thead><tr><th>引数</th><th>説明</th><th>例</th></tr></thead>
813
815
  <tbody>
@@ -818,6 +820,10 @@ def kick_drum():
818
820
  </tbody>
819
821
  </table>
820
822
 
823
+ <blockquote>
824
+ <p><code>@loop("kick", synth="floor_kick")</code> の <code>"kick"</code> はループ名、<code>"floor_kick"</code> は音色名です。<code>pycodedj eval demo.py::kick</code> や <code>pycodedj mute kick</code> ではループ名を使います。</p>
825
+ </blockquote>
826
+
821
827
  <h3>トリガーパターン — x と . だけで</h3>
822
828
 
823
829
  <p>音程なしで「リズムだけ」を指定したいときは <code>x</code> と <code>.</code> だけ使います。</p>
@@ -965,9 +971,29 @@ def atmosphere(volume=0.08):
965
971
 
966
972
  <pre data-lang="bash"><code id="code-s8-2">pycodedj watch myfile.py</code></pre>
967
973
 
974
+ <h3>重低音クラブセットを試す</h3>
975
+
976
+ <p><code>examples/club_set.py</code> は、クラブのフロアで鳴る低域を意識したサンプルです。4つ打ちキック、2・4拍のクラップ、オフビートハット、16分ハット、ランブル、サブベース、アシッドベース、スタブ、フィル、空間ノイズを重ねています。</p>
977
+
978
+ <pre data-lang="bash"><code id="code-s8-3">pycodedj watch examples/club_set.py</code></pre>
979
+
980
+ <p>主なループ名は次の通りです。<code>mute</code> / <code>unmute</code> で足し引きすると、クラブトラックのレイヤー構造がわかりやすくなります。</p>
981
+
982
+ <table>
983
+ <thead><tr><th>ループ名</th><th>役割</th><th>音色名</th></tr></thead>
984
+ <tbody>
985
+ <tr><td><code>kick</code></td><td>4つ打ちの土台</td><td><code>floor_kick</code></td></tr>
986
+ <tr><td><code>rumble</code></td><td>床鳴りする低域</td><td><code>bass_rumble</code></td></tr>
987
+ <tr><td><code>sub</code> / <code>floor</code></td><td>持続する重低音</td><td><code>sub_bass</code></td></tr>
988
+ <tr><td><code>acid</code></td><td>動くベースライン</td><td><code>bass_acid</code></td></tr>
989
+ <tr><td><code>offhat</code> / <code>hats</code></td><td>グルーヴの推進力</td><td><code>hat_ride</code>, <code>hat_engine</code></td></tr>
990
+ <tr><td><code>room</code></td><td>倉庫のような空間感</td><td><code>warehouse_air</code></td></tr>
991
+ </tbody>
992
+ </table>
993
+
968
994
  <h3>演奏中のコントロール</h3>
969
995
 
970
- <pre data-lang="bash"><code id="code-s8-3">pycodedj mute bass # bass を消音(停止はしない)
996
+ <pre data-lang="bash"><code id="code-s8-4">pycodedj mute bass # bass を消音(停止はしない)
971
997
  pycodedj unmute bass # bass の音量を元に戻す
972
998
  pycodedj panic # すべてのループを即時停止
973
999
  pycodedj status # ループの状態を確認</code></pre>
@@ -980,7 +1006,7 @@ pycodedj status # ループの状態を確認</code></pre>
980
1006
  <thead><tr><th>ファイル</th><th>内容</th></tr></thead>
981
1007
  <tbody>
982
1008
  <tr><td><code>examples/demo.py</code></td><td>bass / melody / pad の 3 ループ入門デモ</td></tr>
983
- <tr><td><code>examples/club_set.py</code></td><td>EDM クラブグルーヴ(キック・ベース・ハット・コード・パッドを含む 8 ループ)</td></tr>
1009
+ <tr><td><code>examples/club_set.py</code></td><td>重低音クラブセット(キック、ランブル、サブ、アシッド、ハット、空間ノイズを含む 11 ループ)</td></tr>
984
1010
  <tr><td><code>examples/sound_showcase.py</code></td><td>全 30 音色 — 1 音ずつ eval して確認できる</td></tr>
985
1011
  </tbody>
986
1012
  </table>
@@ -992,16 +1018,35 @@ pycodedj status # ループの状態を確認</code></pre>
992
1018
  <section id="s9">
993
1019
  <h2>9. 音色リファレンス</h2>
994
1020
 
995
- <p><code>@loop</code> の第一引数(ループ名)を変えると、SuperCollider 側で使う音色を選べます。全 30 音色を 1 音ずつ確認したい場合は <code>examples/sound_showcase.py</code> を使います。</p>
1021
+ <p>PyCodeDJ では <strong>ループ名</strong> <strong>音色名</strong> を分けて考えます。</p>
1022
+
1023
+ <ul>
1024
+ <li>ループ名: <code>@loop("kick", ...)</code> の第一引数。<code>pycodedj eval demo.py::kick</code>、<code>mute</code>、<code>solo</code> などで指定する名前</li>
1025
+ <li>音色名: SuperCollider 側のシンセ名。pattern ループでは <code>synth="floor_kick"</code> のように指定する名前</li>
1026
+ </ul>
1027
+
1028
+ <p>音色を選ぶときは、下の表から音色名を選んで <code>synth=</code> に入れます。たとえば <code>@loop("bass", synth="floor_kick")</code> は、<code>bass</code> というループ名で <code>floor_kick</code> の音色を鳴らします。</p>
1029
+
1030
+ <p><code>pattern()</code> を使う場合は、<code>@loop("kick", synth="floor_kick")</code> のように、短いループ名と具体的な音色名を分けて書けます。</p>
996
1031
 
997
- <pre data-lang="bash"><code id="code-s9-1">pycodedj eval examples/sound_showcase.py::bass_acid
998
- pycodedj eval examples/sound_showcase.py::riser_noise
999
- pycodedj eval examples/sound_showcase.py::bell_rave</code></pre>
1032
+ <p><code>pattern()</code> を使わない構造マッピングのループでは、互換性のためにループ名と同じ名前の音色があればそれが使われます。たとえば <code>@loop("bass_acid", interval=0.5)</code> <code>bass_acid</code> 音色で鳴ります。</p>
1033
+
1034
+ <p>全 30 音色を 1 音ずつ確認したい場合は <code>examples/sound_showcase.py</code> を使います。</p>
1035
+
1036
+ <pre data-lang="bash"><code id="code-s9-1">pycodedj eval examples/sound_showcase.py::floor_kick
1037
+ pycodedj eval examples/sound_showcase.py::bass_acid
1038
+ pycodedj eval examples/sound_showcase.py::acid_lead</code></pre>
1039
+
1040
+ <p><code>examples/sound_showcase.py</code> では試聴しやすいように、ループ名を音色名と同じにしています。通常の pattern ループでは、次のように <code>synth=</code> にこの表の音色名を指定します。</p>
1041
+
1042
+ <pre data-lang="python"><code id="code-s9-2">@loop("kick", synth="floor_kick", dur=0.25)
1043
+ def kick():
1044
+ pattern("x . x .")</code></pre>
1000
1045
 
1001
1046
  <h3>キック</h3>
1002
1047
  <table>
1003
1048
  <thead>
1004
- <tr><th>ループ名</th><th>音</th></tr>
1049
+ <tr><th>音色名</th><th>音</th></tr>
1005
1050
  </thead>
1006
1051
  <tbody>
1007
1052
  <tr><td><code>kick_hard</code></td><td>硬めでアタックの強いキック</td></tr>
@@ -1013,7 +1058,7 @@ pycodedj eval examples/sound_showcase.py::bell_rave</code></pre>
1013
1058
  <h3>ベース</h3>
1014
1059
  <table>
1015
1060
  <thead>
1016
- <tr><th>ループ名</th><th>音</th></tr>
1061
+ <tr><th>音色名</th><th>音</th></tr>
1017
1062
  </thead>
1018
1063
  <tbody>
1019
1064
  <tr><td><code>bass_rumble</code></td><td>キック由来の低いランブル</td></tr>
@@ -1026,7 +1071,7 @@ pycodedj eval examples/sound_showcase.py::bell_rave</code></pre>
1026
1071
  <h3>パーカッション</h3>
1027
1072
  <table>
1028
1073
  <thead>
1029
- <tr><th>ループ名</th><th>音</th></tr>
1074
+ <tr><th>音色名</th><th>音</th></tr>
1030
1075
  </thead>
1031
1076
  <tbody>
1032
1077
  <tr><td><code>hat_engine</code></td><td>クローズ/オープンのハットグリッド</td></tr>
@@ -1042,7 +1087,7 @@ pycodedj eval examples/sound_showcase.py::bell_rave</code></pre>
1042
1087
  <h3>コード・スタブ</h3>
1043
1088
  <table>
1044
1089
  <thead>
1045
- <tr><th>ループ名</th><th>音</th></tr>
1090
+ <tr><th>音色名</th><th>音</th></tr>
1046
1091
  </thead>
1047
1092
  <tbody>
1048
1093
  <tr><td><code>chord_rave</code></td><td>明るいレイブスタブ</td></tr>
@@ -1057,7 +1102,7 @@ pycodedj eval examples/sound_showcase.py::bell_rave</code></pre>
1057
1102
  <h3>リード・メロディー</h3>
1058
1103
  <table>
1059
1104
  <thead>
1060
- <tr><th>ループ名</th><th>音</th></tr>
1105
+ <tr><th>音色名</th><th>音</th></tr>
1061
1106
  </thead>
1062
1107
  <tbody>
1063
1108
  <tr><td><code>acid_lead</code></td><td>アシッド系リード</td></tr>
@@ -1071,7 +1116,7 @@ pycodedj eval examples/sound_showcase.py::bell_rave</code></pre>
1071
1116
  <h3>アトモスフィア</h3>
1072
1117
  <table>
1073
1118
  <thead>
1074
- <tr><th>ループ名</th><th>音</th></tr>
1119
+ <tr><th>音色名</th><th>音</th></tr>
1075
1120
  </thead>
1076
1121
  <tbody>
1077
1122
  <tr><td><code>shimmer_pad</code></td><td>深いシマーパッド</td></tr>
@@ -1083,7 +1128,7 @@ pycodedj eval examples/sound_showcase.py::bell_rave</code></pre>
1083
1128
  <h3>FX</h3>
1084
1129
  <table>
1085
1130
  <thead>
1086
- <tr><th>ループ名</th><th>音</th></tr>
1131
+ <tr><th>音色名</th><th>音</th></tr>
1087
1132
  </thead>
1088
1133
  <tbody>
1089
1134
  <tr><td><code>fx_impact</code></td><td>低いインパクト</td></tr>
@@ -1377,7 +1422,7 @@ pycodedj eval demo.py::pad --sc-port 57200</code></pre>
1377
1422
  <p><strong>使用例:</strong></p>
1378
1423
 
1379
1424
  <pre data-lang="bash"><code id="code-s12-2">pycodedj watch examples/demo.py
1380
- pycodedj watch club_set.py --debounce 0.5
1425
+ pycodedj watch examples/club_set.py --debounce 0.5
1381
1426
  pycodedj watch myfile.py --sc-host 192.168.1.10</code></pre>
1382
1427
 
1383
1428
  <h3><code>pycodedj panic</code></h3>
@@ -505,6 +505,8 @@ def kick_drum():
505
505
 
506
506
  `pattern()` を使うループでは、`@loop` デコレータに次の引数を追加します。
507
507
 
508
+ **音色は `synth=` で指定します。** `@loop("bass", synth="floor_kick")` と書くと、ループ名は `bass` のまま、鳴る音色だけが `floor_kick` になります。
509
+
508
510
  | 引数 | 説明 | 例 |
509
511
  | :--- | :--- | :--- |
510
512
  | `synth=` | 使うシンセ名 | `synth="floor_kick"` |
@@ -513,6 +515,7 @@ def kick_drum():
513
515
  | `dur=` | 1 ステップの長さ(秒) | `dur=0.25`(16 分音符相当) |
514
516
 
515
517
  > **ポイント:** `volume=` / `eq=` / `interval=` は pattern ループでも引き続き使えます。
518
+ > `@loop("kick", synth="floor_kick")` の `"kick"` はループ名、`"floor_kick"` は音色名です。`pycodedj eval demo.py::kick` や `pycodedj mute kick` ではループ名を使います。
516
519
 
517
520
  ### トリガーパターン — x と . だけで
518
521
 
@@ -693,6 +696,25 @@ watch で起動すれば、保存するたびに変更したループだけが
693
696
  pycodedj watch myfile.py
694
697
  ```
695
698
 
699
+ ### 重低音クラブセットを試す
700
+
701
+ `examples/club_set.py` は、クラブのフロアで鳴る低域を意識したサンプルです。4つ打ちキック、2・4拍のクラップ、オフビートハット、16分ハット、ランブル、サブベース、アシッドベース、スタブ、フィル、空間ノイズを重ねています。
702
+
703
+ ```bash
704
+ pycodedj watch examples/club_set.py
705
+ ```
706
+
707
+ 主なループ名は次の通りです。`mute` / `unmute` で足し引きすると、クラブトラックのレイヤー構造がわかりやすくなります。
708
+
709
+ | ループ名 | 役割 | 音色名 |
710
+ | :--- | :--- | :--- |
711
+ | `kick` | 4つ打ちの土台 | `floor_kick` |
712
+ | `rumble` | 床鳴りする低域 | `bass_rumble` |
713
+ | `sub` / `floor` | 持続する重低音 | `sub_bass` |
714
+ | `acid` | 動くベースライン | `bass_acid` |
715
+ | `offhat` / `hats` | グルーヴの推進力 | `hat_ride`, `hat_engine` |
716
+ | `room` | 倉庫のような空間感 | `warehouse_air` |
717
+
696
718
  ### 演奏中のコントロール
697
719
 
698
720
  **ミュートとアンミュート**
@@ -729,7 +751,16 @@ pad muted 0.08 200Hz
729
751
 
730
752
  ## 9. 音色リファレンス
731
753
 
732
- `@loop` の第一引数(ループ名)で、SuperCollider 側で使う音色が決まります。
754
+ PyCodeDJ では **ループ名** と **音色名** を分けて考えます。
755
+
756
+ - ループ名: `@loop("kick", ...)` の第一引数。`pycodedj eval demo.py::kick`、`mute`、`solo` などで指定する名前
757
+ - 音色名: SuperCollider 側のシンセ名。pattern ループでは `synth="floor_kick"` のように指定する名前
758
+
759
+ 音色を選ぶときは、下の表から音色名を選んで `synth=` に入れます。たとえば `@loop("bass", synth="floor_kick")` は、`bass` というループ名で `floor_kick` の音色を鳴らします。
760
+
761
+ `pattern()` を使う場合は、`@loop("kick", synth="floor_kick")` のように、短いループ名と具体的な音色名を分けて書けます。
762
+
763
+ `pattern()` を使わない構造マッピングのループでは、互換性のためにループ名と同じ名前の音色があればそれが使われます。たとえば `@loop("bass_acid", interval=0.5)` は `bass_acid` 音色で鳴ります。
733
764
 
734
765
  全 30 音色を 1 音ずつ確認したい場合は `examples/sound_showcase.py` を使います。
735
766
 
@@ -739,11 +770,17 @@ pycodedj eval examples/sound_showcase.py::bass_acid
739
770
  pycodedj eval examples/sound_showcase.py::acid_lead
740
771
  ```
741
772
 
742
- `synth=` パラメーター(pattern 使用時)でも同じ名前を指定できます。
773
+ `examples/sound_showcase.py` では試聴しやすいように、ループ名を音色名と同じにしています。通常の pattern ループでは、次のように `synth=` にこの表の音色名を指定します。
774
+
775
+ ```python
776
+ @loop("kick", synth="floor_kick", dur=0.25)
777
+ def kick():
778
+ pattern("x . x .")
779
+ ```
743
780
 
744
781
  ### キック
745
782
 
746
- | 名前 | 音の特徴 |
783
+ | 音色名 | 音の特徴 |
747
784
  | :--- | :--- |
748
785
  | `kick_hard` | 硬めでアタックの強いキック |
749
786
  | `floor_kick` | 太い四つ打ちキック |
@@ -751,7 +788,7 @@ pycodedj eval examples/sound_showcase.py::acid_lead
751
788
 
752
789
  ### ベース
753
790
 
754
- | 名前 | 音の特徴 |
791
+ | 音色名 | 音の特徴 |
755
792
  | :--- | :--- |
756
793
  | `bass_rumble` | 深い低音ランブル |
757
794
  | `bass_reese` | 揺れる Reese 系ベース |
@@ -760,7 +797,7 @@ pycodedj eval examples/sound_showcase.py::acid_lead
760
797
 
761
798
  ### パーカッション
762
799
 
763
- | 名前 | 音の特徴 |
800
+ | 音色名 | 音の特徴 |
764
801
  | :--- | :--- |
765
802
  | `hat_engine` | クローズ/オープンのハットグリッド |
766
803
  | `hat_ride` | 長めのライド/オープンハット |
@@ -772,7 +809,7 @@ pycodedj eval examples/sound_showcase.py::acid_lead
772
809
 
773
810
  ### コード・スタブ
774
811
 
775
- | 名前 | 音の特徴 |
812
+ | 音色名 | 音の特徴 |
776
813
  | :--- | :--- |
777
814
  | `chord_rave` | 明るいレイブスタブ |
778
815
  | `neon_stab` | ネオン系スタブコード |
@@ -783,7 +820,7 @@ pycodedj eval examples/sound_showcase.py::acid_lead
783
820
 
784
821
  ### リード・メロディー
785
822
 
786
- | 名前 | 音の特徴 |
823
+ | 音色名 | 音の特徴 |
787
824
  | :--- | :--- |
788
825
  | `acid_lead` | アシッド系リード |
789
826
  | `lead_hoover` | Hoover 風リード |
@@ -793,7 +830,7 @@ pycodedj eval examples/sound_showcase.py::acid_lead
793
830
 
794
831
  ### アトモスフィア
795
832
 
796
- | 名前 | 音の特徴 |
833
+ | 音色名 | 音の特徴 |
797
834
  | :--- | :--- |
798
835
  | `shimmer_pad` | 深いシマーパッド |
799
836
  | `warehouse_air` | 倉庫っぽい空気感 |
@@ -801,7 +838,7 @@ pycodedj eval examples/sound_showcase.py::acid_lead
801
838
 
802
839
  ### FX
803
840
 
804
- | 名前 | 音の特徴 |
841
+ | 音色名 | 音の特徴 |
805
842
  | :--- | :--- |
806
843
  | `fx_impact` | 低いインパクト |
807
844
  | `riser_noise` | ノイズライザー(8 秒でスイープ上昇) |
@@ -1089,7 +1126,7 @@ pycodedj watch FILE [--sc-host HOST] [--sc-port PORT] [--debounce SECS]
1089
1126
 
1090
1127
  ```bash
1091
1128
  pycodedj watch examples/demo.py
1092
- pycodedj watch club_set.py --debounce 0.5
1129
+ pycodedj watch examples/club_set.py --debounce 0.5
1093
1130
  ```
1094
1131
 
1095
1132
  ### `pycodedj panic`
@@ -484,6 +484,8 @@ Tokens are separated by spaces. `"x . x ."` is a 4-step pattern.
484
484
 
485
485
  ### @loop arguments for pattern mode
486
486
 
487
+ **Choose the sound with `synth=`.** `@loop("bass", synth="floor_kick")` keeps the loop name as `bass`, but plays the `floor_kick` synth.
488
+
487
489
  | Argument | Description | Example |
488
490
  | :--- | :--- | :--- |
489
491
  | `synth=` | Synth name | `synth="floor_kick"` |
@@ -492,6 +494,7 @@ Tokens are separated by spaces. `"x . x ."` is a 4-step pattern.
492
494
  | `dur=` | Step length in seconds | `dur=0.25` (sixteenth note) |
493
495
 
494
496
  > `volume=`, `eq=`, and `interval=` still work alongside pattern arguments.
497
+ > In `@loop("kick", synth="floor_kick")`, `"kick"` is the loop name and `"floor_kick"` is the synth name. Use the loop name for commands such as `pycodedj eval demo.py::kick` and `pycodedj mute kick`.
495
498
 
496
499
  ### Trigger patterns — x and . only
497
500
 
@@ -670,6 +673,25 @@ Start watching and each save updates only the changed loops:
670
673
  pycodedj watch myfile.py
671
674
  ```
672
675
 
676
+ ### Try the sub-heavy club set
677
+
678
+ `examples/club_set.py` is a low-end club-floor example. It layers a four-on-the-floor kick, clap on beats 2 and 4, offbeat hats, 16th-note hats, rumble, sub bass, acid bass, sparse stabs, fills, and room noise.
679
+
680
+ ```bash
681
+ pycodedj watch examples/club_set.py
682
+ ```
683
+
684
+ These are the main loop names. Use `mute` / `unmute` to bring layers in and out and hear how the groove is built.
685
+
686
+ | Loop name | Role | Synth name |
687
+ | :--- | :--- | :--- |
688
+ | `kick` | Four-on-the-floor foundation | `floor_kick` |
689
+ | `rumble` | Floor-shaking low end | `bass_rumble` |
690
+ | `sub` / `floor` | Sustained sub pressure | `sub_bass` |
691
+ | `acid` | Moving bassline | `bass_acid` |
692
+ | `offhat` / `hats` | Groove and forward motion | `hat_ride`, `hat_engine` |
693
+ | `room` | Warehouse-like space | `warehouse_air` |
694
+
673
695
  ### Live controls
674
696
 
675
697
  **Mute and unmute**
@@ -706,7 +728,16 @@ Delete the `@loop` block (decorator + function) and save. In watch mode, the loo
706
728
 
707
729
  ## 9. Sound Reference
708
730
 
709
- The first argument to `@loop` (the loop name) selects the synth on the SuperCollider side.
731
+ PyCodeDJ has two related names:
732
+
733
+ - Loop name: the first argument to `@loop("kick", ...)`. Use it with `pycodedj eval demo.py::kick`, `mute`, `solo`, and status commands.
734
+ - Synth name: the SuperCollider synth name. Pattern loops set it with `synth="floor_kick"`.
735
+
736
+ To choose a sound, pick a synth name from the tables below and put it in `synth=`. For example, `@loop("bass", synth="floor_kick")` creates a loop called `bass` that plays the `floor_kick` synth.
737
+
738
+ With `pattern()`, you can keep these names separate: `@loop("kick", synth="floor_kick")` means the loop is called `kick`, and it plays the `floor_kick` synth.
739
+
740
+ For non-pattern structure-mapping loops, PyCodeDJ keeps the older shorthand: if the loop name matches a synth name, that synth is used. For example, `@loop("bass_acid", interval=0.5)` plays the `bass_acid` synth.
710
741
 
711
742
  To audition all 30 synths one at a time, use `examples/sound_showcase.py`:
712
743
 
@@ -716,11 +747,17 @@ pycodedj eval examples/sound_showcase.py::bass_acid
716
747
  pycodedj eval examples/sound_showcase.py::acid_lead
717
748
  ```
718
749
 
719
- The same names work with the `synth=` argument in pattern mode.
750
+ In `examples/sound_showcase.py`, loop names intentionally match synth names so each sound is easy to audition. In normal pattern loops, put one of the synth names below in `synth=`:
751
+
752
+ ```python
753
+ @loop("kick", synth="floor_kick", dur=0.25)
754
+ def kick():
755
+ pattern("x . x .")
756
+ ```
720
757
 
721
758
  ### Kick
722
759
 
723
- | Name | Sound |
760
+ | Synth name | Sound |
724
761
  | :--- | :--- |
725
762
  | `kick_hard` | Hard, punchy kick |
726
763
  | `floor_kick` | Full four-on-the-floor kick |
@@ -728,7 +765,7 @@ The same names work with the `synth=` argument in pattern mode.
728
765
 
729
766
  ### Bass
730
767
 
731
- | Name | Sound |
768
+ | Synth name | Sound |
732
769
  | :--- | :--- |
733
770
  | `bass_rumble` | Deep low rumble |
734
771
  | `bass_reese` | Reese-style wobble bass |
@@ -737,7 +774,7 @@ The same names work with the `synth=` argument in pattern mode.
737
774
 
738
775
  ### Percussion
739
776
 
740
- | Name | Sound |
777
+ | Synth name | Sound |
741
778
  | :--- | :--- |
742
779
  | `hat_engine` | Closed/open hi-hat grid |
743
780
  | `hat_ride` | Long ride/open hat |
@@ -749,7 +786,7 @@ The same names work with the `synth=` argument in pattern mode.
749
786
 
750
787
  ### Chords / Stabs
751
788
 
752
- | Name | Sound |
789
+ | Synth name | Sound |
753
790
  | :--- | :--- |
754
791
  | `chord_rave` | Bright rave stab |
755
792
  | `neon_stab` | Neon-style stab chord |
@@ -760,7 +797,7 @@ The same names work with the `synth=` argument in pattern mode.
760
797
 
761
798
  ### Lead / Melody
762
799
 
763
- | Name | Sound |
800
+ | Synth name | Sound |
764
801
  | :--- | :--- |
765
802
  | `acid_lead` | Acid-style lead |
766
803
  | `lead_hoover` | Hoover-style lead |
@@ -770,7 +807,7 @@ The same names work with the `synth=` argument in pattern mode.
770
807
 
771
808
  ### Atmosphere
772
809
 
773
- | Name | Sound |
810
+ | Synth name | Sound |
774
811
  | :--- | :--- |
775
812
  | `shimmer_pad` | Deep shimmer pad |
776
813
  | `warehouse_air` | Industrial air texture |
@@ -778,7 +815,7 @@ The same names work with the `synth=` argument in pattern mode.
778
815
 
779
816
  ### FX
780
817
 
781
- | Name | Sound |
818
+ | Synth name | Sound |
782
819
  | :--- | :--- |
783
820
  | `fx_impact` | Low impact hit |
784
821
  | `riser_noise` | Noise riser (8-second upward sweep) |
@@ -1058,7 +1095,7 @@ pycodedj watch FILE [--sc-host HOST] [--sc-port PORT] [--debounce SECS]
1058
1095
 
1059
1096
  ```bash
1060
1097
  pycodedj watch examples/demo.py
1061
- pycodedj watch club_set.py --debounce 0.5
1098
+ pycodedj watch examples/club_set.py --debounce 0.5
1062
1099
  ```
1063
1100
 
1064
1101
  ### `pycodedj panic`
@@ -0,0 +1,107 @@
1
+ # PyCodeDJ club set — sub-heavy warehouse floor
2
+ #
3
+ # Load sc/synths.scd in SuperCollider, then run:
4
+ # pycodedj watch examples/club_set.py
5
+ #
6
+ # Club-focused design:
7
+ # - steady four-on-the-floor kick for dancers
8
+ # - clap on 2 and 4
9
+ # - offbeat hats plus 16th-note motion
10
+ # - bass notes locked to the kick with syncopated replies
11
+ # - sustained sub and rumble layers for floor pressure
12
+ # - sparse stabs and fills so the bass has room
13
+
14
+ from pycodedj import loop, pattern
15
+
16
+
17
+ # --- Drum engine: stable pulse first, variations second ---
18
+
19
+ @loop("kick", synth="floor_kick", dur=0.117)
20
+ def kick(volume=1.0, eq="edm", low=1.7, mid=0.78, high=0.82):
21
+ pattern(
22
+ "x . . . x . . . x . . . x . . . "
23
+ "x . . . x . . x x . . . x . . x"
24
+ )
25
+
26
+
27
+ @loop("clap", synth="clap_snare", dur=0.117)
28
+ def clap(volume=0.18, eq="pop", low=0.62, mid=0.85, high=0.98):
29
+ pattern(
30
+ ". . . . x . . . . . . . x . . . "
31
+ ". . . . x . . . . . . x x . x ."
32
+ )
33
+
34
+
35
+ @loop("offhat", synth="hat_ride", dur=0.117)
36
+ def offhat(volume=0.065, eq="edm", low=0.28, mid=0.78, high=1.0):
37
+ pattern(
38
+ ". . x . . . x . . . x . . . x . "
39
+ ". . x . . x x . . . x . . x x ."
40
+ )
41
+
42
+
43
+ @loop("hats", synth="hat_engine", dur=0.0585)
44
+ def hats(volume=0.075, eq="edm", low=0.3, mid=0.82, high=1.02):
45
+ pattern(
46
+ "x . x . x x x . x . x . x x x . "
47
+ "x x x . x . x x x . x x x . x ."
48
+ )
49
+
50
+
51
+ # --- Low end: kick-locked weight with room-shaking sustain ---
52
+
53
+ @loop("rumble", synth="bass_rumble", root="A1", scale="minor", dur=0.117)
54
+ def rumble(volume=0.42, eq="edm", low=1.75, mid=0.58, high=0.32):
55
+ pattern(
56
+ "0 ~ . . 0 ~ . . 0 ~ . . 0 ~ . . "
57
+ "0 ~ . . 3 ~ . . 0 ~ . . 5 3 0 ."
58
+ )
59
+
60
+
61
+ @loop("sub", synth="sub_bass", root="A1", scale="minor", dur=0.117)
62
+ def sub(volume=0.58, eq="edm", low=1.85, mid=0.58, high=0.25):
63
+ pattern(
64
+ "0 ~ . 0 0 ~ . 0 0 ~ 3 . 0 ~ . 0 "
65
+ "0 ~ 0 . 3 ~ . 0 0 ~ 5 . 3 ~ 0 ."
66
+ )
67
+
68
+
69
+ @loop("floor", synth="sub_bass", root="A1", scale="minor", dur=0.468)
70
+ def floor(volume=0.22, eq="edm", low=1.9, mid=0.45, high=0.2):
71
+ pattern("0 ~ 0 ~ 0 ~ 0 ~")
72
+
73
+
74
+ @loop("acid", synth="bass_acid", root="A1", scale="minor", dur=0.0585)
75
+ def acid(volume=0.15, eq="edm", low=1.15, mid=0.88, high=0.72):
76
+ pattern(
77
+ "0 . 0 . 3 . 0 0 0 . 5 . 3 . 0 . "
78
+ "0 . 0 3 . 5 . 3 0 . 7 . 5 . 3 . "
79
+ "0 3 0 . 5 . 7 . 0 . 5 3 0 . 10 . "
80
+ "0 . 0 . 3 . 0 0 5 . 3 . 0 . 0 ."
81
+ )
82
+
83
+
84
+ # --- Hooks and fills: sparse enough to leave room for the bass ---
85
+
86
+ @loop("stabs", synth="chord_rave", root="A2", scale="minor", dur=0.117)
87
+ def stabs(volume=0.095, eq="edm", low=0.55, mid=0.72, high=0.78):
88
+ pattern(
89
+ ". . . . [0 2 4] . . . . . . . [3 5 7] . . . "
90
+ ". . [5 7 9] . . . . . [0 2 4] . . . [3 5 7] . . ."
91
+ )
92
+
93
+
94
+ @loop("fill", synth="tom_drum", root="A1", scale="minor", dur=0.0585)
95
+ def fill(volume=0.09, eq="edm", low=1.15, mid=0.72, high=0.5):
96
+ pattern(
97
+ ". . . . . . . . . . . . . . . . "
98
+ ". . . . . . . . . . 0 . 3 5 7 10"
99
+ )
100
+
101
+
102
+ @loop("room", synth="warehouse_air", root="A1", scale="minor", dur=0.468)
103
+ def room(volume=0.08, eq="edm", low=1.2, mid=0.55, high=0.45):
104
+ # dark concrete reflections
105
+ # low frequency pressure
106
+ # distant system noise
107
+ pattern("0 . . . 0 . 3 . 0 . . . 5 . 3 .")
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pycodedj"
3
- version = "0.5.0"
3
+ version = "0.5.1"
4
4
  requires-python = ">=3.10"
5
5
  readme = "README.md"
6
6
  license = {file = "LICENSE"}
@@ -2,5 +2,5 @@
2
2
 
3
3
  from ._loop import loop, pattern
4
4
 
5
- __version__ = "0.5.0"
5
+ __version__ = "0.5.1"
6
6
  __all__ = ["loop", "pattern"]
@@ -4,6 +4,7 @@ import argparse
4
4
  import sys
5
5
  from pathlib import Path
6
6
 
7
+ from . import __version__
7
8
  from .block_parser import parse_blocks
8
9
  from .engine import Engine
9
10
  from .osc_bridge import OscBridge, OscEndpoint, OscError
@@ -11,6 +12,7 @@ from .osc_bridge import OscBridge, OscEndpoint, OscError
11
12
 
12
13
  def _build_parser() -> argparse.ArgumentParser:
13
14
  parser = argparse.ArgumentParser(prog="pycodedj")
15
+ parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
14
16
  sub = parser.add_subparsers(dest="command")
15
17
 
16
18
  eval_p = sub.add_parser("eval", help="Evaluate a loop block and send OSC parameters")
@@ -1,150 +0,0 @@
1
- # PyCodeDJ club groove — EDM edition
2
- #
3
- # 8 loops, each with a distinct code structure and sonic role.
4
- # Load sc/synths.scd in SuperCollider, then run:
5
- # pycodedj watch examples/club_set.py
6
- #
7
- # Code structure → sound (by design):
8
- #
9
- # kick_hard depth=1, cf=0 → 580 Hz, 0.10 Hz LFO (dark, static)
10
- # sub_bass depth=2, cf=1 → 960 Hz, 0.59 Hz LFO (dark sub pulse)
11
- # bass_acid depth=5, cf=4 → 2100 Hz, 2.06 Hz LFO (acid squelch)
12
- # hat_engine depth=7, cf=9 → 2860 Hz, 4.51 Hz LFO (brightest, fastest)
13
- # clap_snare depth=6, cf=5 → 2480 Hz, 2.55 Hz LFO (bright backbeat)
14
- # chord_rave depth=7, cf=7 → 2860 Hz, 3.53 Hz LFO (rave stabs)
15
- # lead_hoover depth=5, cf=4 → 2100 Hz, 2.06 Hz LFO reverb=0.15
16
- # shimmer_pad depth=1, cf=0 → 580 Hz, 0.10 Hz LFO reverb=0.60
17
-
18
- from pycodedj import loop
19
-
20
-
21
- # --- Foundation: dead simple, bone-dry ---
22
-
23
- @loop("kick_hard", interval=1.0)
24
- def four_on_floor(volume=0.9, eq="edm", low=1.25):
25
- hit = "down"
26
- _ = hit
27
-
28
-
29
- # --- Sub: minimal pulse, barely more than the kick ---
30
-
31
- @loop("sub_bass", interval=1.0)
32
- def sub_layer(volume=0.4, eq="edm", low=1.45, high=0.7):
33
- for beat in range(4):
34
- sub = "low"
35
- _ = sub
36
-
37
-
38
- # --- Groove: syncopated acid sequence with accents ---
39
-
40
- @loop("bass_acid", interval=0.5)
41
- def acid_line(volume=0.22, eq="edm", low=1.2, mid=1.05):
42
- pattern = [
43
- ("hit", True), ("skip", False), ("slide", True),
44
- ("hit", True), ("skip", False), ("accent", True),
45
- ("skip", False), ("slide", True),
46
- ]
47
- for step, (note, active) in enumerate(pattern):
48
- if active:
49
- for layer in range(2):
50
- if layer == 0:
51
- out = note
52
- else:
53
- out = f"{note}_tail"
54
- _ = out
55
-
56
-
57
- # --- Hats: mechanical 16th-note grid ---
58
-
59
- @loop("hat_engine", interval=0.25)
60
- def closed_hats(volume=0.13, eq="edm", low=0.45, high=1.25):
61
- for bar in range(2):
62
- for tick in range(16):
63
- if tick % 4 == 0:
64
- if tick == 0:
65
- if bar == 0:
66
- hat = "anchor_a"
67
- else:
68
- hat = "anchor_b"
69
- else:
70
- hat = "beat"
71
- elif tick % 2 == 0:
72
- hat = "up"
73
- elif tick in (3, 7, 11, 15):
74
- if bar == 0:
75
- hat = "ghost_a"
76
- else:
77
- hat = "ghost_b"
78
- else:
79
- hat = "skip"
80
- if tick in (6, 14):
81
- hat = "open"
82
- _ = hat
83
-
84
-
85
- # --- Backbeat: decisive 2 & 4 with fill on bar 4 ---
86
-
87
- @loop("clap_snare", interval=1.0)
88
- def backbeat(volume=0.26, eq="pop", low=0.75, high=1.15):
89
- for bar in range(4):
90
- for beat in range(4):
91
- if beat == 1:
92
- hit = "snap"
93
- elif beat == 3:
94
- if bar == 3:
95
- hit = "fill"
96
- else:
97
- hit = "heavy"
98
- else:
99
- hit = "off"
100
- _ = hit
101
-
102
-
103
- # --- Harmonic: stacked chord voices across phrases ---
104
-
105
- @loop("chord_rave", interval=2.0)
106
- def rave_stabs(volume=0.14, eq="edm", mid=0.9):
107
- for phrase in range(4):
108
- for voice in range(3):
109
- for harmonic in range(2):
110
- if phrase == 0:
111
- if voice == 0:
112
- chord = "root"
113
- elif voice == 1:
114
- chord = "fifth"
115
- else:
116
- chord = "octave"
117
- elif phrase == 2:
118
- chord = "resolve"
119
- else:
120
- chord = "hit"
121
- _ = chord
122
-
123
-
124
- # --- Lead: phrase-based, slight air ---
125
-
126
- @loop("lead_hoover", interval=4.0)
127
- def hoover(volume=0.11, eq="edm", high=1.25):
128
- # classic rave swell
129
- # builds and drops
130
- for phrase in range(4):
131
- for step in range(3):
132
- if phrase in (0, 2):
133
- if step == 0:
134
- motion = "attack"
135
- else:
136
- motion = "hold"
137
- _ = motion
138
-
139
-
140
- # --- Space: pure atmosphere, no code at all ---
141
-
142
- @loop("shimmer_pad", interval=8.0)
143
- def shimmer(volume=0.06, eq="acoustic", low=0.65):
144
- # wide hall reverb
145
- # slow harmonic drift
146
- # always underneath everything
147
- # never noticed until it stops
148
- # consonance without definition
149
- # air between the notes
150
- pass
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes