################################################## # CsoundAPI.py # # Jeremy Flores, flores1@mit.edu # last modified 01/29/08 import socket import threading import select import os import thread #import curses from time import sleep from codes import * # where we store values for the message codes (i.e. CONNECTED=1, etc.) import gobject IO_MASKS = gobject.IO_IN | gobject.IO_PRI | gobject.IO_ERR | gobject.IO_HUP waiton_endflg = 0 class CsoundAPI(threading.Thread): def __init__(self, address='localhost', port=7000, is_gtk=True, timeout=5, error_callback = None): super(CsoundAPI, self).__init__() self._deactivate = False self._is_gtk = is_gtk # if it's going to be used in PyGTK, set this to True. If used from a terminal, set to False (to specify the thread type) self._error_callback = error_callback if str(address) != address: print "Error: address must be a string" return False if int(port) != port: print "Error: port must be an integer value" return False self._address = address self._port = port self._message_queue = [] # an ordered list of messages that we expect and the corresponding callbacks that will handle them when they arrive # HOST = '18.85.18.29' # The remote host # PORT = 42000 # The same port as used by the server # self.scratchSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # self.scratchSock.connect((HOST, PORT)) self._connect(timeout) self._waiton_endflg = 0 def _connect(self, timeout): print "Connecting to Csound..." self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._socket.settimeout(timeout) self._socket.connect( (self._address, self._port) ) self._isConnected = True if self._is_gtk == True: gobject.io_add_watch(self._socket, IO_MASKS, self.gtk_run) # this will monitor self._socket for inputs in a separate thread else: self.start() # this will call self.start() in a separate thread def gtk_run(self, socket, condition): data = '' try: data = socket.recv(8) if data != None: if len(data) != 0: self._parseReceived(data) else: print "**Csound server failed... disconnecting1" if self._error_callback != None: if self._deactivate == False: print "recover callback()" self._error_callback() else: print "ignore recover callback()" self._isConnected = False socket.close() else: print "**Csound server failed... disconnecting2" if self._error_callback != None: if self._deactivate == False: print "recover callback()" self._error_callback() else: print "ignore recover callback()" self._isConnected = False socket.close() except: pass return self._isConnected def run(self): data = '' while self._isConnected == True: try: data = self._socket.recv(8) if data != None: if len(data) != 0: self._parseReceived(data) else: print "**Csound server failed... disconnecting1" self._isConnected = False self._socket.close() else: print "**Csound server failed... disconnecting2" self._isConnected = False self._socket.close() except: pass def _parseReceived(self, buffer): recv_code = int(buffer[0:2]) size = int(buffer[3:8]) callback = self._error_callback # if we receive something from CS and our message queue doesn't know how to handle it, send the data to the error_callback fcn. for message in self._message_queue: # for each message in our message queue code = message[0] # look up the code for this particular message if code == recv_code: # if the type received matches the code recorded in our message callback = message[1] # redefine our callback from the default error_callback to the one recorded in our message if code != BUFTIMER and code != P2TIMER and code != UL_BEAT: # if not registered print code,callback self._message_queue.remove(message) # delete the message from our queue break # break the loop since we found the first message that corresponds to the received type try: data = self._socket.recv(size) # print 'Received data:',data if data != None: if callback != None: # if the callback has been registered callback(data) # execute the function that callback points to, passing to it the information contained in data if len(data) == 0: print "**Csound server failed... disconnecting3" self._isConnected = False self._socket.close() else: print "**Csound server failed... disconnecting4" self._isConnected = False self._socket.close() except: pass def newInstance(self): self._sendString("%2d %5d" %(NEW_INST, 0)) def setOrcName(self, str): self._sendString("%2d %5d" %(ORCFIL, len(str))) self._sendString(str) def setScoName(self, str): self._sendString("%2d %5d" %(SCOFIL, len(str))) self._sendString(str) def setMidiName(self, str): self._sendString("%2d %5d" %(MIDFIL, len(str))) self._sendString(str) def sendOrcStr(self, str): self._sendString("%2d %5d" %(ORCSTR, len(str))) self._sendString(str) def sendScoStr(self, str): self._sendString("%2d %5d" %(SCOSTR, len(str))) self._sendString(str) def setAlias(self, alias, val): str_val = str(val) self._sendString("%2d %5d" %(SET_ALIAS, len(alias))) self._sendString(alias) self._sendString("%2d" %(len(str_val))) self._sendString(str_val) def getAlias(self, str, callback): self._sendString("%2d %5d" %(GET_ALIAS, len(str))) self._sendString(str) self._message_queue.append([GET_ALIAS, callback]) def setVolume(self, val): str_val = str(val) self._sendString("%2d %5d" %(SET_VOLUME, len(str_val))) self._sendString(str_val) def getVolume(self, callback): self._sendString("%2d %5d" %(GET_VOLUME, 0)) self._message_queue.append([GET_VOLUME, callback]) def setMasterVolume(self, val): str_val = str(val) self._sendString("%2d %5d" %(SET_MAS_VOL, len(str_val))) self._sendString(str_val) def getMasterVolume(self, callback): self._sendString("%2d %5d" %(GET_MAS_VOL, 0)) self._message_queue.append([GET_MAS_VOL, callback]) def setTempo(self, val): str_val = str(val) self._sendString("%2d %5d" %(SET_TEMPO, len(str_val))) self._sendString(str_val) def getTempo(self, callback): self._sendString("%2d %5d" %(GET_TEMPO, 0)) self._message_queue.append([GET_TEMPO, callback]) def setMidiSpeed(self, val): str_val = str(val) self._sendString("%2d %5d" %(SET_MIDISPEED, len(str_val))) self._sendString(str_val) def getMidiSpeed(self, callback): self._sendString("%2d %5d" %(GET_MIDISPEED, 0)) self._message_queue.append([GET_MIDISPEED, callback]) def setMsgLevel(self, val): str_val = str(val) self._sendString("%2d %5d" %(SET_MSGLEVEL, len(str_val))) self._sendString(str_val) def getMsgLevel(self, callback): self._sendString("%2d %5d" %(GET_MSGLEVEL, 0)) self._message_queue.append([GET_MSGLEVEL, callback]) def setDisplays(self, val): str_val = str(val) self._sendString("%2d %5d" %(SET_DISPLAYS, len(str_val))) self._sendString(str_val) def setGraphics(self, val): str_val = str(val) self._sendString("%2d %5d" %(SET_GRAPHICS, len(str_val))) self._sendString(str_val) def sendBeat(self): self._sendString("%2d %5d" %(BEAT_EVT, 0)) def sendLinevt(self, str): self._sendString("%2d %5d" %(LINE_EVT, len(str))) self._sendString(str) def sendClearLines(self): self._sendString("%2d %5d" %(CLR_LINES, 0)) def setUploadBeat(self, val, callback): str_val = str(val) self._sendString("%2d %5d" %(UL_BEAT, len(str_val))) self._sendString(str_val) self._message_queue.append([UL_BEAT, callback]) def sendSVOpen(self): self._sendString("%2d %5d" %(SVOPEN, 0)) def setInsRemote(self, insno, IPadrs, instance): str_val = str(insno) self._sendString("%2d %5d" %(INSREMOTE, len(str_val))) self._sendString(str_val) self._sendString("%2d" %(len(IPadrs))) self._sendString(IPadrs) str_val = str(instance) self._sendString("%2d" %(len(str_val))) self._sendString(str_val) def setInsGlobal(self, insno, IPadrs, instance): str_val = str(insno) self._sendString("%2d %5d" %(INSGLOBAL, len(str_val))) self._sendString(str_val) self._sendString("%2d" %(len(IPadrs))) self._sendString(IPadrs) str_val = str(instance) self._sendString("%2d" %(len(str_val))) self._sendString(str_val) def setMidiRemote(self, chnum, IPadrs, instance): str_val = str(chnum) self._sendString("%2d %5d" %(MIDIREMOTE, len(str_val))) self._sendString(str_val) self._sendString("%2d" %(len(IPadrs))) self._sendString(IPadrs) str_val = str(instance) self._sendString("%2d" %(len(str_val))) self._sendString(str_val) def setMidiGlobal(self, chnum, IPadrs, instance): str_val = str(chnum) self._sendString("%2d %5d" %(MIDIGLOBAL, len(str_val))) self._sendString(str_val) self._sendString("%2d" %(len(IPadrs))) self._sendString(IPadrs) str_val = str(instance) self._sendString("%2d" %(len(str_val))) self._sendString(str_val) def setAudioRemoteSI(self, IPadrs): self._sendString("%2d %5d" %(AUDREMOTE_SI, len(IPadrs))) self._sendString(IPadrs) def setAudioGlobalSI(self, IPadrs): self._sendString("%2d %5d" %(AUDGLOBAL_SI, len(IPadrs))) self._sendString(IPadrs) def setAudioRemoteSO(self, IPadrs): self._sendString("%2d %5d" %(AUDREMOTE_SO, len(IPadrs))) self._sendString(IPadrs) def setAudioGlobalSO(self, IPadrs): self._sendString("%2d %5d" %(AUDGLOBAL_SO, len(IPadrs))) self._sendString(IPadrs) def setXtraScoreEvents(self, xtras): str_val = str(xtras) self._sendString("%2d %5d" %(XTRA_SCOREVT, len(str_val))) self._sendString(str_val) def setXtraMidiNoteOns(self, xtras): str_val = str(xtras) self._sendString("%2d %5d" %(XTRA_MIDINOTEON, len(str_val))) self._sendString(str_val) def setXtraMidiNoteOffs(self, xtras): str_val = str(xtras) self._sendString("%2d %5d" %(XTRA_MIDINOTEOFF, len(str_val))) self._sendString(str_val) def setXtraMidiEvents(self, xtras): str_val = str(xtras) self._sendString("%2d %5d" %(XTRA_MIDIEVT, len(str_val))) self._sendString(str_val) def setXtraMidiMessages(self, xtras): str_val = str(xtras) self._sendString("%2d %5d" %(XTRA_MIDIMSG, len(str_val))) self._sendString(str_val) def setXtraSysexMessages(self, xtras): str_val = str(xtras) self._sendString("%2d %5d" %(XTRA_SYSEXMSG, len(str_val))) self._sendString(str_val) def setXtraAudioBufsSI(self, xtras): str_val = str(xtras) self._sendString("%2d %5d" %(XTRA_AUDBUF_SI, len(str_val))) self._sendString(str_val) def setXtraAudioBufsSO(self, xtras): str_val = str(xtras) self._sendString("%2d %5d" %(XTRA_AUDBUF_SO, len(str_val))) self._sendString(str_val) def setNetStats(self, val1, val2): str_val = str(val1) self._sendString("%2d %5d" %(NET_STATS, len(str_val))) self._sendString(str_val) str_val = str(val2) self._sendString("%2d" %(len(str_val))) self._sendString(str_val) def sendPrintStats(self, str): self._sendString("%2d %5d" %(PRINT_STATS, len(str))) self._sendString(str) def setIOBufFrames(self, val): str_val = str(val) self._sendString("%2d %5d" %(IOBUFFRAMES, len(str_val))) self._sendString(str_val) def setBufTimer(self, val, callback): str_val = str(val) self._sendString("%2d %5d" %(BUFTIMER, len(str_val))) self._sendString(str_val) self._message_queue.append([BUFTIMER, callback]) def setP2Timer(self, val, callback): str_val = str(val) self._sendString("%2d %5d" %(P2TIMER, len(str_val))) self._sendString(str_val) self._message_queue.append([P2TIMER, callback]) def setOMaxLag(self, val): str_val = str(val) self._sendString("%2d %5d" %(OMAXLAGG, len(str_val))) self._sendString(str_val) def setInName(self, str): self._sendString("%2d %5d" %(INNAME, len(str))) self._sendString(str) def setOutName(self, str): self._sendString("%2d %5d" %(OUTNAME, len(str))) self._sendString(str) def setIntAudio(self): self._sendString("%2d %5d" %(INTAUDIO, 0)) def sendPrep(self): self._sendString("%2d %5d" %(PREP, 0)) def sendPlay(self): self._sendString("%2d %5d" %(PLAY, 0)) def sendPlayForTime(self, val): str_val = str(val) self._sendString("%2d %5d" %(PLAY_FOR_TIME, len(str_val))) self._sendString(str_val) def sendPause(self): self._sendString("%2d %5d" %(PAUSE, 0)) def sendResume(self): self._sendString("%2d %5d" %(RESUME, 0)) def sendPlayAll(self): self._sendString("%2d %5d" %(PLAYALL, 0)) def sendPlayAllForTime(self, val): str_val = str(val) self._sendString("%2d %5d" %(PLAYALL_FOR_TIME, len(str_val))) self._sendString(str_val) def sendPauseAll(self): self._sendString("%2d %5d" %(PAUSEALL, 0)) def sendResumeAll(self): self._sendString("%2d %5d" %(RESUMEALL, 0)) def sendDeactivateWithCallback(self, callback): self._sendString("%2d %5d" %(DEACTIVATE, 0)) self._message_queue.append([DEACTIVATE, callback]) def sendDeactivate(self): self._sendString("%2d %5d" %(DEACTIVATE, 0)) def sendReactivateWithCallback(self, callback): self._sendString("%2d %5d" %(REACTIVATE, 0)) self._message_queue.append([REACTIVATE, callback]) def sendReactivate(self): self._sendString("%2d %5d" %(REACTIVATE, 0)) def sendRewind(self): self._sendString("%2d %5d" %(REWIND1, 0)) def sendRewindAll(self): self._sendString("%2d %5d" %(REWINDALL, 0)) def sendFastFwdForTime(self, val): str_val = str(val) self._sendString("%2d %5d" %(FASTFWD_FOR_TIME, len(str_val))) self._sendString(str_val) def sendFastFwdToTime(self, val): str_val = str(val) self._sendString("%2d %5d" %(FASTFWD_TO_TIME, len(str_val))) self._sendString(str_val) def sendEcho(self, callback): print "send Echo" self._sendString("%2d %5d" %(ECHO, 0)) self._message_queue.append([ECHO, callback]) print "return, caller will wait for the echo-back" def sendDestroyWithCallback(self, callback): print "send Destroy with callback" self._sendString("%2d %5d" %(RELEASE_INST, 0)) self._message_queue.append([RELEASE_INST, callback]) print "return, caller will wait for the callback" def sendDestroy(self): # this releases the Cs instance print "send Destroy" self._sendString("%2d %5d" %(RELEASE_INST, 0)) self._message_queue.append([RELEASE_INST, release_inst_callback]) global release_inst_callback_flag release_inst_callback_flag = 1 print "sendDestroy() waiting for callback" while release_inst_callback_flag == 1: sleep(1) print "callback caught, exit sendDestroy" def setP2Timer(self, val, callback): str_val = str(val) self._sendString("%2d %5d" %(P2TIMER, len(str_val))) self._sendString(str_val) self._message_queue.append([P2TIMER, callback]) def sendDisconnect(self): # this releases the CL struct & closes the socket self._sendString("%2d %5d" %(CL_DISCONNECT, 0)) def serverCloseio(self): # just close the server current io print "api.serverCloseio()" self._sendString("%2d %5d" %(SV_CLOSEIO, 0)) def enterLineInputMode(self): #should be cleaned up to be threaded etc. i = True print "Line Input Mode" print "type score lines, or type 'exit'" while i: strinput = raw_input("") if (strinput == "exit"): i = False else: self.sendLinevt(strinput); print "end of Line Input mode" def enterBeatInputMode(self): #should be cleaned up to be threaded etc. stdscr = curses.initscr() # curses.start_color() curses.noecho() curses.cbreak() stdscr.keypad(1) # curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) # i = True # stdscr.addstr("Beat Input Mode\n", curses.color_pair(1)) # stdscr.addstr("press any button to enter a beat,\n", curses.color_pair(1)) # stdscr.addstr("or q to exit this mode\n", curses.color_pair(1)) # stdscr.refresh() while 1: c = stdscr.getch() if c == ord('q'): break else: self.sendBeat(); # stdscr.addstr("exited Beat Input mode\n", curses.color_pair(1)) # stdscr.refresh() curses.nocbreak(); stdscr.keypad(0); curses.echo() curses.endwin() def _sendString(self, string): try: self._socket.send(string) except socket.error, e: self._isConnected = False self._socket.close() # def addScratchCallback(self, callback): # self.callback = callback def close(self): print "Disconnecting from Csound..." self.sendDestroy() # this releases the CS object if self._is_gtk == False: self.sendDisconnect() # this closes the socket self._isConnected = False #should send a command to csound to shut down this socket #self._socket.shutdown(SHUT_RDWR) self._socket.close() def waitonEnd(self): self._sendString("%2d %5d" %(WAITON_END, 0)) self._message_queue.append([WAITON_END, endsignal]) global waiton_endflg waiton_endflg = 1 print "setting waiton_endflg" while waiton_endflg == 1: sleep(3) print "end signal caught, exit waitonEnd" def serverShutdown(self): # release ALL structs, close ALL sockets & shutdown the Server print "sending Server Shutdown" self._sendString("%2d %5d" %(SV_SHUTDOWN, 0)) # self._message_queue.append([SV_SHUTDOWN, "shutdown"]) # self._message_queue.append([SV_SHUTDOWN, shutdownsignal]) # global shutdown_endflg self._isConnected = False self._socket.close() #should send a command to csound to shut down this socket # shutdown_endflg = 1 # print "setting shutdown_endflg" # while shutdown_endflg == 1: # sleep(1) def after_gtk_destroy(self): print "CsoundAPI::after_gtk_destroy()" self.sendDisconnect() # this closes the socket self._isConnected = False #should send a command to csound to shut down this socket #self._socket.shutdown(SHUT_RDWR) self._socket.close() def mpClose(self): print "CsoundAPI::mpClose()" #self.sendDestroyWithCallback(self.destorysignal) #3sleep(1) #self.serverCloseio() #sleep(1) print "serverShutDown" self.serverShutdown() #self._isConnected = False #self._socket.close() #sleep(1) def destorysignal(self): print "got destroy callback" # self.serverShutdown() def release_inst_callback(self): print "release inst callback received" global release_inst_callback_flag release_inst_callback_flag = 0 def endsignal(self): print "got endsignal" print "clearing waiton_endflg" global waiton_endflg waiton_endflg = 0 def shutdownsignal(self): print "Cserver has shutdown" # global shutdown_endflg # shutdown_endflg = 0