CLI args: --channels, --port, --drums, --buffer

python test_live.py --channels 4 --drums trap --port OP-XY
python test_live.py 4217 -c 16 -d house -b 256

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-29 21:16:29 -04:00
parent 970c730012
commit 2084473788
+27 -15
View File
@@ -19,9 +19,12 @@ def note_name(midi):
class LiveTUI: class LiveTUI:
def __init__(self, seed=None, port="OP-XY"): def __init__(self, seed=None, port="OP-XY", n_channels=8,
drum_pattern="rock", buffer_size=128):
self.seed = seed or random.randint(0, 9999) self.seed = seed or random.randint(0, 9999)
self.port = port self.port = port
self.n_channels = n_channels
self.buffer_size = buffer_size
self.engine = None self.engine = None
self.log_lines = [] self.log_lines = []
self.max_log = 500 self.max_log = 500
@@ -29,20 +32,21 @@ class LiveTUI:
self.instruments = sorted([k for k in INSTRUMENTS.keys() self.instruments = sorted([k for k in INSTRUMENTS.keys()
if k not in ("808_bass",)]) if k not in ("808_bass",)])
self.drum_patterns = sorted(Pattern.list_presets()) self.drum_patterns = sorted(Pattern.list_presets())
self.current_drum = "rock" self.current_drum = drum_pattern
self.picks = [] self.picks = []
self.bpm = "" self.bpm = ""
self.status = "Init" self.status = "Init"
self.status_color = 3 # yellow self.status_color = 3
self._build_engine() self._build_engine()
def _build_engine(self): def _build_engine(self):
rng = random.Random(self.seed) rng = random.Random(self.seed)
self.picks = rng.sample(self.instruments, 8) self.picks = rng.sample(self.instruments, min(self.n_channels, len(self.instruments)))
self.engine = LiveEngine(buffer_size=128) self.engine = LiveEngine(buffer_size=self.buffer_size)
for i, inst in enumerate(self.picks, 1): for i, inst in enumerate(self.picks, 1):
self.engine.channel(i, instrument=inst, reverb=0.3) self.engine.channel(i, instrument=inst, reverb=0.3)
self.engine.drums(self.current_drum, volume=0.5) if self.current_drum and self.current_drum not in ("none", "-"):
self.engine.drums(self.current_drum, volume=0.5)
self.engine.cc(0, "lowpass", min_val=300, max_val=8000) self.engine.cc(0, "lowpass", min_val=300, max_val=8000)
def log(self, msg, color=0): def log(self, msg, color=0):
@@ -516,10 +520,10 @@ class LiveTUI:
elif verb == "ch" and len(parts) == 2: elif verb == "ch" and len(parts) == 2:
try: try:
n = int(parts[1]) n = int(parts[1])
if 1 <= n <= 8: if 1 <= n <= len(self.picks):
self.log(f"Ch {n}: {self.picks[n-1]}", 2) self.log(f"Ch {n}: {self.picks[n-1]}", 2)
else: else:
self.log("Channel 1-8", 4) self.log(f"Channel 1-{len(self.picks)}", 4)
except ValueError: except ValueError:
self.log("ch <1-8> [instrument]", 4) self.log("ch <1-8> [instrument]", 4)
elif verb == "ch" and len(parts) >= 3: elif verb == "ch" and len(parts) >= 3:
@@ -529,8 +533,8 @@ class LiveTUI:
if inst not in INSTRUMENTS: if inst not in INSTRUMENTS:
self.log(f"Unknown: {inst}", 4) self.log(f"Unknown: {inst}", 4)
return return
if not (1 <= n <= 8): if not (1 <= n <= len(self.picks)):
self.log("Channel 1-8", 4) self.log(f"Channel 1-{len(self.picks)}", 4)
return return
self.picks[n - 1] = inst self.picks[n - 1] = inst
self.engine.channel(n, instrument=inst, reverb=0.3) self.engine.channel(n, instrument=inst, reverb=0.3)
@@ -542,8 +546,8 @@ class LiveTUI:
n = int(parts[1]) n = int(parts[1])
param = parts[2] param = parts[2]
val = float(parts[3]) val = float(parts[3])
if not (1 <= n <= 8): if not (1 <= n <= len(self.picks)):
self.log("Channel 1-8", 4) self.log(f"Channel 1-{len(self.picks)}", 4)
return return
if n not in self.engine.channels: if n not in self.engine.channels:
self.log(f"Channel {n} not active", 4) self.log(f"Channel {n} not active", 4)
@@ -708,9 +712,17 @@ class LiveTUI:
def main(): def main():
seed = int(sys.argv[1]) if len(sys.argv) > 1 else None import argparse
port = sys.argv[2] if len(sys.argv) > 2 else "OP-XY" parser = argparse.ArgumentParser(description="PyTheory Live — real-time MIDI synthesizer")
tui = LiveTUI(seed=seed, port=port) parser.add_argument("seed", nargs="?", type=int, default=None, help="Random seed for instruments")
parser.add_argument("--port", "-p", default="OP-XY", help="MIDI port name (default: OP-XY)")
parser.add_argument("--channels", "-c", type=int, default=8, help="Number of channels (default: 8)")
parser.add_argument("--drums", "-d", default="rock", help="Drum pattern (default: rock, 'none' to disable)")
parser.add_argument("--buffer", "-b", type=int, default=128, help="Audio buffer size (default: 128)")
args = parser.parse_args()
tui = LiveTUI(seed=args.seed, port=args.port, n_channels=args.channels,
drum_pattern=args.drums, buffer_size=args.buffer)
curses.wrapper(tui.run) curses.wrapper(tui.run)