import pygtk pygtk.require('2.0') import gtk from Util.Profiler import TP import gobject import time import shelve import os import Config from Util.ThemeWidgets import * from Util.CSoundClient import new_csound_client from SynthLab.SynthLabParametersWindow import SynthLabParametersWindow from SynthLab.SynthObjectsParameters import SynthObjectsParameters from SynthLab.SynthLabConstants import SynthLabConstants from SynthLab.Parameter import Parameter from Util.Trackpad import Trackpad from SubActivity import SubActivity Tooltips = Config.Tooltips as_window = False class SynthLabWindow(SubActivity): def __init__( self, set_mode, dummy_to_change_signature ): SubActivity.__init__(self, set_mode) if as_window: color = gtk.gdk.color_parse(Config.PANEL_BCK_COLOR) self.modify_bg(gtk.STATE_NORMAL, color) self.set_border_width(Config.MAIN_WINDOW_PADDING) self.set_keep_above(False) self.set_decorated(False) self.csnd = new_csound_client() self.trackpad = Trackpad( self ) self.synthObjectsParameters = SynthObjectsParameters() self.resetLocations() self.objectCount = len(self.locations) self.connections = [] self.initializeConnections() self.bounds = [] for i in range(self.objectCount): self.bounds.append([0,0,0,0]) self.updateBounds(i) self.instanceOpen = 0 self.recordWait = 0 self.recCount = 0 self.duration = 2 self.durString = '%.2f' % self.duration self.playingPitch = [] loopPointsTable = [] sample_names = [name for i in range( len( Config.INSTRUMENTS ) ) for name in Config.INSTRUMENTS.keys() if Config.INSTRUMENTS[ name ].instrumentId == i ] for inst in sample_names: loopStart = Config.INSTRUMENTS[ inst ].loopStart loopEnd = Config.INSTRUMENTS[ inst ].loopEnd crossDur = Config.INSTRUMENTS[ inst ].crossDur loopPointsTable.extend( [ loopStart, loopEnd, crossDur ] ) mess = "f5755 0 512 -2 " + " " .join([str(n) for n in loopPointsTable]) self.csnd.inputMessage( mess ) self.lineWidth = 3 self.lineWidthMUL2 = self.lineWidth*2 self.lineWidthMUL4 = self.lineWidth*4 self.lineWidthMUL4SQ = self.lineWidthMUL4*self.lineWidthMUL4 self.pix = 10 self.parameterOpen = 0 self.clockStart = 0 self.sample_names = [name for i in range( len( Config.INSTRUMENTS ) ) for name in Config.INSTRUMENTS.keys() if Config.INSTRUMENTS[ name ].instrumentId == i ] self.tooltips = gtk.Tooltips() if as_window: self.add_events(gtk.gdk.KEY_PRESS_MASK|gtk.gdk.KEY_RELEASE_MASK) self.action = None self.dragObject = None self.overWire = None self.overGate = None self.overGateObj = None self.overGateReject = False self.overGateSize = 32 self.overGateSizeDIV2 = self.overGateSize//2 self.overLineWidth = self.lineWidth*2 self.overLineWidthMUL2 = self.overLineWidth*2 self.gatePoint = SynthLabConstants.GATE_POINT self.gateMap = SynthLabConstants.GATE_MAP # look up gate type to find the matching gate type self.gateMatch = [ SynthLabConstants.GT_CONTROL_INPUT, SynthLabConstants.GT_CONTROL_OUTPUT, SynthLabConstants.GT_SOUND_INPUT, SynthLabConstants.GT_SOUND_OUTPUT ] # set up window if as_window: self.set_position( gtk.WIN_POS_CENTER_ON_PARENT ) self.set_title("Synth Lab") self.mainBox = gtk.VBox() self.subBox = gtk.HBox() self.drawingBox = RoundVBox( 10, Config.INST_BCK_COLOR ) self.drawingBox.set_border_width(Config.PANEL_SPACING) self.presetBox = RoundVBox( 10, Config.PANEL_COLOR ) self.presetBox.set_border_width(Config.PANEL_SPACING) self.presetBox.set_size_request(100, 790) self.subBox.pack_start(self.drawingBox, True, True) self.subBox.pack_start(self.presetBox, True, True) self.mainBox.pack_start(self.subBox) self.commandBox = gtk.HBox() self.sliderBox = RoundHBox( 10, Config.PANEL_COLOR ) self.sliderBox.set_border_width(Config.PANEL_SPACING) self.commandBox.pack_start(self.sliderBox) self.buttonBox = RoundHBox( 10, Config.PANEL_COLOR ) self.buttonBox.set_border_width(Config.PANEL_SPACING) self.commandBox.pack_start(self.buttonBox) self.mainBox.pack_start(self.commandBox) self.drawingAreaWidth = 1080 self.drawingAreaHeight = 790 self.separatorY = 690 self.clearMask = gtk.gdk.Rectangle(0,0,self.drawingAreaWidth,self.drawingAreaHeight) win = gtk.gdk.get_default_root_window() self.gc = gtk.gdk.GC( win ) self.gc.set_line_attributes( self.lineWidth, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER ) self.dirtyRectToAdd = gtk.gdk.Rectangle() self.dirty = False self.screenBuf = gtk.gdk.Pixmap( win, self.drawingAreaWidth, self.drawingAreaHeight ) self.screenBufDirtyRect = gtk.gdk.Rectangle() self.screenBufDirty = False self.drawingArea = gtk.DrawingArea() self.drawingArea.set_size_request( self.drawingAreaWidth, self.drawingAreaHeight ) self.col = gtk.gdk.color_parse(Config.INST_BCK_COLOR) colormap = self.drawingArea.get_colormap() self.bgColor = colormap.alloc_color( Config.INST_BCK_COLOR, True, True ) self.lineColor = colormap.alloc_color( Config.SL_LINE_COLOR, True, True ) self.overWireColor = colormap.alloc_color( Config.SL_OVER_WIRE_COLOR, True, True ) self.overGateColor = colormap.alloc_color( Config.SL_OVER_GATE_COLOR, True, True ) self.overGateRejectColor = colormap.alloc_color( Config.SL_OVER_GATE_REJECT_COLOR, True, True ) self.drawingArea.modify_bg(gtk.STATE_NORMAL, self.col) self.drawingArea.add_events( gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK | gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK ) self.drawingArea.connect( "button-press-event", self.handleButtonPress ) self.drawingArea.connect( "button-release-event", self.handleButtonRelease ) self.drawingArea.connect( "motion-notify-event", self.handleMotion ) self.drawingArea.connect("expose-event", self.draw) self.drawingBox.pack_start(self.drawingArea, False, False, 5) self.presets = self.initRadioButton(SynthLabConstants.PRESET, self.presetCallback, self.presetBox) self.durLabel = gtk.Image() self.durLabel.set_from_file(Config.IMAGE_ROOT + 'dur2.png') self.durAdjust = gtk.Adjustment(2, .5, 10, .01, .01, 0) self.durAdjust.connect("value-changed", self.handleDuration) self.durationSlider = ImageHScale( Config.TAM_TAM_ROOT + "/Resources/Images/sliderbutviolet.png", self.durAdjust, 7 ) self.durationSlider.connect("button-press-event", self.showParameter) self.durationSlider.connect("button-release-event", self.hideParameter) self.durationSlider.set_size_request(440, 30) self.sliderBox.pack_start(self.durationSlider, True, True, 5) self.sliderBox.pack_start(self.durLabel, False, padding=10) for i in [1,2,3,4,5,6]: recordButton = ImageToggleButton(Config.IMAGE_ROOT + 'synthRecord' + str(i) + '.png', Config.IMAGE_ROOT + 'synthRecord' + str(i) + 'Down.png', Config.IMAGE_ROOT + 'synthRecord' + str(i) + 'Over.png') recordButton.connect("clicked", self.recordSound, i) self.buttonBox.pack_start(recordButton, False, False, 2) saveButton = ImageButton(Config.IMAGE_ROOT + 'save.png') saveButton.connect("clicked", self.handleSave, None) self.buttonBox.pack_start(saveButton, False, False, 2) loadButton = ImageButton(Config.IMAGE_ROOT + 'load.png') loadButton.connect("clicked", self.handleLoad, None) self.buttonBox.pack_start(loadButton, False, False, 2) resetButton = ImageButton(Config.IMAGE_ROOT + 'reset.png') resetButton.connect("clicked", self.handleReset, None) self.buttonBox.pack_start(resetButton, False, False, 2) closeButton = ImageButton(Config.IMAGE_ROOT + 'close.png') closeButton.connect("clicked", self.handleClose, None) self.buttonBox.pack_start(closeButton, False, False, 2) self.tooltips.set_tip(saveButton, Tooltips.SAVE) self.tooltips.set_tip(loadButton, Tooltips.LOAD) self.tooltips.set_tip(resetButton, Tooltips.RESET) self.tooltips.set_tip(closeButton, Tooltips.CLOSE) self.add(self.mainBox) self.tooltips.set_tip(self.durationSlider, Tooltips.SOUNDDUR + ': ' + self.durString) tempFile = 'synthTemp' if tempFile in os.listdir(Config.PREF_DIR): self.handleLoadTemp() else: self.presetCallback(self.presets,0) self.show_all() def onDestroy(self): pass def onKeyPress(self,widget,event): key = event.hardware_keycode if key not in Config.KEY_MAP: return midiPitch = Config.KEY_MAP[key] if midiPitch not in self.playingPitch: if self.recordWait == 0: self.playingPitch.append( midiPitch ) self.playNote( midiPitch, 0 ) else: self.recordWait = 0 self.playingPitch.append( midiPitch ) self.playNote( midiPitch, self.table ) self.waitRecording() def resetRecord( self ): gobject.source_remove( self.wait ) self.recordButton.set_active(False) inst = 'lab' + str(self.table-85) self.csnd.load_synth_instrument(inst) self.table = 0 return True def waitRecording(self): self.wait = gobject.timeout_add(int(self.duration*1000) , self.resetRecord ) def onKeyRelease( self, widget, event ): key = event.hardware_keycode if key not in Config.KEY_MAP: return midiPitch = Config.KEY_MAP[key] if midiPitch in self.playingPitch: self.playingPitch.remove( midiPitch ) def handleDuration( self, data ): self.duration = self.durAdjust.value self.durString = '%.2f' % self.duration img = int((self.duration - .5) * .5 + 1) self.durLabel.set_from_file(Config.IMAGE_ROOT + 'dur' + str(img) + '.png') self.parameterUpdate(self.durString) self.tooltips.set_tip(self.durationSlider, Tooltips.SOUNDDUR + ': ' + self.durString) def showParameter( self, widget, data=None ): if not self.parameterOpen: self.parameter = Parameter(self.durString) self.parameterOpen = 1 def hideParameter( self, widget, data=None ): if self.parameterOpen and not self.clockStart: self.windowCloseDelay = gobject.timeout_add(500, self.closeParameterWindow) self.clockStart = 1 def closeParameterWindow( self ): if self.parameterOpen: self.parameter.hide() self.parameterOpen = 0 gobject.source_remove( self.windowCloseDelay ) self.clockStart = 0 self.tooltips.set_tip(self.durationSlider, Tooltips.SOUNDDUR + ': ' + self.durString) return True def parameterUpdate( self, durString ): if self.parameterOpen: self.parameter.update(durString) def playNote( self, midiPitch, table ): cpsPitch = 261.626*pow(1.0594633, midiPitch-36) self.recCount += 1 mess = "i5203." + str(self.recCount) + " 0 " + str(self.duration) + " " + str(cpsPitch) + " " + str(table) + " " + " " .join([str(n) for n in self.synthObjectsParameters.getOutputParameters()]) self.csnd.inputMessage( mess ) if self.recCount >= 9: self.recCount = 0 def handleClose( self, widget, data ): if self.instanceOpen: self.synthLabParametersWindow.destroy() self.set_mode('welcome') if as_window: self.set_keep_above(False) self.hide() def resetLocations( self ): # deep copy the list self.locations = [ loc[:] for loc in SynthLabConstants.INIT_LOCATIONS ] def handleReset( self, widget, data ): self.resetLocations() self.objectCount = len(self.locations) for i in range(self.objectCount): self.updateBounds( i ) self.duration = 2 self.durAdjust.set_value(self.duration) self.connections = [] self.synthObjectsParameters.__init__() self.writeTables( self.synthObjectsParameters.types, self.synthObjectsParameters.controlsParameters, self.synthObjectsParameters.sourcesParameters, self.synthObjectsParameters.fxsParameters ) self.synthObjectsParameters.update() self.initializeConnections() self.invalidate_rect( 0, 0, self.drawingAreaWidth, self.drawingAreaHeight ) time.sleep(.01) self.controlToSrcConnections() time.sleep(.01) self.controlToFxConnections() time.sleep(.01) self.audioConnections() time.sleep(.01) def setAction( self, action ): self.action = action def doneAction( self ): if self.action == "drag-object": self.doneDragObject() self.action = None def handleButtonRelease( self, widget, event ): self.highlightWire( None ) self.highlightGate( None ) if self.action == "drag-object": self.doneAction() elif self.action == "draw-wire": for i in range(self.objectCount): if self.bounds[i][0] < event.x < self.bounds[i][2] and self.bounds[i][1] < event.y < self.bounds[i][3]: if i == self.wireObj: break gate = self.testGates( i, event.x-self.locations[i][0], event.y-self.locations[i][1] ) if gate: self.connectWire( i, gate ) break # if we don't connect the wire here they can try to click it somewhere, so don't end the action def handleButtonPress( self, widget, event): self.clickLoc = (int(event.x),int(event.y)) self.highlightWire( None ) self.highlightGate( None ) if event.button == 1: for i in range(self.objectCount): if self.bounds[i][0] < event.x < self.bounds[i][2] and self.bounds[i][1] < event.y < self.bounds[i][3]: gate = self.testGates( i, event.x-self.locations[i][0], event.y-self.locations[i][1] ) if gate: if self.action == "draw-wire": self.connectWire( i, gate ) else: self.startWire( i, gate ) else: if self.action == "draw-wire": self.doneWire() if i != self.objectCount-1: self.startDragObject( i ) return if self.action == "draw-wire": # didn't hit anything self.doneWire() else: # check if we clicked a wire i = self.wireUnderLoc( event.x, event.y ) if i >= 0: self.deleteWire( i ) elif event.button == 3: for i in range(self.objectCount): if self.bounds[i][0] < event.x < self.bounds[i][2] and self.bounds[i][1] < event.y < self.bounds[i][3]: if self.instanceOpen: self.synthLabParametersWindow.destroy() self.synthLabParametersWindow = SynthLabParametersWindow( i, self.synthObjectsParameters, self.writeTables, self.playNote ) self.instanceOpen = 1 def handleMotion( self, widget, event ): if event.is_hint: x, y, state = widget.window.get_pointer() event.x = float(x) event.y = float(y) event.state = state if self.action == "drag-object": self.updateDragObject( int(event.x), int(event.y) ) elif self.action == "draw-wire": self.updateWire( int(event.x), int(event.y) ) for i in range(self.objectCount): if self.locations[i] == SynthLabConstants.INIT_LOCATIONS[i] \ and i != self.objectCount-1: continue if self.bounds[i][0] < event.x < self.bounds[i][2] and self.bounds[i][1] < event.y < self.bounds[i][3]: gate = self.testGates( i, event.x-self.locations[i][0], event.y-self.locations[i][1] ) if gate and gate != self.wireGate: if gate[0] == SynthLabConstants.GT_CONTROL_OUTPUT or gate[0] == SynthLabConstants.GT_SOUND_OUTPUT: ok = self.testConnection( i, gate, self.wireObj, self.wireGate ) else: ok = self.testConnection( self.wireObj, self.wireGate, i, gate ) self.highlightGate( i, gate, not ok ) else: self.highlightGate( None ) return self.highlightGate( None ) else: # check for mouse overs for i in range(self.objectCount): if self.locations[i] == SynthLabConstants.INIT_LOCATIONS[i] \ and i != self.objectCount-1: continue if self.bounds[i][0] < event.x < self.bounds[i][2] and self.bounds[i][1] < event.y < self.bounds[i][3]: gate = self.testGates( i, event.x-self.locations[i][0], event.y-self.locations[i][1] ) if gate: self.highlightGate( i, gate ) else: self.highlightGate( None ) if self.parameterOpen: self.parameter.hide() self.parameterOpen = 0 self.highlightWire( None ) return # didn't find a gate self.highlightGate( None ) if self.parameterOpen: self.parameter.hide() self.parameterOpen = 0 # check for wires i = self.wireUnderLoc( event.x, event.y ) if i >= 0: self.highlightWire( i ) else: self.highlightWire( None ) def testGates( self, i, x, y ): oT = i >> 2 for gT in range(len(self.gateMap[oT])): for n in range(len(self.gateMap[oT][gT])): if self.gateMap[oT][gT][n][0] <= x <= self.gateMap[oT][gT][n][2] \ and self.gateMap[oT][gT][n][1] <= y <= self.gateMap[oT][gT][n][3]: return ( gT, n, self.gateMap[oT][gT][n][4], self.gatePoint[oT][gT][n] ) # type, index, wire loc, center loc return False def startWire( self, obj, gate ): self.wireObj = obj self.wireGate = gate x = gate[2][0] + self.locations[obj][0] y = gate[2][1] + self.locations[obj][1] self.wirePoint = [ [ x, y ], [ x, y ] ] self.wireRect = [ 0, 0, 0, 0 ] self.setAction( "draw-wire" ) def updateWire( self, x, y ): if x < 0: x = 0 elif x > self.drawingAreaWidth: x = self.drawingAreaWidth if y < 0: y = 0 elif y > self.separatorY: y = self.separatorY self.invalidate_rect( self.wireRect[0], self.wireRect[1], self.wireRect[2], self.wireRect[3], False ) if x < self.wirePoint[0][0]: self.wireRect[0], self.wireRect[2] = x-self.lineWidth, self.wirePoint[0][0]-x+self.lineWidthMUL2 else: self.wireRect[0], self.wireRect[2] = self.wirePoint[0][0]-self.lineWidth, x-self.wirePoint[0][0]+self.lineWidthMUL2 if y < self.wirePoint[0][1]: self.wireRect[1], self.wireRect[3] = y-self.lineWidth, self.wirePoint[0][1]-y+self.lineWidthMUL2 else: self.wireRect[1], self.wireRect[3] = self.wirePoint[0][1]-self.lineWidth, y-self.wirePoint[0][1]+self.lineWidthMUL2 self.wirePoint[1][0] = x self.wirePoint[1][1] = y self.invalidate_rect( self.wireRect[0], self.wireRect[1], self.wireRect[2], self.wireRect[3], False ) def connectWire( self, obj, gate ): if gate[0] == SynthLabConstants.GT_CONTROL_OUTPUT or gate[0] == SynthLabConstants.GT_SOUND_OUTPUT: bObj, eObj = obj, self.wireObj bGate, eGate = gate, self.wireGate else: bObj, eObj = self.wireObj, obj bGate, eGate = self.wireGate, gate i = self.newConnection( bObj, bGate, eObj, eGate ) if i >= 0: # successful connection self.invalidate_rect( self.cBounds[i][0], self.cBounds[i][1], self.cBounds[i][2], self.cBounds[i][3] ) self.doneWire() def deleteWire( self, i ): self.invalidate_rect( self.cBounds[i][0], self.cBounds[i][1], self.cBounds[i][2], self.cBounds[i][3] ) self.delConnection( i ) def doneWire( self ): self.invalidate_rect( self.wireRect[0], self.wireRect[1], self.wireRect[2], self.wireRect[3] ) self.doneAction() def wireUnderLoc( self, x, y ): for i in range(len(self.connections)): if x < self.cBounds[i][0] or x > self.cBounds[i][4]: continue if y < self.cBounds[i][1] or y > self.cBounds[i][5]: continue if self.cPoints[i][0] == self.cPoints[i][2]: # vertical line if abs(x-self.cPoints[i][0]) < self.lineWidthMUL4: return i else: slope = (self.cPoints[i][3]-self.cPoints[i][1])/float(self.cPoints[i][2]-self.cPoints[i][0]) if abs(slope) < 1: yy = self.cPoints[i][1] + (x-self.cPoints[i][0])*slope if abs(y-yy) < self.lineWidthMUL4: return i else: xx = self.cPoints[i][0] + (y-self.cPoints[i][1])/slope if abs(x-xx) < self.lineWidthMUL4: return i return -1 # nothing found # pass in i = None to clear def highlightWire( self, i ): if self.overWire != i: if self.overWire != None: self.invalidate_rect( self.cBounds[self.overWire][0], self.cBounds[self.overWire][1], self.cBounds[self.overWire][2], self.cBounds[self.overWire][3] ) self.overWire = i if self.overWire != None: self.invalidate_rect( self.cBounds[self.overWire][0], self.cBounds[self.overWire][1], self.cBounds[self.overWire][2], self.cBounds[self.overWire][3] ) # pass in obj = None to clear def highlightGate( self, obj, gate = None, reject = False ): if self.overGateObj != obj or self.overGate != gate or self.overGateReject != reject: if self.overGate != None: self.invalidate_rect( self.overGateLoc[0], self.overGateLoc[1], self.overGateSize, self.overGateSize ) self.overGateObj = obj self.overGate = gate self.overGateReject = reject if self.overGate != None: oT = self.overGateObj//4 x = self.locations[self.overGateObj][0] + self.overGate[3][0] - self.overGateSizeDIV2 y = self.locations[self.overGateObj][1] + self.overGate[3][1] - self.overGateSizeDIV2 self.overGateLoc = ( x, y ) self.invalidate_rect( self.overGateLoc[0], self.overGateLoc[1], self.overGateSize, self.overGateSize ) if obj != 12: choosen = SynthLabConstants.CHOOSE_TYPE[obj/4][self.typesTable[obj]] str = Tooltips.SYNTHTYPES[obj/4][self.typesTable[obj]] + ': ' + Tooltips.SYNTHPARA[choosen][gate[1]] if gate[0] == 1: if self.parameterOpen: self.parameterUpdate( str ) else: self.parameter = Parameter( str ) self.parameterOpen = 1 def startDragObject( self, i ): self.dragObject = i self.dragInitialLoc = (self.locations[i][0],self.locations[i][1]) self.potentialDisconnect = False self.invalidate_rect( self.bounds[i][0], self.bounds[i][1], SynthLabConstants.PIC_SIZE, SynthLabConstants.PIC_SIZE ) for i in self.outputMap[self.dragObject]: self.invalidate_rect( self.cBounds[i][0], self.cBounds[i][1], self.cBounds[i][2], self.cBounds[i][3] ) for i in self.inputMap[self.dragObject]: self.invalidate_rect( self.cBounds[i][0], self.cBounds[i][1], self.cBounds[i][2], self.cBounds[i][3] ) self.setAction( "drag-object" ) def updateDragObject( self, x, y ): delta = [ x-self.clickLoc[0], y-self.clickLoc[1] ] x = self.dragInitialLoc[0]+delta[0] if x-SynthLabConstants.HALF_SIZE < 0: x = SynthLabConstants.HALF_SIZE elif x+SynthLabConstants.HALF_SIZE > self.drawingAreaWidth: x = self.drawingAreaWidth - SynthLabConstants.HALF_SIZE y = self.dragInitialLoc[1]+delta[1] if y-SynthLabConstants.HALF_SIZE < 0: y = SynthLabConstants.HALF_SIZE elif y+SynthLabConstants.HALF_SIZE > self.drawingAreaHeight: y = self.drawingAreaHeight - SynthLabConstants.HALF_SIZE self.invalidate_rect(self.bounds[self.dragObject][0], self.bounds[self.dragObject][1], SynthLabConstants.PIC_SIZE, SynthLabConstants.PIC_SIZE, False ) if not self.potentialDisconnect: for i in self.outputMap[self.dragObject]: self.invalidate_rect( self.cBounds[i][0], self.cBounds[i][1], self.cBounds[i][2], self.cBounds[i][3], False ) for i in self.inputMap[self.dragObject]: self.invalidate_rect( self.cBounds[i][0], self.cBounds[i][1], self.cBounds[i][2], self.cBounds[i][3], False ) if y > self.separatorY: self.potentialDisconnect = True else: self.potentialDisconnect = False self.locations[self.dragObject][0] = int( x ) self.locations[self.dragObject][1] = int( y ) self.updateBounds(self.dragObject) self.invalidate_rect(self.bounds[self.dragObject][0], self.bounds[self.dragObject][1], SynthLabConstants.PIC_SIZE, SynthLabConstants.PIC_SIZE, False ) if not self.potentialDisconnect: for i in self.outputMap[self.dragObject]: self.invalidate_rect( self.cBounds[i][0], self.cBounds[i][1], self.cBounds[i][2], self.cBounds[i][3], False ) for i in self.inputMap[self.dragObject]: self.invalidate_rect( self.cBounds[i][0], self.cBounds[i][1], self.cBounds[i][2], self.cBounds[i][3], False ) def doneDragObject( self ): if self.potentialDisconnect: self.invalidate_rect( self.bounds[self.dragObject][0], self.bounds[self.dragObject][1], SynthLabConstants.PIC_SIZE, SynthLabConstants.PIC_SIZE, False ) m = self.outputMap[self.dragObject][:] m.sort(reverse=True) for i in m: self.delConnection( i ) m = self.inputMap[self.dragObject][:] m.sort(reverse=True) for i in m: self.delConnection( i ) self.locations[self.dragObject][0] = SynthLabConstants.INIT_LOCATIONS[self.dragObject][0] self.locations[self.dragObject][1] = SynthLabConstants.INIT_LOCATIONS[self.dragObject][1] self.updateBounds( self.dragObject ) self.invalidate_rect(self.bounds[self.dragObject][0], self.bounds[self.dragObject][1], SynthLabConstants.PIC_SIZE, SynthLabConstants.PIC_SIZE ) else: self.invalidate_rect( self.bounds[self.dragObject][0], self.bounds[self.dragObject][1], SynthLabConstants.PIC_SIZE, SynthLabConstants.PIC_SIZE ) for i in self.outputMap[self.dragObject]: self.invalidate_rect( self.cBounds[i][0], self.cBounds[i][1], self.cBounds[i][2], self.cBounds[i][3] ) for i in self.inputMap[self.dragObject]: self.invalidate_rect( self.cBounds[i][0], self.cBounds[i][1], self.cBounds[i][2], self.cBounds[i][3] ) self.dragObject = None self.handleSaveTemp() def updateBounds( self, i ): self.bounds[i][0] = self.locations[i][0]-SynthLabConstants.HALF_SIZE self.bounds[i][1] = self.locations[i][1]-SynthLabConstants.HALF_SIZE self.bounds[i][2] = self.locations[i][0]+SynthLabConstants.HALF_SIZE self.bounds[i][3] = self.locations[i][1]+SynthLabConstants.HALF_SIZE for c in self.outputMap[i]: self.updateConnection( c ) for c in self.inputMap[i]: self.updateConnection( c ) def updateConnection( self, i ): c = self.connections[i] oT = c[0][0]//4 x1 = self.locations[c[0][0]][0] + self.gateMap[oT][c[0][1]][c[0][2]][4][0] y1 = self.locations[c[0][0]][1] + self.gateMap[oT][c[0][1]][c[0][2]][4][1] oT = c[1][0]//4 x2 = self.locations[c[1][0]][0] + self.gateMap[oT][c[1][1]][c[1][2]][4][0] y2 = self.locations[c[1][0]][1] + self.gateMap[oT][c[1][1]][c[1][2]][4][1] self.cPoints[i][0], self.cPoints[i][1], self.cPoints[i][2], self.cPoints[i][3] = ( x1, y1, x2, y2 ) if x1 > x2: x1, x2 = ( x2, x1 ) if y1 > y2: y1, y2 = ( y2, y1 ) self.cBounds[i][0], self.cBounds[i][1], self.cBounds[i][2], self.cBounds[i][3], self.cBounds[i][4], self.cBounds[i][5] = ( x1-self.lineWidth, y1-self.lineWidth, x2-x1+self.lineWidthMUL2, y2-y1+self.lineWidthMUL2, x2+self.lineWidth, y2+self.lineWidth ) def findRecursive( self, obj, target ): if obj == target: return True for c in self.outputMap[obj]: if self.findRecursive( self.straightConnections[c][1], target ): return True return False def testConnection( self, bObj, bGate, eObj, eGate ): if self.gateMatch[bGate[0]] != eGate[0]: return False # type mismatch for c in self.inputMap[eObj]: if self.connections[c][1][1] == eGate[0] \ and self.connections[c][1][2] == eGate[1] : # same type and port if self.connections[c][0][0] == bObj: return False # connections already exists if self.findRecursive( eObj, bObj ): return False # loop return True def newConnection( self, bObj, bGate, eObj, eGate ): if not self.testConnection( bObj, bGate, eObj, eGate ): return -1 # connection failed ind = len(self.connections) # connection format: [ ( outputObject, gate type, gate num ), ( inputObject, gate type, gate num ) ] self.connections.append ( [ ( bObj, bGate[0], bGate[1] ), ( eObj, eGate[0], eGate[1] ) ] ) self.straightConnections.append( ( bObj, eObj ) ) self.outputMap[bObj].append(ind) self.inputMap[eObj].append(ind) self.outputs.append( bObj ) self.cPoints.append( [ 0, 0, 0, 0 ] ) self.cBounds.append( [ 0, 0, 0, 0, 0, 0 ] ) self.updateConnection( ind ) self.updateSound() self.handleSaveTemp() return ind def delConnection( self, i ): b = self.straightConnections[i][0] e = self.straightConnections[i][1] self.straightConnections.pop(i) self.outputMap[b].remove(i) self.inputMap[e].remove(i) self.outputs.pop(i) self.cPoints.pop(i) self.cBounds.pop(i) self.connections.pop(i) for o in range(self.objectCount): for m in range(len(self.outputMap[o])): if self.outputMap[o][m] > i: self.outputMap[o][m] -= 1 for m in range(len(self.inputMap[o])): if self.inputMap[o][m] > i: self.inputMap[o][m] -= 1 self.updateSound() self.handleSaveTemp() def initializeConnections( self ): self.straightConnections = [] self.outputMap = [ [] for i in self.locations ] self.inputMap = [ [] for i in self.locations ] self.outputs = [] self.cPoints = [] self.cBounds = [] for i in range(len(self.connections)): c = self.connections[i] first = c[0][0] second = c[1][0] self.straightConnections.append([first, second]) self.outputMap[first].append(i) self.inputMap[second].append(i) self.outputs.append(first) self.cPoints.append( [ 0, 0, 0, 0 ] ) self.cBounds.append( [ 0, 0, 0, 0, 0, 0 ] ) self.updateConnection( i ) self.updateSound() def predraw( self, buf ): startX = self.screenBufDirtyRect.x startY = self.screenBufDirtyRect.y stopX = self.screenBufDirtyRect.x + self.screenBufDirtyRect.width stopY = self.screenBufDirtyRect.y + self.screenBufDirtyRect.height # draw bg self.gc.foreground = self.bgColor buf.draw_rectangle( self.gc, True, startX, startY, self.screenBufDirtyRect.width, self.screenBufDirtyRect.height ) # draw separator self.gc.foreground = self.lineColor buf.draw_line( self.gc, startX, self.separatorY, stopX, self.separatorY ) # draw objects self.gc.set_clip_mask( self.clipMask ) for i in range(self.objectCount): if i == self.dragObject: continue if startX > self.bounds[i][2] or stopX < self.bounds[i][0] or startY > self.bounds[i][3] or stopY < self.bounds[i][1]: continue type = i >> 2 self.gc.set_clip_origin( self.bounds[i][0]-SynthLabConstants.PIC_SIZE*type, self.bounds[i][1] ) buf.draw_drawable( self.gc, self.pixmap[i], 0, 0, self.bounds[i][0], self.bounds[i][1], SynthLabConstants.PIC_SIZE, SynthLabConstants.PIC_SIZE ) self.gc.set_clip_rectangle( self.clearMask ) # draw wires for c in range(len(self.connections)): if self.straightConnections[c][0] == self.dragObject or self.straightConnections[c][1] == self.dragObject: continue if startX > self.cBounds[c][4] or stopX < self.cBounds[c][0] or startY > self.cBounds[c][5] or stopY < self.cBounds[c][1]: continue buf.draw_line( self.gc, self.cPoints[c][0], self.cPoints[c][1], self.cPoints[c][2], self.cPoints[c][3] ) self.screenBufDirty = False def draw( self, widget, event ): #TP.ProfileBegin("SL::draw") startX = event.area.x startY = event.area.y stopX = event.area.x + event.area.width stopY = event.area.y + event.area.height self.gc.foreground = self.lineColor if self.screenBufDirty: self.predraw( self.screenBuf ) # draw base widget.window.draw_drawable( self.gc, self.screenBuf, startX, startY, startX, startY, event.area.width, event.area.height ) if self.action == "drag-object": # draw dragObject self.gc.set_clip_mask( self.clipMask ) type = self.dragObject >> 2 self.gc.set_clip_origin( self.bounds[self.dragObject][0]-SynthLabConstants.PIC_SIZE*type, self.bounds[self.dragObject][1] ) widget.window.draw_drawable( self.gc, self.pixmap[self.dragObject], 0, 0, self.bounds[self.dragObject][0], self.bounds[self.dragObject][1], SynthLabConstants.PIC_SIZE, SynthLabConstants.PIC_SIZE ) self.gc.set_clip_rectangle( self.clearMask ) # draw wires if not self.potentialDisconnect: for c in self.outputMap[self.dragObject]: if startX > self.cBounds[c][4] or stopX < self.cBounds[c][0] or startY > self.cBounds[c][5] or stopY < self.cBounds[c][1]: continue widget.window.draw_line( self.gc, self.cPoints[c][0], self.cPoints[c][1], self.cPoints[c][2], self.cPoints[c][3] ) for c in self.inputMap[self.dragObject]: if startX > self.cBounds[c][4] or stopX < self.cBounds[c][0] or startY > self.cBounds[c][5] or stopY < self.cBounds[c][1]: continue widget.window.draw_line( self.gc, self.cPoints[c][0], self.cPoints[c][1], self.cPoints[c][2], self.cPoints[c][3] ) elif self.action == "draw-wire": # draw the wire widget.window.draw_line( self.gc, self.wirePoint[0][0], self.wirePoint[0][1], self.wirePoint[1][0], self.wirePoint[1][1] ) # draw highlights if self.overWire != None: self.gc.foreground = self.overWireColor widget.window.draw_line( self.gc, self.cPoints[self.overWire][0], self.cPoints[self.overWire][1], self.cPoints[self.overWire][2], self.cPoints[self.overWire][3] ) elif self.overGate != None: self.gc.set_line_attributes( self.overLineWidth, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER ) if self.overGateReject: self.gc.foreground = self.overGateRejectColor widget.window.draw_line( self.gc, self.overGateLoc[0]+self.overLineWidth, self.overGateLoc[1]+self.overLineWidth, self.overGateLoc[0]+self.overGateSize-self.overLineWidth, self.overGateLoc[1]+self.overGateSize-self.overLineWidth ) widget.window.draw_line( self.gc, self.overGateLoc[0]+self.overLineWidth, self.overGateLoc[1]+self.overGateSize-self.overLineWidth, self.overGateLoc[0]+self.overGateSize-self.overLineWidth, self.overGateLoc[1]+self.overLineWidth ) else: self.gc.foreground = self.overGateColor widget.window.draw_arc( self.gc, False, self.overGateLoc[0]+self.overLineWidth, self.overGateLoc[1]+self.overLineWidth, self.overGateSize-self.overLineWidthMUL2, self.overGateSize-self.overLineWidthMUL2, 0, 23040 ) self.gc.set_line_attributes( self.lineWidth, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER ) #print TP.ProfileEndAndPrint("SL::draw") return True def invalidate_rect( self, x, y, w, h, dirtyScreenBuf = True ): self.dirtyRectToAdd.x = x self.dirtyRectToAdd.y = y self.dirtyRectToAdd.width = w self.dirtyRectToAdd.height = h if dirtyScreenBuf: if self.screenBufDirty: self.screenBufDirtyRect = self.screenBufDirtyRect.union(self.dirtyRectToAdd) else: self.screenBufDirtyRect.x = x self.screenBufDirtyRect.y = y self.screenBufDirtyRect.width = w self.screenBufDirtyRect.height = h self.screenBufDirty = True if self.drawingArea.window != None: self.drawingArea.window.invalidate_rect( self.dirtyRectToAdd, True ) self.dirty = True def writeTables( self, typesTable, controlParametersTable, sourceParametersTable, fxParametersTable ): mess = 'f5200 0 16 -2 ' + " ".join([str(n) for n in controlParametersTable]) self.csnd.inputMessage( mess ) time.sleep(0.005) mess = "f5201 0 16 -2 " + " " .join([str(n) for n in sourceParametersTable]) self.csnd.inputMessage( mess ) time.sleep(.005) mess = "f5202 0 16 -2 " + " " .join([str(n) for n in fxParametersTable]) self.csnd.inputMessage( mess ) time.sleep(.005) self.typesTable = typesTable lastTable = [0]*12 for i in range(12): if i in self.outputs: lastTable[i] = (typesTable[i]+1) mess = "f5203 0 16 -2 " + " " .join([str(n) for n in lastTable]) + " 0 0 0 0" self.csnd.inputMessage( mess ) time.sleep(.005) if lastTable[4] == 8: snd = Config.SOUNDS_DIR + '/' + self.sample_names[int(sourceParametersTable[1])] mess = "f5501 0 32768 -1 " + "\"%s\" 0 0 0" % snd self.csnd.inputMessage( mess ) if lastTable[5] == 8: snd = Config.SOUNDS_DIR + '/' + self.sample_names[int(sourceParametersTable[5])] mess = "f5502 0 32768 -1 " + "\"%s\" 0 0 0" % snd self.csnd.inputMessage( mess ) if lastTable[6] == 8: snd = Config.SOUNDS_DIR + '/' + self.sample_names[int(sourceParametersTable[9])] mess = "f5503 0 32768 -1 " + "\"%s\" 0 0 0" % snd self.csnd.inputMessage( mess ) if lastTable[7] == 8: snd = Config.SOUNDS_DIR + '/' + self.sample_names[int(sourceParametersTable[13])] mess = "f5504 0 32768 -1 " + "\"%s\" 0 0 0" % snd self.csnd.inputMessage( mess ) time.sleep(.005) self.loadPixmaps(typesTable) self.invalidate_rect( 0, 0, self.drawingAreaWidth, self.drawingAreaHeight ) def recordSound( self, widget, data ): if widget.get_active() == True: self.recordButton = widget self.recordWait = 1 os.system('rm ' + Config.PREF_DIR + '/lab' + str(data)) self.table = 85 + data else: self.recordWait = 0 def updateSound( self ): self.controlToSrcConnections() time.sleep(.01) self.controlToFxConnections() time.sleep(.01) self.audioConnections() time.sleep(.01) lastTable = [0]*12 for i in range(12): if i in self.outputs: lastTable[i] = (self.synthObjectsParameters.types[i]+1) mess = "f5203 0 16 -2 " + " " .join([str(n) for n in lastTable]) + " 0 0 0 0" self.csnd.inputMessage( mess ) time.sleep(.01) def controlToSrcConnections( self ): self.contSrcConnections = [] for i in self.connections: if i[0][0] < 4 and 3 < i[1][0] < 8: offset = i[1][2] self.contSrcConnections.append([i[0][0], i[1][0], offset]) table = [0 for i in range(16)] sources = [source for source in range(4,8) if source in self.outputs] for source in sources: for entre in range(4): value = sum([2**(li[0]+1) for li in self.contSrcConnections if li[1] == source and li[2] == entre], 1) table[(source % 4) * 4 + entre] = value mess = "f5204 0 16 -2 " + " " .join([str(n) for n in table]) self.csnd.inputMessage( mess ) def controlToFxConnections( self ): self.contFxConnections = [] for i in self.connections: if i[0][0] < 4 and 7 < i[1][0] < 12: offset = i[1][2] self.contFxConnections.append([i[0][0], i[1][0], offset]) table = [0 for i in range(16)] fxs = [fx for fx in range(8,12) if fx in self.outputs] for fx in fxs: for entre in range(4): value = sum([2**(li[0]+1) for li in self.contFxConnections if li[1] == fx and li[2] == entre], 1) table[(fx % 4) * 4 + entre] = value mess = "f5205 0 16 -2 " + " " .join([str(n) for n in table]) self.csnd.inputMessage( mess ) def audioConnections( self ): self.srcFxConnections = [i for i in self.straightConnections if 3 < i[0] < 8 and 7 < i[1] < 12] self.fxConnections = [i for i in self.straightConnections if 7 < i[0] < 12 and 7 < i[1] < 12] self.outConnections = [i[0] for i in self.straightConnections if i[1] == 12] table = [] for fx in range(8, 12): value = 0 for li in self.srcFxConnections: if li[1] == fx: value += pow(2, li[0]-4) table.append(value) for fx in range(8, 12): value = 0 for li in self.fxConnections: if li[1] == fx: value += pow(2, li[0]-8) table.append(value) for sig in range(4, 12): value = 0 if sig in self.outConnections: value = 1 table.append(value) mess = "f5206 0 16 -2 " + " " .join([str(n) for n in table]) self.csnd.inputMessage( mess ) def loadPixmaps( self, typesList ): win = gtk.gdk.get_default_root_window() gc = gtk.gdk.GC( win ) gc.foreground = self.bgColor self.pixmap = [] for i in range(13): if i < 4: img = SynthLabConstants.CHOOSE_TYPE_PLUS[0][typesList[i]] elif i < 8: img = SynthLabConstants.CHOOSE_TYPE_PLUS[1][typesList[i]] elif i < 12: img = SynthLabConstants.CHOOSE_TYPE_PLUS[2][typesList[i]] else: img = "speaker" pix = gtk.gdk.pixbuf_new_from_file(Config.IMAGE_ROOT + img + '.png') map = gtk.gdk.Pixmap( win, pix.get_width(), pix.get_height() ) map.draw_rectangle( gc, True, 0, 0, pix.get_width(), pix.get_height() ) map.draw_pixbuf( gc, pix, 0, 0, 0, 0, pix.get_width(), pix.get_height(), gtk.gdk.RGB_DITHER_NONE ) self.pixmap.append(map) pix = gtk.gdk.pixbuf_new_from_file(Config.IMAGE_ROOT+'synthlabMask.png') pixels = pix.get_pixels() stride = pix.get_rowstride() channels = pix.get_n_channels() bitmap = "" byte = 0 shift = 0 for j in range(pix.get_height()): offset = stride*j for i in range(pix.get_width()): r = pixels[i*channels+offset] if r != "\0": byte += 1 << shift shift += 1 if shift > 7: bitmap += "%c" % byte byte = 0 shift = 0 bitmap += "%c" % byte self.clipMask = gtk.gdk.bitmap_create_from_data( None, bitmap, pix.get_width(), pix.get_height() ) def handleSave(self, widget, data): chooser = gtk.FileChooserDialog(title=None,action=gtk.FILE_CHOOSER_ACTION_SAVE, buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_SAVE,gtk.RESPONSE_OK)) if chooser.run() == gtk.RESPONSE_OK: try: print 'INFO: save SynthLab file %s' % chooser.get_filename() f = shelve.open( chooser.get_filename(), 'n') self.saveState(f) f.close() except IOError: print 'ERROR: failed to save SynthLab to file %s' % chooser.get_filename() chooser.destroy() def handleLoad(self, widget, data): chooser = gtk.FileChooserDialog(title=None,action=gtk.FILE_CHOOSER_ACTION_OPEN, buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK)) if chooser.run() == gtk.RESPONSE_OK: try: print 'INFO: load SynthLab state from file %s' % chooser.get_filename() f = shelve.open( chooser.get_filename(), 'r') self.loadState(f) f.close() except IOError: print 'ERROR: failed to load SynthLab state from file %s' % chooser.get_filename() chooser.destroy() def handleSaveTemp( self ): file = Config.PREF_DIR + '/synthTemp' f = shelve.open(file, 'n') self.saveState(f) f.close() def handleLoadTemp( self ): file = Config.PREF_DIR + '/synthTemp' f = shelve.open(file, 'r') self.loadState(f) f.close() def saveState( self, state ): state['types'] = self.synthObjectsParameters.types state['controls'] = self.synthObjectsParameters.controlsParameters state['sources'] = self.synthObjectsParameters.sourcesParameters state['fxs'] = self.synthObjectsParameters.fxsParameters state['envelope'] = self.synthObjectsParameters.outputParameters state['locations'] = self.locations state['connections'] = self.connections state['duration'] = self.duration def tempVerifyLocations(self): for l in self.locations: l[0] = int(l[0]) l[1] = int(l[1]) def tempVerifyConnectionFormat(self): for c in self.connections: if c[0][1] > 3 or c[0][2] > 3 \ or c[1][1] > 3 or c[1][2] > 3: print "old format" print c i = c[0] if i[1] == 0 and i[2] == 40: if i[0] < 4: t,n = 0,0 # control output else: t,n = 2,0 # sound output else: print "unhandled loc" t,n = i[1],i[2] c[0] = ( c[0][0], t, n ) i = c[1] if i[1] == 0 and i[2] == -40: t,n = 3,0 elif i[1] == 40 and i[2] == -19: t,n = 1,0 elif i[1] == -25 and i[2] == -40: t,n = 1,0 elif i[1] == -9 and i[2] == -40: t,n = 1,1 elif i[1] == 8 and i[2] == -40: t,n = 1,2 elif i[1] == 25 and i[2] == -40: t,n = 1,3 else: print "unhandled loc" t,n = i[1],i[2] c[1] = ( c[1][0], t, n ) def loadState( self, state ): self.synthObjectsParameters.types = state['types'] self.synthObjectsParameters.controlsParameters = state['controls'] self.synthObjectsParameters.sourcesParameters = state['sources'] self.synthObjectsParameters.fxsParameters = state['fxs'] self.synthObjectsParameters.outputParameters = state['envelope'] self.locations = state['locations'] #self.tempVerifyLocations() self.objectCount = len(self.locations) for i in range(self.objectCount): self.updateBounds( i ) self.connections = state['connections'] #self.tempVerifyConnectionFormat() self.duration = state['duration'] self.durAdjust.set_value(self.duration) self.initializeConnections() self.controlToSrcConnections() time.sleep(.01) self.controlToFxConnections() time.sleep(.01) self.audioConnections() time.sleep(.01) self.synthObjectsParameters.update() self.writeTables( self.synthObjectsParameters.types, self.synthObjectsParameters.controlsParameters, self.synthObjectsParameters.sourcesParameters, self.synthObjectsParameters.fxsParameters ) time.sleep(.01) self.invalidate_rect( 0, 0, self.drawingAreaWidth, self.drawingAreaHeight ) def presetCallback( self, widget, data ): preset = 'synthFile' + str(data+1) f = shelve.open( Config.TAM_TAM_ROOT + '/Resources/SynthFiles/' + preset, 'r') self.loadState(f) f.close() self.handleSaveTemp() def initRadioButton( self, labelList, methodCallback, box ): for i in range( len( labelList ) ): label = labelList[i] if i == 0: button = ImageRadioButton( group = None, mainImg_path = Config.IMAGE_ROOT + label + '.png', altImg_path = Config.IMAGE_ROOT + label + 'sel.png' ) else: button = ImageRadioButton( group = button, mainImg_path = Config.IMAGE_ROOT + label + '.png', altImg_path = Config.IMAGE_ROOT + label + 'sel.png' ) button.connect( "toggled", methodCallback, i ) box.pack_start( button, True, True )