| 1 |
#!/usr/bin/env python |
|---|
| 2 |
|
|---|
| 3 |
import sys |
|---|
| 4 |
import os.path |
|---|
| 5 |
import optparse |
|---|
| 6 |
import threading |
|---|
| 7 |
import rtmidi |
|---|
| 8 |
import scosc |
|---|
| 9 |
import scsynth |
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
def die(msg): |
|---|
| 13 |
sys.stderr.write(msg+'\n') |
|---|
| 14 |
sys.exit(1) |
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
def print_ports(midi): |
|---|
| 18 |
ports = range(midi.getPortCount()) |
|---|
| 19 |
if ports: |
|---|
| 20 |
for i in ports: |
|---|
| 21 |
print ' [%i]:' % i,midi.getPortName(i) |
|---|
| 22 |
else: |
|---|
| 23 |
print 'NO MIDI PORTS!' |
|---|
| 24 |
sys.exit(0) |
|---|
| 25 |
|
|---|
| 26 |
|
|---|
| 27 |
def ask_for_port(midi): |
|---|
| 28 |
print_ports(midi) |
|---|
| 29 |
print 'Enter midi device number [0]:', |
|---|
| 30 |
entry = sys.stdin.readline().strip() |
|---|
| 31 |
if entry: |
|---|
| 32 |
return int(entry) |
|---|
| 33 |
else: |
|---|
| 34 |
return 0 |
|---|
| 35 |
|
|---|
| 36 |
|
|---|
| 37 |
|
|---|
| 38 |
class Instrument: |
|---|
| 39 |
def __init__(self, server, synthdef, verbose=False): |
|---|
| 40 |
self.notes = {} |
|---|
| 41 |
self.synth_pool = scsynth.IntPool(1000) |
|---|
| 42 |
self.synthdef = synthdef |
|---|
| 43 |
self.server = server |
|---|
| 44 |
self.verbose = verbose |
|---|
| 45 |
|
|---|
| 46 |
def note_on(self, key, vel): |
|---|
| 47 |
self.notes[key] = self.synth_pool.get() |
|---|
| 48 |
osc = ('/s_new', self.synthdef, self.notes[key], 1, 0, 'key', key) |
|---|
| 49 |
self.server.sendMsg(*osc) |
|---|
| 50 |
if self.verbose: |
|---|
| 51 |
print '<<<', osc |
|---|
| 52 |
|
|---|
| 53 |
def note_off(self, key, vel): |
|---|
| 54 |
if key in self.notes: |
|---|
| 55 |
osc = ('/n_free', self.notes[key]) |
|---|
| 56 |
self.server.sendMsg(*osc) |
|---|
| 57 |
if self.verbose: |
|---|
| 58 |
print '<<<', osc |
|---|
| 59 |
|
|---|
| 60 |
|
|---|
| 61 |
|
|---|
| 62 |
class Collector(threading.Thread): |
|---|
| 63 |
def __init__(self, device, port, server, synthdef, verbose=False): |
|---|
| 64 |
threading.Thread.__init__(self) |
|---|
| 65 |
self.setDaemon(True) |
|---|
| 66 |
self.port = port |
|---|
| 67 |
self.device = device |
|---|
| 68 |
self.instrument = Instrument(server, synthdef) |
|---|
| 69 |
self.running = True |
|---|
| 70 |
self.verbose = verbose |
|---|
| 71 |
|
|---|
| 72 |
def quit(self): |
|---|
| 73 |
self.running = False |
|---|
| 74 |
|
|---|
| 75 |
def run(self): |
|---|
| 76 |
self.device.openPort(self.port, True) |
|---|
| 77 |
while self.running: |
|---|
| 78 |
midi = self.device.getMessage() |
|---|
| 79 |
if midi: |
|---|
| 80 |
if self.verbose: |
|---|
| 81 |
print '>>>', midi |
|---|
| 82 |
device, key, vel, timestamp = midi |
|---|
| 83 |
if vel: |
|---|
| 84 |
self.instrument.note_on(key, vel) |
|---|
| 85 |
else: |
|---|
| 86 |
self.instrument.note_off(key, vel) |
|---|
| 87 |
|
|---|
| 88 |
def main(): |
|---|
| 89 |
parser = optparse.OptionParser() |
|---|
| 90 |
parser.add_option("-S", "--synthdef", |
|---|
| 91 |
dest="synthdef", |
|---|
| 92 |
help="name of SynthDef to load and play") |
|---|
| 93 |
parser.add_option("-d", "--defdir", |
|---|
| 94 |
dest="defdir", |
|---|
| 95 |
help="dir with scsyndef file") |
|---|
| 96 |
parser.add_option("-H", "--host", |
|---|
| 97 |
dest="host", default='localhost', |
|---|
| 98 |
help="host with a running supercollider server") |
|---|
| 99 |
parser.add_option("-p", "--port", |
|---|
| 100 |
dest="port", default=57110, |
|---|
| 101 |
help="port to connect to supercollider server") |
|---|
| 102 |
parser.add_option("-v", "--verbose", |
|---|
| 103 |
dest="verbose", action="store_true", |
|---|
| 104 |
help="spit OSC responses, etc") |
|---|
| 105 |
parser.add_option("-s", "--spew", |
|---|
| 106 |
dest="spew", action='store_true', |
|---|
| 107 |
help="spew OSC commands") |
|---|
| 108 |
options, args = parser.parse_args(sys.argv) |
|---|
| 109 |
|
|---|
| 110 |
if options.synthdef is None: |
|---|
| 111 |
die('--synthdef is required') |
|---|
| 112 |
|
|---|
| 113 |
server = scosc.Controller((options.host, int(options.port)), |
|---|
| 114 |
verbose=options.verbose, |
|---|
| 115 |
spew=options.spew) |
|---|
| 116 |
if options.defdir: |
|---|
| 117 |
fpath = os.path.join(options.defdir, options.synthdef+'.scsyndef') |
|---|
| 118 |
server.sendMsg('/d_load', os.path.expanduser(fpath)) |
|---|
| 119 |
|
|---|
| 120 |
midi = rtmidi.RtMidiIn() |
|---|
| 121 |
port = ask_for_port(midi) |
|---|
| 122 |
print '\busing port %i' % port |
|---|
| 123 |
collector = Collector(midi, port, server, options.synthdef, |
|---|
| 124 |
verbose=options.verbose) |
|---|
| 125 |
collector.start() |
|---|
| 126 |
print 'HIT ENTER TO EXIT' |
|---|
| 127 |
sys.stdin.read(1) |
|---|
| 128 |
collector.quit = True |
|---|
| 129 |
|
|---|
| 130 |
|
|---|
| 131 |
main() |
|---|