PLATO rooms as musicians — flux tensor MIDI ensemble coordination
Project description
FLUX-Tensor-MIDI ⚒️
PLATO rooms as musicians. Each room has a T-0 clock, produces timestamped events (tiles=notes), listens to other rooms, snaps to rhythm via Eisenstein lattice, and sends side-channels (nods/smiles/frowns) for ensemble coordination.
Zero external dependencies. Pure Python 3.10+.
Musical Analogy Table
| PLATO Concept | Musical Equivalent | MIDI Mapping |
|---|---|---|
| Room | Musician | MIDI Channel |
| T-0 Clock | Metronome / Conductor | MIDI Clock (0xF8) |
| Tile (event) | Note | Note On/Off |
| FluxVector (9-ch) | Chord / Harmonic Spectrum | 9 MIDI notes (C4–D5) |
| Salience | Note Velocity | Velocity (0–127) |
| Tolerance | Pitch Bend / Jitter | Pitch Bend range |
| Eisenstein Snap | Rhythmic Quantization | Grid snapping |
| Nod | Nod (agreement) | CC #1 (Mod Wheel) |
| Smile | Smile (approval) | CC #2 (Breath) |
| Frown | Frown (disagreement) | CC #3 (Expression) |
| Listening (room→room) | Musician's ear | MIDI Channel routing |
| Conductor | Band leader / click | Master MIDI Clock |
| Ensemble | Band / Orchestra | Multi-channel MIDI output |
| Jaccard Harmony | Chord similarity | Chromatic interval match |
| Rhythmic Role | Groove part | Program change (patch) |
Rhythm Ratios (Eisenstein Lattice)
| Role | Ratio | Period (at 120 BPM) | Musical Feel |
|---|---|---|---|
| Root | 1:1 | 500 ms | Downbeat / quarter |
| Halftime | 2:1 | 1000 ms | Half speed |
| Triplet | 3:2 | 750 ms | Swung feel |
| Waltz | 3:1 | 1500 ms | Waltz time |
| Compound | 4:3 | ~667 ms | Compound meter |
| Doubletime | 1:2 | 250 ms | Double speed |
| Offset | 1:1 | 500 ms + 120° phase | Syncopated |
| Quintuple | 5:4 | 625 ms | Quintuple meter |
| Septuple | 7:4 | 875 ms | Septuple meter |
Covering radius: 1/√3 ≈ 0.577 — optimal hexagonal tiling.
Quick Start
pip install flux-tensor-midi
from flux_tensor_midi import FluxVector, TZeroClock, RoomMusician, EisensteinSnap
from flux_tensor_midi.core.snap import RhythmicRole
from flux_tensor_midi.ensemble.band import Band
# Create musicians with different rhythmic roles
piano = RoomMusician("Piano", role=RhythmicRole.ROOT)
bass = RoomMusician("Bass", role=RhythmicRole.HALFTIME)
drums = RoomMusician("Drums", role=RhythmicRole.TRIPLET)
# Form a band with a conductor
band = Band("Trio", conductor=piano, bpm=120.0)
band.add_musician(bass)
band.add_musician(drums)
band.everyone_listens_to_everyone()
# Set some initial state
piano.update_state(FluxVector([0.8, 0.0, 0.6, 0.0, 0.7, 0.0, 0.0, 0.0, 0.0]))
bass.update_state(FluxVector([0.0, 0.9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])) # simple root
# Tick the band
results = band.tick_all()
print(f"Piano event at {results['Piano'][0]:.1f}ms")
# Check harmony
hs = band.harmony()
print(f"Chord quality: {hs.quality()}, consonance: {hs.consonance():.3f}")
# Side-channel comms
piano.send_nod(bass)
print(f"Bass received nods from: {bass.receive_sidechannels()['nods']}")
Package Structure
flux_tensor_midi/
├── __init__.py
├── core/
│ ├── __init__.py
│ ├── flux.py — FluxVector (9-ch tensor with salience+tolerance)
│ ├── clock.py — TZeroClock (EWMA-adaptive metronome)
│ ├── room.py — RoomMusician (PLATO room as musician)
│ └── snap.py — EisensteinSnap (rhythmic quantization lattice)
├── midi/
│ ├── __init__.py
│ ├── events.py — MidiEvent, NoteName, flux→MIDI conversion
│ ├── clock.py — MidiClock (24 PPQN, software clock)
│ └── channel.py — MidiChannel mapping by rhythmic role
├── sidechannel/
│ ├── __init__.py
│ ├── nod.py — Nod gesture (agreement/acknowledgment)
│ ├── smile.py — Smile gesture (positive affect)
│ └── frown.py — Frown gesture (disagreement/concern)
├── harmony/
│ ├── __init__.py
│ ├── jaccard.py — Jaccard similarity for FluxVector sets
│ ├── spectrum.py — Spectral analysis (centroid, flux, autocorr)
│ └── chord.py — HarmonyState (consonance, quality, voice leading)
└── ensemble/
├── __init__.py
├── band.py — Band (ensemble with conductor)
└── score.py — Score (recorded performance, export)
Running Tests
cd python
pip install -e ".[dev]"
pytest -v
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flux_tensor_midi-0.1.0.tar.gz.
File metadata
- Download URL: flux_tensor_midi-0.1.0.tar.gz
- Upload date:
- Size: 30.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b38b912caaf2daad7994d191d0c18f2f64c621debfeb4c319fae364990bfd2ab
|
|
| MD5 |
4820f22a11bb999675e10eba4f36d62c
|
|
| BLAKE2b-256 |
c1f273df5c7f44ceaf0885fe934769f12a23b45c3f24f2f7e952ebdf9249fbaa
|
File details
Details for the file flux_tensor_midi-0.1.0-py3-none-any.whl.
File metadata
- Download URL: flux_tensor_midi-0.1.0-py3-none-any.whl
- Upload date:
- Size: 28.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f055cb7c230c878c45f421eb66b3209b96bf615a91886d67315733dd847017ca
|
|
| MD5 |
fb951cca9eec2a28f73835aab65592d6
|
|
| BLAKE2b-256 |
31766649b90ddf234701e2a986401df007366ca442f0826c571ea06c9ad8b855
|