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.1.tar.gz (43.0 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.1-py3-none-any.whl (41.6 kB view details)

Uploaded Python 3

File details

Details for the file flux_tensor_midi-0.1.1.tar.gz.

File metadata

  • Download URL: flux_tensor_midi-0.1.1.tar.gz
  • Upload date:
  • Size: 43.0 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.1.tar.gz
Algorithm Hash digest
SHA256 b811d0e90d8efdc071bfd54bfd83d4e5f7c5b9f6b5217d72a53741aec3f70954
MD5 98077067fa1daf0bbfce4e6a1fd76095
BLAKE2b-256 ea85e6feee7947b902e142718ba0d1e6f21f7d5e731f6bb09ef66202d14f43fb

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for flux_tensor_midi-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3bf2dfc0fefe2de925516bd900bcec115fb48eb5509c8bc11d5a387d3a5d8c86
MD5 17db96060bc473a3776345365e84a836
BLAKE2b-256 3c21dbe5a12e8f84c6ed7f89e1bc48a04d197698f9756f8693d18a50a36fefab

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