Mercurial > hg > movesynth
changeset 4:5f9ad838d417
Completed grid_mapper.py
author | Tim MB <tim.murraybrowne@eecs.qmul.ac.uk> |
---|---|
date | Thu, 17 Feb 2011 12:46:52 +0000 |
parents | 7bd1f07044ab |
children | 149328b21d06 |
files | Specification.txt tim_grid_mapper/grid_mapper.py |
diffstat | 2 files changed, 113 insertions(+), 18 deletions(-) [+] |
line wrap: on
line diff
--- a/Specification.txt Tue Feb 15 17:31:11 2011 +0000 +++ b/Specification.txt Thu Feb 17 12:46:52 2011 +0000 @@ -8,7 +8,7 @@ s0 - ignored i1 - pitch value: 0 <= i1 < 16 i2 - on/off: i2==0 or i2==1 -i3 - velocity: 1 <= i3 <= 127 +i3 - velocity: 1 <= i3 <= 127 (never send 0) i4 - channel: anything /chord
--- a/tim_grid_mapper/grid_mapper.py Tue Feb 15 17:31:11 2011 +0000 +++ b/tim_grid_mapper/grid_mapper.py Thu Feb 17 12:46:52 2011 +0000 @@ -8,14 +8,17 @@ ''' from OSC import ThreadingOSCServer, OSCClient, OSCMessage, OSCClientError +from threading import Thread +from time import sleep #### PUBLIC OPTIONS #### -num_instruments = 3 # number of output channels +num_channels = 3 # number of instruments (and max number of people who can + # make noise). #### OSC OPTIONS - THESE NEED TO BE SET MANUALLY #### my_port = 2001 # to receive OSC messages -joe = ('localhost', " PUT JOE's PORT NUMBER IN HERE") -ableton = ('localhost', " PUT ABLETON'S PORT NUMBER IN HERE ") +joe = ('localhost', "THIS_MUST_BE_SET") +ableton = ('localhost', "THIS_MUST_BE_SET") ### Constants for grid mapping: # The range of values that the input coordinates and output values may take: @@ -40,27 +43,53 @@ #### PRIVATE VARIABLES #### -person_positions = {} # mapping from personId to x,y,z -last_update_times = {} # mapping from personId to time of last update + +# mapping from channel to the currently playing note (pitch values) or None +# initialize each channel to None: +currently_playing = [None] * num_channels + +# mapping from personId to time of last update +last_update_times = {} # OSC OBJECTS -server = ThreadingOSCServer(('localhost'), my_port) +server = None # Initialised when start() is run client = OSCClient() + + + +### MAPPING + def send_to_joe(data, address='/test'): '''Sends `data` to Joe directly as an OSC message. ''' message = OSCMesssage(address) message.extend(data) client.sendto(message, joe) + print_d('==OSC Output to Joe %s:==\n %s' % (joe, data)) +def flush(channel): + '''Sends note off messages for whatever note is currently playing on + `channel`. + ''' + pitch = currently_playing[channel] + if pitch: + #print_d('Sending note-off for note %i on channel %i.' % (pitch, channel)) + send_to_joe([ + 'Turn off note %i on channel %i' % (pitch, channel), + # first string is ignored + pitch, # pitch to turn off + 0, # 0 to turn note off + 127, # doesn't matter for note-off (but never send 0) + channel, + ]) def person_handler(address, tags, data, client_address): ''' Handles OSC input matching the 'person' tag. - `data` is [person_id, x, y, z] + `data` should be in form [person_id, x, y, z] ''' pitch, velocity, channel, cc1, cc2 = grid_map(data) @@ -68,18 +97,23 @@ # constrain and round off pitch and velocity pitch = max(min(round(pitch), MAX['pitch']), MIN['pitch']) - velocity = max(min(round(pitch), MAX['velocity']), MIN['velocity']) + velocity = max(min(round(velocity), MAX['velocity']), MIN['velocity']) - # turn integer pitch into a single 1 in a boolean array of 0s - boolean_note_array = [0] * (MAX['pitch'] - MIN['pitch'] + 1) - boolean_note_array[pitch] = 1 + if velocity and pitch == currently_playing[channel]: + return # keep playing current note - # seperate boolean for note-on and note-off - note_on = velocity > 0 - # Never send velocity == 0 - if velocity==0: - velocity = 127 - THIS NEEDS FINISHING. + # otherwise turn note off: + flush(channel) + + if velocity: # if there is a new note to play + send_to_joe([ + 'Turn on note %i on channel %i' % (pitch, channel), + # first value is string which is ignored + pitch, + 1, # 1 to turn note on + velocity, + channel + ]) @@ -125,3 +159,64 @@ (B-A)*(x-a)/(b-a) + A ''' return (B-A)*(x-a)/(b-a) + A + + +def print_d(string): + '''Function to print out debug method. Disable if processing power is in + demand. + ''' + print(string) + + + + + + +#### CONTROL + +def start(): + '''Set up OSC servers and start the program running. + ''' + global joe, ableton, server + if joe[1] == "THIS_MUST_BE_SET": + joe_port = input("Enter port number on %s for Joe's synth software: " % joe[0]) + joe = (joe[0], joe_port) + + if ableton[1] == "THIS_MUST_BE_SET": + ableton_port = input("Enter port number on %s for Ableton: " % ableton[0]) + ableton = (ableton[0], ableton_port) + + server = ThreadingOSCServer(('localhost', my_port)) + # Register OSC callbacks: + server.addMsgHandler('/person', person_handler) + t = Thread(target=server.serve_forever) + t.start() + if server.running and t.is_alive(): + print('OSC Server running on port %i.' % my_port) + print("Use 'stop()' to close it.") + else: + print('Error: Either server thread died or server is not reported as running.') + + +def stop(): + '''Close the OSC server. + ''' + if server: + server.close() + sleep(0.3) + if not server.running: + print("\n\nSuccessfully closed the OSC server. Ignore a 'Bad file descriptor' Exception - there's nothing I can do about that.") + print("Type 'quit()' to exit.") + else: + print('Error: server has been told to close but is still running.') + +if __name__=='__main__': + start() + while True: + try: + print(repr(input())) + except Exception as e: + print('Caught %s:' % repr(e)) + exception = e + trace = traceback.format_exc() + print('Exception saved as `exception`. Stack trace saved as `trace`.')