Metadata-Version: 2.4
Name: pycodedj
Version: 0.5.0
License: MIT License with Commons Clause
        
        "Commons Clause" License Condition v1.0
        
        The Software is provided to you by the Licensor under the License,
        as defined below, subject to the following condition.
        
        Without limiting other conditions in the License, the grant of rights
        under the License will not include, and the License does not grant to
        you, the right to Sell the Software.
        
        For purposes of the foregoing, "Sell" means practicing any or all of
        the rights granted to you under the License to provide to third
        parties, for a fee or other consideration (including without limitation
        fees for hosting or consulting/support services related to the
        Software), a product or service whose value derives, entirely or
        substantially, from the functionality of the Software. Any license
        notice or attribution required by the License must also include this
        Commons Clause License Condition notice.
        
        Software: PyCodeDJ
        License: MIT
        Licensor: Yuichi Kaneko
        
        -------------------------------------------------------------------------------
        
        MIT License
        
        Copyright (c) 2026 Yuichi Kaneko
        
        Permission is hereby granted, free of charge, to any person obtaining
        a copy of this software and associated documentation files (the
        "Software"), to deal in the Software without restriction, including
        without limitation the rights to use, copy, modify, merge, publish,
        distribute, sublicense, and/or sell copies of the Software, and to
        permit persons to whom the Software is furnished to do so, subject to
        the following conditions:
        
        The above copyright notice and this permission notice shall be included
        in all copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
        EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
        MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
        IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
        CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
        TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
        SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: python-osc>=1.8
Provides-Extra: dev
Requires-Dist: mypy; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Provides-Extra: watch
Requires-Dist: watchdog>=3.0; extra == 'watch'
Description-Content-Type: text/markdown

# PyCodeDJ

