Skip to content

Commit e590a11

Browse files
committed
add playground.py
1 parent 7aeffbe commit e590a11

File tree

1 file changed

+217
-0
lines changed

1 file changed

+217
-0
lines changed

playground.py

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Isobar Playground - Quick MIDI Experimentation Environment
4+
5+
A ready-to-go environment for live coding and experimenting with isobar patterns.
6+
Start coding immediately with pre-configured MIDI output and helpful shortcuts.
7+
8+
Usage:
9+
python playground.py
10+
11+
Quick Commands:
12+
play(pattern, **kwargs) - Schedule pattern immediately
13+
stop_all() - Stop all playing tracks
14+
set_tempo(bpm) - Change tempo
15+
show_devices() - List available MIDI devices
16+
17+
Example:
18+
play(PSequence([60, 64, 67]), duration=0.5)
19+
play(PChoice([72, 75, 79]) + PRandom(-12, 12), channel=1)
20+
"""
21+
22+
from isobar import *
23+
import logging
24+
import time
25+
import atexit
26+
27+
# Global references for easy access
28+
timeline = None
29+
tracks = []
30+
31+
def setup():
32+
"""Initialize the playground environment"""
33+
global timeline
34+
35+
# Set up logging for feedback
36+
logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(message)s")
37+
38+
# Show available MIDI devices
39+
print("🎹 Isobar Playground - MIDI Experimentation Environment")
40+
print("=" * 60)
41+
show_devices()
42+
43+
# Create timeline with error handling for stability
44+
timeline = Timeline(tempo=120, ignore_exceptions=True)
45+
timeline.start()
46+
47+
print(f"\n✅ Timeline started at 120 BPM")
48+
print("💡 Type 'help_playground()' for commands and examples")
49+
50+
# Cleanup on exit
51+
atexit.register(cleanup)
52+
53+
def show_devices():
54+
"""Display available MIDI output devices"""
55+
from isobar.io import get_midi_output_names
56+
devices = get_midi_output_names()
57+
print(f"\n📱 Available MIDI Devices ({len(devices)}):")
58+
if devices:
59+
for i, name in enumerate(devices):
60+
print(f" {i}: {name}")
61+
else:
62+
print(" No MIDI devices found. Consider:")
63+
print(" - Enable IAC Driver in Audio MIDI Setup (macOS)")
64+
print(" - Connect a hardware MIDI device")
65+
print(" - Use virtual MIDI software")
66+
67+
def play(pattern, duration=1, velocity=80, channel=0, **kwargs):
68+
"""
69+
Quick function to play a pattern immediately
70+
71+
Args:
72+
pattern: The pattern to play (note values)
73+
duration: Note duration pattern (default 1)
74+
velocity: Note velocity pattern (default 80)
75+
channel: MIDI channel (default 0)
76+
**kwargs: Additional track parameters
77+
78+
Returns:
79+
Track reference for further manipulation
80+
"""
81+
global timeline, tracks
82+
83+
if timeline is None:
84+
print("❌ Timeline not initialized. Run setup() first.")
85+
return None
86+
87+
# Build event dict
88+
event_dict = {
89+
"note": pattern,
90+
"duration": duration,
91+
"velocity": velocity,
92+
"channel": channel,
93+
**kwargs
94+
}
95+
96+
# Schedule the pattern
97+
track = timeline.schedule(event_dict)
98+
tracks.append(track)
99+
100+
print(f"🎵 Playing pattern on channel {channel}")
101+
return track
102+
103+
def stop_all():
104+
"""Stop all currently playing tracks"""
105+
global timeline, tracks
106+
107+
if timeline:
108+
for track in tracks:
109+
if track and hasattr(track, 'stop'):
110+
track.stop()
111+
tracks.clear()
112+
print("🛑 All tracks stopped")
113+
114+
def set_tempo(bpm):
115+
"""Change the global tempo"""
116+
global timeline
117+
118+
if timeline:
119+
timeline.tempo = bpm
120+
print(f"🎯 Tempo set to {bpm} BPM")
121+
122+
def cleanup():
123+
"""Clean up resources on exit"""
124+
global timeline
125+
126+
if timeline:
127+
stop_all()
128+
timeline.stop()
129+
print("\n👋 Playground session ended")
130+
131+
def help_playground():
132+
"""Show playground commands and examples"""
133+
print("""
134+
🎹 Isobar Playground Commands:
135+
136+
Basic Functions:
137+
play(pattern, **kwargs) - Play a pattern immediately
138+
stop_all() - Stop all tracks
139+
set_tempo(bpm) - Change tempo
140+
show_devices() - List MIDI devices
141+
help_playground() - Show this help
142+
143+
Quick Pattern Examples:
144+
145+
# Simple sequences
146+
play(PSequence([60, 64, 67, 72]))
147+
play(PRange(60, 73))
148+
149+
# Rhythmic patterns
150+
play(PSequence([36]), duration=PBjorklund(3, 8))
151+
play(PChoice([60, 64, 67]), duration=PChoice([0.25, 0.5, 1]))
152+
153+
# Random and evolving
154+
play(PChoice([60, 64, 67]) + PRandom(-12, 12))
155+
play(PBrown(60, 12, 2), duration=0.25)
156+
play(PWalk([60, 64, 67, 70], 1))
157+
158+
# Multiple channels
159+
play(PSequence([36, 38]), channel=9) # Drums
160+
play(PChoice([48, 50, 53]), channel=1, velocity=60) # Bass
161+
162+
# Control changes
163+
play(PSequence([60]), control={74: PSine(64, 32, 8)}) # Filter sweep
164+
165+
Common Pattern Classes:
166+
PSequence(list) - Play list in order
167+
PChoice(list) - Random choice from list
168+
PRange(start, stop, step) - Numeric range
169+
PRandom(min, max) - Random numbers
170+
PBrown(start, max, step) - Brownian motion
171+
PWalk(values, step_size) - Random walk through values
172+
PBjorklund(hits, length) - Euclidean rhythms
173+
PSine(center, amplitude, period) - Sine wave
174+
175+
Shortcuts for live coding:
176+
C4 = 60 # Middle C
177+
KICK = 36 # Standard kick drum
178+
SNARE = 38 # Standard snare drum
179+
180+
Try:
181+
play(PSequence([C4, C4+4, C4+7]), duration=0.5)
182+
""")
183+
184+
# Helpful constants for live coding
185+
C4 = 60
186+
D4 = 62
187+
E4 = 64
188+
F4 = 65
189+
G4 = 67
190+
A4 = 69
191+
B4 = 71
192+
C5 = 72
193+
194+
KICK = 36
195+
SNARE = 38
196+
HIHAT = 42
197+
OPENHAT = 46
198+
CRASH = 49
199+
200+
# Auto-setup when imported or run as script
201+
setup()
202+
203+
# Interactive mode when run as main script
204+
if __name__ == "__main__":
205+
print("\n🚀 Quick Start Examples:")
206+
print(" play(PSequence([60, 64, 67, 72]))")
207+
print(" play(PChoice([60, 64, 67]) + PRandom(-12, 12), duration=0.25)")
208+
print(" play(PSequence([36]), duration=PBjorklund(3, 8), channel=9)")
209+
print("\n⌨️ Press Ctrl+C to exit")
210+
211+
try:
212+
# Keep the script running for interactive use
213+
while True:
214+
time.sleep(1)
215+
except KeyboardInterrupt:
216+
print("\n🛑 Stopping playground...")
217+
cleanup()

0 commit comments

Comments
 (0)