Skip to main content

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flux_tensor_midi-0.1.0.tar.gz (30.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

flux_tensor_midi-0.1.0-py3-none-any.whl (28.3 kB view details)

Uploaded Python 3

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

Hashes for flux_tensor_midi-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b38b912caaf2daad7994d191d0c18f2f64c621debfeb4c319fae364990bfd2ab
MD5 4820f22a11bb999675e10eba4f36d62c
BLAKE2b-256 c1f273df5c7f44ceaf0885fe934769f12a23b45c3f24f2f7e952ebdf9249fbaa

See more details on using hashes here.

File details

Details for the file flux_tensor_midi-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for flux_tensor_midi-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f055cb7c230c878c45f421eb66b3209b96bf615a91886d67315733dd847017ca
MD5 fb951cca9eec2a28f73835aab65592d6
BLAKE2b-256 31766649b90ddf234701e2a986401df007366ca442f0826c571ea06c9ad8b855

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page