[日本語版 README はこちら](https://github.com/kanekoyuichi/pycodedj/blob/main/README.ja.md) · [Full Manual (EN)](https://github.com/kanekoyuichi/pycodedj/blob/main/docs/manual.md) · [マニュアル (JA)](https://github.com/kanekoyuichi/pycodedj/blob/main/docs/manual.ja.md)

A live-coding environment that translates Python code structure into music in real time. Every save changes the performance.

---

## Concept

PyCodeDJ connects "writing code" directly to "making sound."

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.

Two things set it apart from existing Python ↔ SuperCollider bridges (sc3nb, supriya):

- **Hot-reload performance** — swap out a loop without stopping it. Saving a file is an immediate sound change.
- **Two performance styles** — auto-generation from code structure, and explicit `pattern()` notation for rhythm and pitch. Mix them freely in the same file.

---

## Architecture

```
[Python engine]  →OSC→  [SuperCollider]  →audio out→  speakers
      ↓ OSC
  [Hydra etc.]  →video out→  screen
```

| Layer | Role | Technology |
| :--- | :--- | :--- |
| Control | Code analysis, scheduling, OSC dispatch | Python 3.10+, python-osc, watchdog |
| Audio | Real-time sound synthesis | SuperCollider (scsynth) |
| Visual | Music-synced visuals | Hydra or Pyxel |

BPM clock is held by SuperCollider's `TempoClock`. Python only sends parameter updates over OSC; timing accuracy is delegated to SuperCollider.

---

## Code Structure → Music Parameter Mapping

| Code feature | Music parameter |
| :--- | :--- |
| Max nesting depth | Filter Cutoff (200–4000 Hz) |
| Control-flow count (if/for/while) | LFO rate (0.1–5.0 Hz) |
| Function definition count | Polyphony voice count (1–4) |
| Comment ratio | Reverb depth (0.0–0.8) |
| `volume=` argument | Amplitude (0.0–1.0) |
| `eq=` / `low=` / `mid=` / `high=` arguments | Simple 3-band EQ |

---

## Installation

**Requirements**

- Python 3.10 or later
- SuperCollider (with scsynth available)

```bash
pip install 'pycodedj[watch]'
```

The `[watch]` extra enables the `pycodedj watch` command.

Development install:

```bash
git clone https://github.com/kanekoyuichi/pycodedj
cd pycodedj
pip install -e ".[dev]"
```

---

## Quick Start

**1. Boot SuperCollider and load the synths**

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:

```
PyCodeDJ synths loaded. Ready. OSC port: 57120
```

**2. Write a live-coding file**

```python
from pycodedj import loop, pattern

# Code-structure mode: the shape of your code maps to sound
@loop("bass", interval=2.0)
def bass(volume=0.4):
    for i in range(8):
        if i % 2 == 0:
            pass

# pattern() mode: specify rhythm and pitch explicitly
@loop("kick", synth="floor_kick", dur=0.25)
def kick():
    pattern("x . x .")

@loop("melody", synth="acid_lead", root="A3", scale="minor", dur=0.25)
def melody():
    pattern("0 . 3 . 5 .")

# Comments create space (reverb)
@loop("pad", interval=4.0)
def pad(volume=0.1):
    # ambient space
    # silence is music
    pass
```

**3. Start watch mode**

```bash
pycodedj watch demo.py
```

From here, just write code and save. Every save re-evaluates all loops.

**4. Emergency stop**

```bash
pycodedj panic
```

**5. Mute / unmute**

```bash
pycodedj mute bass
pycodedj unmute bass
```

---

## Using pattern()

`pattern()` lets you specify rhythm and pitch explicitly.

```python
from pycodedj import loop, pattern

# Trigger pattern (x = hit, . = rest)
@loop("kick", synth="floor_kick", dur=0.25)
def kick():
    pattern("x . x .")

# Pitch pattern (integer = scale degree)
@loop("bass", synth="bass_acid", root="A1", scale="minor", dur=0.25)
def bass():
    pattern("0 . 3 . 5 .")

# Chords and ties
@loop("chord", synth="note", root="A1", scale="minor", dur=0.25)
def chord():
    pattern("0 . [0 3] ~ 5 . 3 .")
    # [0 3] = two-note chord, ~ = sustain the previous note one more step
```

Token reference:

| Token | Meaning |
| :--- | :--- |
| `x` | Trigger (plays root note) |
| `.` | Rest (silence) |
| `0`, `1`, `2` … | Scale degree (pitch) |
| `[0 3]` | Chord (multiple degrees simultaneously) |
| `~` | Tie (extends the previous note/chord by one step) |

`@loop` arguments for pattern mode:

| Argument | Description |
| :--- | :--- |
| `synth=` | Synth name to use |
| `root=` | Root note (e.g. `"A3"`, `"C4"`) |
| `scale=` | Scale name (e.g. `"minor"`, `"major"`, `"pentatonicMinor"`) |
| `dur=` | Step length in seconds. `0.25` = sixteenth note at 60 BPM |

---

## Example Files

| File | Contents |
| :--- | :--- |
| `examples/demo.py` | Intro demo: bass / melody / pad |
| `examples/club_set.py` | EDM groove: 8 loops (kick, bass, hat, chords, pad) |
| `examples/sound_showcase.py` | All 30 synths — evaluate one at a time to audition |

---

## OSC Address Reference

| Address | Type | Parameter |
| :--- | :--- | :--- |
| `/pycodedj/loop/<name>/params` | int, float, float, float, float | `voice_count`, `cutoff`, `lfo_rate`, `reverb`, `amp` |
| `/pycodedj/loop/<name>/pattern` | int, str, float, str, int… | Pattern data |
| `/pycodedj/loop/<name>/pattern_stop` | — | Stop pattern |
| `/pycodedj/loop/<name>/amp` | float | Amplitude (compatibility) |

---

## Requirements

- **Recommended OS:** macOS (low-latency Core Audio) or Linux (Raspberry Pi 5, etc.)
- **Python:** 3.10 or later
- **SuperCollider:** 3.12 or later

---

## Roadmap

- [x] Python → SuperCollider OSC prototype
- [x] Hot-reload live loop implementation (`pycodedj watch`)
- [x] Sprint 1: Live stability (`panic`, SyntaxError recovery, `mute`/`solo`, `status`)
- [x] Sprint 2: Music DSL (`pattern()`, `@loop` parameter expansion: `synth`, `root`, `scale`, `dur`)
- [ ] Sprint 3: Sound design and playability (SynthDef cleanup, `bpm`, `list-synths`, `sample()`)
- [ ] Sprint 4: Hydra visualiser integration

---

## License

MIT + Commons Clause — free to use, modify, and perform (including paid live performances). Selling or commercially distributing the software itself is not permitted. See [LICENSE](LICENSE) for details.
