diff options
author | amartin <olpc@localhost.localdomain> | 2007-01-31 06:28:53 (GMT) |
---|---|---|
committer | amartin <olpc@localhost.localdomain> | 2007-01-31 06:28:53 (GMT) |
commit | 46d07b755b7a53610f6b88a98a2e170410668ba3 (patch) | |
tree | fe81b1f416db3e1312f4a4d7f63d468c80d2e96e | |
parent | 8f6117e6876047931a035d3058a5b725a720c318 (diff) |
NoteDB
-rw-r--r-- | Config.py | 1 | ||||
-rw-r--r--[-rwxr-xr-x] | Edit/HitInterface.py | 117 | ||||
-rw-r--r-- | Edit/MainWindow.py | 1074 | ||||
-rw-r--r--[-rwxr-xr-x] | Edit/NoteInterface.py | 184 | ||||
-rw-r--r-- | Edit/TrackInterface.py | 499 | ||||
-rw-r--r-- | Edit/TuneInterface.py | 248 | ||||
-rw-r--r-- | Util/NoteDB.py | 461 |
7 files changed, 1270 insertions, 1314 deletions
@@ -481,6 +481,7 @@ MAXIMUM_PITCH_DRUM = MINIMUM_PITCH_DRUM + PITCH_STEP_DRUM*(NUMBER_OF_POSSIBLE_PI MINIMUM_NOTE_DURATION = 1 # ticks MS_PER_MINUTE = 60000.0 TICKS_PER_BEAT = 12 +MAXIMUM_BEATS = 12 # maximum beats per page NUMBER_OF_TRACKS = 5 NUMBER_OF_PAGES = 2 diff --git a/Edit/HitInterface.py b/Edit/HitInterface.py index 3eac69f..1301a33 100755..100644 --- a/Edit/HitInterface.py +++ b/Edit/HitInterface.py @@ -2,87 +2,62 @@ import pygtk pygtk.require( '2.0' ) import gtk +from Util.NoteDB import PARAMETER from Edit.NoteInterface import NoteInterface import Config class HitInterface( NoteInterface ): - def __init__( self, parent, page, track, note, pitch, onset, duration, amplitude, image, imageSelected, colors ): - NoteInterface.__init__( self, parent, page, track, note, pitch, onset, duration, amplitude, image, imageSelected, colors ) + def __init__( self, noteDB, owner, note ): + NoteInterface.__init__( self, noteDB, owner, note ) - self.width = self.height = Config.HIT_HEIGHT + self.width = self.height = Config.HIT_HEIGHT self.imgWidth = self.imgHeight = Config.HIT_HEIGHT + Config.HIT_IMAGE_PADDING_MUL2 self.firstTransform = True self.updateTransform() - def updateParams( self, pitch, onset, duration, amplitude): - self.pitch = pitch - self.onset = onset - self.duration = duration - self.end = onset + duration - - self.amplitude = amplitude - r = self.baseColors[0][0] + int(self.baseColors[1][0]*amplitude) - g = self.baseColors[0][1] + int(self.baseColors[1][1]*amplitude) - b = self.baseColors[0][2] + int(self.baseColors[1][2]*amplitude) - self.color = self.parent.drawingArea.get_colormap().alloc_color( r, g, b, True, True ) - - self.updateTransform() - - def getId( self ): - return self.note - - def getStartTick( self ): - return self.onset - - def getEndTick( self ): - return self.end - - def testOnset( self, start, stop ): - return self.onset >= start and self.onset < stop - def updateTransform( self ): - if self.page == self.parent.curPage and not self.firstTransform: + if self.note.page == self.owner.curPage and not self.firstTransform: oldX = self.imgX oldY = self.imgY oldEndX = self.imgX + self.imgWidth - if self.onset != self.oldOnset: - self.x = self.parent.ticksToPixels( self.onset ) + if self.note.cs.onset != self.oldOnset: + self.x = self.owner.ticksToPixels( self.noteDB.getPage(self.note.page).beats, self.note.cs.onset ) self.x += self.origin[0] self.imgX = self.x - Config.NOTE_IMAGE_PADDING - self.oldOnset = self.onset - if self.pitch != self.oldPitch: - self.y = self.parent.pitchToPixelsDrum( self.pitch ) + self.origin[1] + self.oldOnset = self.note.cs.onset + if self.note.cs.pitch != self.oldPitch: + self.y = self.owner.pitchToPixelsDrum( self.note.cs.pitch ) + self.origin[1] self.imgY = self.y - Config.NOTE_IMAGE_PADDING - self.oldPitch = self.pitch - - if self.page == self.parent.curPage: + self.oldPitch = self.note.cs.pitch + + if self.note.page == self.owner.curPage: if self.firstTransform: - self.parent.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.page ) + self.owner.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page ) self.firstTransform = False else: x = min( self.imgX, oldX ) y = min( self.imgY, oldY ) endx = max( self.imgX + self.imgWidth, oldEndX ) endy = max( self.imgY, oldY ) + self.imgHeight - self.parent.invalidate_rect( x, y, endx-x, endy-y, self.page ) + self.owner.invalidate_rect( x, y, endx-x, endy-y, self.note.page ) def updateDragLimits( self, dragLimits, leftBound, rightBound, widthBound, maxRightBound ): - left = 0 - self.onset - right = maxRightBound - self.duration - self.onset - up = Config.MAXIMUM_PITCH_DRUM - self.pitch - down = Config.MINIMUM_PITCH_DRUM - self.pitch - + left = 0 - self.note.cs.onset + right = maxRightBound - self.note.cs.duration - self.note.cs.onset + up = Config.MAXIMUM_PITCH_DRUM - self.note.cs.pitch + down = Config.MINIMUM_PITCH_DRUM - self.note.cs.pitch + if dragLimits[0][0] < left: dragLimits[0][0] = left if dragLimits[0][1] > right: dragLimits[0][1] = right if dragLimits[1][0] < down: dragLimits[1][0] = down if dragLimits[1][1] > up: dragLimits[1][1] = up - + # store the current loc as a reference point - self.baseOnset = self.onset - self.basePitch = self.pitch + self.baseOnset = self.note.cs.onset + self.basePitch = self.note.cs.pitch #======================================================= # Events @@ -97,62 +72,54 @@ class HitInterface( NoteInterface ): return -1 # event occurs before us, no point in checking further if eX > self.width: return 0 # no X overlap - + eY = event.y - self.y if eY < 0 or eY > self.height: return 0 # not a hit - + if event.button == 3: - print "Show some note parameters!?!" - #self.noteParameters = NoteParametersWindow( self.note, self.getNoteParameters ) + print "Show some note parameters!?!" + #self.noteParameters = NoteParametersWindow( self.note, self.getNoteParameters ) return 1 # handled if event.type == gtk.gdk._2BUTTON_PRESS: # select bar self.potentialDeselect = False start = 0 - check = self.onset - Config.TICKS_PER_BEAT + check = self.note.cs.onset - Config.TICKS_PER_BEAT while start <= check: start += Config.TICKS_PER_BEAT stop = start + Config.TICKS_PER_BEAT - check += self.duration + check += 1 while stop < check: stop += Config.TICKS_PER_BEAT - emitter.selectNotesByBar( self.track, start, stop ) + emitter.selectNotesByBar( self.note.track, start, stop ) elif event.type == gtk.gdk._3BUTTON_PRESS: # select track self.potentialDeselect = False - emitter.selectNotesByTrack( self.track ) + emitter.selectNotesByTrack( self.note.track ) else: if self.getSelected(): # we already selected, might want to delected self.potentialDeselect = True else: - emitter.selectNotes( { self.track: [ self ] } ) - self.updateSampleNote( self.pitch ) - + emitter.selectNotes( { self.note.track: [ self ] } ) + self.updateSampleNote( self.note.cs.pitch ) + percent = eX/self.width if percent < 0.5: emitter.setCurrentAction( "note-drag-onset", self ) else: emitter.setCurrentAction( "note-drag-pitch-drum", self ) - + return 1 def noteDrag( self, emitter, do, dp, dd ): self.potentialDeselect = False - changed = False if do != self.lastDragO: self.lastDragO = do - self.onset = self.baseOnset + do - self.end = self.onset + self.duration - changed = True + self.noteDB.updateNote( self.note.page, self.note.track, self.note.id, PARAMETER.ONSET, self.baseOnset + do ) + self.end = self.note.cs.onset + self.note.cs.duration if dp != self.lastDragP and not dp%2: self.lastDragP = dp newPitch = self.basePitch + dp - self.pitch = newPitch + self.noteDB.updateNote( self.note.page, self.note.track, self.note.id, PARAMETER.PITCH, newPitch ) self.updateSampleNote( newPitch ) - changed = True - - self.updateTransform() - - if changed: return (self.note, self.pitch, self.onset, self.duration ) - else: return False # updateTooltip returns: # -1, event occurs before us so don't bother checking any later notes @@ -164,15 +131,15 @@ class HitInterface( NoteInterface ): return -1 # event occurs before us, no point in checking further if eX > self.width: return 0 # no X overlap - + eY = event.y - self.y if eY < 0 or eY > self.height: return 0 # not a hit - + percent = eX/self.width if percent < 0.5: emitter.setCursor("drag-onset") else: emitter.setCursor("drag-pitch") - + return 1 # we handled it #======================================================= @@ -190,4 +157,4 @@ class HitInterface( NoteInterface ): win.draw_pixbuf( gc, img, 0, 0, self.imgX, self.imgY, self.imgWidth, self.imgHeight, gtk.gdk.RGB_DITHER_NONE ) return True # we drew something - + diff --git a/Edit/MainWindow.py b/Edit/MainWindow.py index 504fecf..f99e62e 100644 --- a/Edit/MainWindow.py +++ b/Edit/MainWindow.py @@ -1,6 +1,6 @@ import pygtk pygtk.require( '2.0' ) -import gtk +import gtk import gobject from Util.ThemeWidgets import * @@ -17,8 +17,9 @@ import Config from Edit.MixerWindow import MixerWindow from Generation.GenerationConstants import GenerationConstants from Generation.GenerationParametersWindow import GenerationParametersWindow -from Edit.TrackInterface import TrackInterface -from Edit.TuneInterface import TuneInterface +from Util.NoteDB import NoteDB +from Edit.TrackInterface import TrackInterface, TrackInterfaceParasite +from Edit.TuneInterface import TuneInterface, TuneInterfaceParasite from Util.Profiler import TP @@ -28,9 +29,40 @@ from Generation.Generator import generator1, variate # The main TamTam window #----------------------------------- class MainWindow( gtk.EventBox ): - + def __init__( self, CSoundClient ): self.csnd = CSoundClient + + def init_data( ): + self._data = {} + + #[ volume, ... ] + self._data['track_volume'] = [ Config.DEFAULT_VOLUME ] * Config.NUMBER_OF_TRACKS + self._data['track_mute'] = [ 1.0 ] * Config.NUMBER_OF_TRACKS + + #[ instrument index, ... ] + track_inst = [ + Config.FLUTE, + Config.KOTO, + Config.GAM, + Config.GUIT, + Config.DRUM1KIT ] + if len(track_inst) != Config.NUMBER_OF_TRACKS: raise 'error' + + self._data['track_inst'] = track_inst + [Config.FLUTE] * (Config.NUMBER_OF_TRACKS - len( track_inst) ) + #{ pageId: { [track 0 = note list], [track 2 = note list], ... ] } + npages = 40 + nbeats = 4 + + self._data['volume'] = Config.DEFAULT_VOLUME + self._data['page_beats'] = [nbeats for p in range(npages)] + self._data['tempo'] = Config.PLAYER_TEMPO + + self.trackSelected = [ 0 for i in range(Config.NUMBER_OF_TRACKS) ] + + self.noteDB = NoteDB() + self.noteDB.addListener( self, page=True ) # register for page notifications + def formatRoundBox( box, fillcolor ): box.set_radius( 7 ) box.set_border_width( 1 ) @@ -41,426 +73,232 @@ class MainWindow( gtk.EventBox ): def init_GUI(): self.GUI = {} self.GUI["2main"] = gtk.HBox() - + def track_menu(trackId, lbl): instrumentMenuItem = gtk.MenuItem( lbl ) instrumentMenu = gtk.Menu() instrumentMenuItem.set_submenu( instrumentMenu ) - + instrumentNames = [ k for k in Config.INSTRUMENTS.keys() if k[0:4] != 'drum' ] + ['drum1kit'] instrumentNames.sort() for i in instrumentNames: menuItem = gtk.MenuItem( i ) menuItem.connect_object( "activate", self.handleInstrumentChanged, ( trackId, i ) ) instrumentMenu.append( menuItem ) - + instrumentMenuBar = gtk.MenuBar() instrumentMenuBar.append( instrumentMenuItem ) return instrumentMenuBar - + #------------------------------------------------------------------------- # left panel self.GUI["2leftPanel"] = gtk.VBox() self.GUI["2leftPanel"].set_size_request( 137, -1 ) - # + instrument panel - self.GUI["2instrumentPanel"] = gtk.VBox() - # + + instrument 1 box - self.GUI["2instrument1Box"] = formatRoundBox( RoundHBox(), "#6C9790" ) - self.GUI["2instrument1Box"].set_size_request( -1, 137 ) - self.GUI["2instrument1volumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][1], 0, 100, 1, 1, 0 ) - self.GUI["2instrument1volumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 0 ) - self.GUI["2instrument1volumeSlider"] = ImageVScale( Config.IMAGE_ROOT+"sliderInst1.png", self.GUI["2instrument1volumeAdjustment"], 6 ) - self.GUI["2instrument1volumeSlider"].set_inverted(True) - self.GUI["2instrument1volumeSlider"].set_size_request( 30, -1 ) - self.GUI["2instrument1volumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 0 ) - self.GUI["2instrument1Box"].pack_start( self.GUI["2instrument1volumeSlider"], False, False, 0 ) - #self.GUI["2instrument1Button"] = gtk.Button("Inst 1") - #self.GUI["2instrument1Box"].pack_start( self.GUI["2instrument1Button"] ) - self.GUI["2instrument1Box"].pack_start( track_menu(0,'?') ) - self.GUI["2instrumentPanel"].pack_start( self.GUI["2instrument1Box"] ) - # + + instrument 2 box - self.GUI["2instrument2Box"] = formatRoundBox( RoundHBox(), "#6C9790" ) - self.GUI["2instrument2Box"].set_size_request( -1, 137 ) - self.GUI["2instrument2volumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][1], 0, 100, 1, 1, 0 ) - self.GUI["2instrument2volumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 1 ) - self.GUI["2instrument2volumeSlider"] = ImageVScale( Config.IMAGE_ROOT+"sliderInst2.png", self.GUI["2instrument2volumeAdjustment"], 6 ) - self.GUI["2instrument2volumeSlider"].set_inverted(True) - self.GUI["2instrument2volumeSlider"].set_size_request( 30, -1 ) - self.GUI["2instrument2volumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 1 ) - self.GUI["2instrument2Box"].pack_start( self.GUI["2instrument2volumeSlider"], False, False, 0 ) - #self.GUI["2instrument2Button"] = gtk.Button("Inst 2") - #self.GUI["2instrument2Box"].pack_start( self.GUI["2instrument2Button"] ) - self.GUI["2instrument2Box"].pack_start( track_menu(1,'?') ) - self.GUI["2instrumentPanel"].pack_start( self.GUI["2instrument2Box"] ) - # + + instrument 3 box - self.GUI["2instrument3Box"] = formatRoundBox( RoundHBox(), "#6C9790" ) - self.GUI["2instrument3Box"].set_size_request( -1, 137 ) - self.GUI["2instrument3volumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][2], 0, 100, 1, 1, 0 ) - self.GUI["2instrument3volumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 2 ) - self.GUI["2instrument3volumeSlider"] = ImageVScale( Config.IMAGE_ROOT+"sliderInst3.png", self.GUI["2instrument3volumeAdjustment"], 6 ) - self.GUI["2instrument3volumeSlider"].set_inverted(True) - self.GUI["2instrument3volumeSlider"].set_size_request( 30, -1 ) - self.GUI["2instrument3volumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 2 ) - self.GUI["2instrument3Box"].pack_start( self.GUI["2instrument3volumeSlider"], False, False, 0 ) - #self.GUI["2instrument3Button"] = gtk.Button("Inst 3") - #self.GUI["2instrument3Box"].pack_start( self.GUI["2instrument3Button"] ) - self.GUI["2instrument3Box"].pack_start( track_menu(2,'?') ) - self.GUI["2instrumentPanel"].pack_start( self.GUI["2instrument3Box"] ) - # + + instrument 4 box - self.GUI["2instrument4Box"] = formatRoundBox( RoundHBox(), "#6C9790" ) - self.GUI["2instrument4Box"].set_size_request( -1, 137 ) - self.GUI["2instrument4volumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][3], 0, 100, 1, 1, 0 ) - self.GUI["2instrument4volumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 3 ) - self.GUI["2instrument4volumeSlider"] = ImageVScale( Config.IMAGE_ROOT+"sliderInst4.png", self.GUI["2instrument4volumeAdjustment"], 6 ) - self.GUI["2instrument4volumeSlider"].set_inverted(True) - self.GUI["2instrument4volumeSlider"].set_size_request( 30, -1 ) - self.GUI["2instrument4volumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 3 ) - self.GUI["2instrument4Box"].pack_start( self.GUI["2instrument4volumeSlider"], False, False, 0 ) - #self.GUI["2instrument4Button"] = gtk.Button("Inst 4") - #self.GUI["2instrument4Box"].pack_start( self.GUI["2instrument4Button"] ) - self.GUI["2instrument4Box"].pack_start( track_menu(3,'?') ) - self.GUI["2instrumentPanel"].pack_start( self.GUI["2instrument4Box"] ) - # + + drum box - self.GUI["2drumBox"] = formatRoundBox( RoundHBox(), "#6C9790" ) - self.GUI["2drumBox"].set_size_request( -1, 165 ) - self.GUI["2drumvolumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][4], 0, 100, 1, 1, 0 ) - self.GUI["2drumvolumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 4 ) - self.GUI["2drumvolumeSlider"] = ImageVScale( Config.IMAGE_ROOT+"sliderDrum.png", self.GUI["2drumvolumeAdjustment"], 6 ) - self.GUI["2drumvolumeSlider"].set_inverted(True) - self.GUI["2drumvolumeSlider"].set_size_request( 30, -1 ) - self.GUI["2drumvolumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 4 ) - self.GUI["2drumBox"].pack_start( self.GUI["2drumvolumeSlider"], False, False, 0 ) - self.GUI["2drumButton"] = gtk.Button("?") - self.GUI["2drumBox"].pack_start( self.GUI["2drumButton"] ) - #self.GUI["2instrument1Box"].pack_start( track_menu(4,'?') ) - self.GUI["2instrumentPanel"].pack_start( self.GUI["2drumBox"] ) - self.GUI["2leftPanel"].pack_start( self.GUI["2instrumentPanel"], False ) - # + volume panel - self.GUI["2volumePanel"] = formatRoundBox( RoundHBox(), "#6C9790" ) - # + + volume box - self.GUI["2volumeBox"] = gtk.VBox() - self.GUI["2volumeImage"] = gtk.Image() - self.GUI["2volumeImage"].set_from_file( Config.IMAGE_ROOT+"volume2.png" ) - self.GUI["2volumeBox"].pack_start( self.GUI["2volumeImage"], False ) - self.GUI["2volumeAdjustment"] = gtk.Adjustment( self._data["volume"], 0, 100, 1, 1, 0 ) - self.GUI["2volumeSlider"] = ImageVScale( Config.IMAGE_ROOT+"sliderEditVolume.png", self.GUI["2volumeAdjustment"], 6 ) - self.GUI["2volumeSlider"].set_inverted(True) - self.GUI["2volumeAdjustment"].connect( "value-changed", self.handleVolume ) - self.GUI["2volumeBox"].pack_start( self.GUI["2volumeSlider"] ) - self.GUI["2volumePanel"].pack_start( self.GUI["2volumeBox"] ) - # + + tempo box - self.GUI["2tempoBox"] = gtk.VBox() - self.GUI["2tempoImage"] = gtk.Image() - self.GUI["2tempoImage"].set_from_file( Config.IMAGE_ROOT+"tempo3.png" ) - self.GUI["2tempoBox"].pack_start( self.GUI["2tempoImage"], False ) - self.GUI["2tempoAdjustment"] = gtk.Adjustment( self._data["tempo"], 40, 240, 1, 1, 0 ) - self.GUI["2tempoSlider"] = ImageVScale( Config.IMAGE_ROOT+"sliderEditTempo.png", self.GUI["2tempoAdjustment"], 6 ) - self.GUI["2tempoSlider"].set_inverted(True) - self.GUI["2tempoAdjustment"].connect( "value-changed", self.handleTempo ) - self.GUI["2tempoBox"].pack_start( self.GUI["2tempoSlider"] ) - self.GUI["2volumePanel"].pack_start( self.GUI["2tempoBox"] ) - self.GUI["2leftPanel"].pack_start( self.GUI["2volumePanel"] ) - self.GUI["2main"].pack_start( self.GUI["2leftPanel"], False ) - + if 1: # + instrument panel + self.GUI["2instrumentPanel"] = gtk.VBox() + # + + instrument 1 box + self.GUI["2instrument1Box"] = formatRoundBox( RoundHBox(), "#6C9790" ) + self.GUI["2instrument1Box"].set_size_request( -1, 137 ) + self.GUI["2instrument1volumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][1], 0, 100, 1, 1, 0 ) + self.GUI["2instrument1volumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 0 ) + self.GUI["2instrument1volumeSlider"] = ImageVScale( Config.IMAGE_ROOT+"sliderInst1.png", self.GUI["2instrument1volumeAdjustment"], 6 ) + self.GUI["2instrument1volumeSlider"].set_inverted(True) + self.GUI["2instrument1volumeSlider"].set_size_request( 30, -1 ) + self.GUI["2instrument1volumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 0 ) + self.GUI["2instrument1Box"].pack_start( self.GUI["2instrument1volumeSlider"], False, False, 0 ) + #self.GUI["2instrument1Button"] = gtk.Button("Inst 1") + #self.GUI["2instrument1Box"].pack_start( self.GUI["2instrument1Button"] ) + self.GUI["2instrument1Box"].pack_start( track_menu(0,'?') ) + self.GUI["2instrumentPanel"].pack_start( self.GUI["2instrument1Box"] ) + # + + instrument 2 box + self.GUI["2instrument2Box"] = formatRoundBox( RoundHBox(), "#6C9790" ) + self.GUI["2instrument2Box"].set_size_request( -1, 137 ) + self.GUI["2instrument2volumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][1], 0, 100, 1, 1, 0 ) + self.GUI["2instrument2volumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 1 ) + self.GUI["2instrument2volumeSlider"] = ImageVScale( Config.IMAGE_ROOT+"sliderInst2.png", self.GUI["2instrument2volumeAdjustment"], 6 ) + self.GUI["2instrument2volumeSlider"].set_inverted(True) + self.GUI["2instrument2volumeSlider"].set_size_request( 30, -1 ) + self.GUI["2instrument2volumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 1 ) + self.GUI["2instrument2Box"].pack_start( self.GUI["2instrument2volumeSlider"], False, False, 0 ) + #self.GUI["2instrument2Button"] = gtk.Button("Inst 2") + #self.GUI["2instrument2Box"].pack_start( self.GUI["2instrument2Button"] ) + self.GUI["2instrument2Box"].pack_start( track_menu(1,'?') ) + self.GUI["2instrumentPanel"].pack_start( self.GUI["2instrument2Box"] ) + # + + instrument 3 box + self.GUI["2instrument3Box"] = formatRoundBox( RoundHBox(), "#6C9790" ) + self.GUI["2instrument3Box"].set_size_request( -1, 137 ) + self.GUI["2instrument3volumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][2], 0, 100, 1, 1, 0 ) + self.GUI["2instrument3volumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 2 ) + self.GUI["2instrument3volumeSlider"] = ImageVScale( Config.IMAGE_ROOT+"sliderInst3.png", self.GUI["2instrument3volumeAdjustment"], 6 ) + self.GUI["2instrument3volumeSlider"].set_inverted(True) + self.GUI["2instrument3volumeSlider"].set_size_request( 30, -1 ) + self.GUI["2instrument3volumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 2 ) + self.GUI["2instrument3Box"].pack_start( self.GUI["2instrument3volumeSlider"], False, False, 0 ) + #self.GUI["2instrument3Button"] = gtk.Button("Inst 3") + #self.GUI["2instrument3Box"].pack_start( self.GUI["2instrument3Button"] ) + self.GUI["2instrument3Box"].pack_start( track_menu(2,'?') ) + self.GUI["2instrumentPanel"].pack_start( self.GUI["2instrument3Box"] ) + # + + instrument 4 box + self.GUI["2instrument4Box"] = formatRoundBox( RoundHBox(), "#6C9790" ) + self.GUI["2instrument4Box"].set_size_request( -1, 137 ) + self.GUI["2instrument4volumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][3], 0, 100, 1, 1, 0 ) + self.GUI["2instrument4volumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 3 ) + self.GUI["2instrument4volumeSlider"] = ImageVScale( Config.IMAGE_ROOT+"sliderInst4.png", self.GUI["2instrument4volumeAdjustment"], 6 ) + self.GUI["2instrument4volumeSlider"].set_inverted(True) + self.GUI["2instrument4volumeSlider"].set_size_request( 30, -1 ) + self.GUI["2instrument4volumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 3 ) + self.GUI["2instrument4Box"].pack_start( self.GUI["2instrument4volumeSlider"], False, False, 0 ) + #self.GUI["2instrument4Button"] = gtk.Button("Inst 4") + #self.GUI["2instrument4Box"].pack_start( self.GUI["2instrument4Button"] ) + self.GUI["2instrument4Box"].pack_start( track_menu(3,'?') ) + self.GUI["2instrumentPanel"].pack_start( self.GUI["2instrument4Box"] ) + # + + drum box + self.GUI["2drumBox"] = formatRoundBox( RoundHBox(), "#6C9790" ) + self.GUI["2drumBox"].set_size_request( -1, 165 ) + self.GUI["2drumvolumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][4], 0, 100, 1, 1, 0 ) + self.GUI["2drumvolumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 4 ) + self.GUI["2drumvolumeSlider"] = ImageVScale( Config.IMAGE_ROOT+"sliderDrum.png", self.GUI["2drumvolumeAdjustment"], 6 ) + self.GUI["2drumvolumeSlider"].set_inverted(True) + self.GUI["2drumvolumeSlider"].set_size_request( 30, -1 ) + self.GUI["2drumvolumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 4 ) + self.GUI["2drumBox"].pack_start( self.GUI["2drumvolumeSlider"], False, False, 0 ) + self.GUI["2drumButton"] = gtk.Button("?") + self.GUI["2drumBox"].pack_start( self.GUI["2drumButton"] ) + #self.GUI["2instrument1Box"].pack_start( track_menu(4,'?') ) + self.GUI["2instrumentPanel"].pack_start( self.GUI["2drumBox"] ) + self.GUI["2leftPanel"].pack_start( self.GUI["2instrumentPanel"], False ) + # + volume panel + self.GUI["2volumePanel"] = formatRoundBox( RoundHBox(), "#6C9790" ) + # + + volume box + self.GUI["2volumeBox"] = gtk.VBox() + self.GUI["2volumeImage"] = gtk.Image() + self.GUI["2volumeImage"].set_from_file( Config.IMAGE_ROOT+"volume2.png" ) + self.GUI["2volumeBox"].pack_start( self.GUI["2volumeImage"], False ) + self.GUI["2volumeAdjustment"] = gtk.Adjustment( self._data["volume"], 0, 100, 1, 1, 0 ) + self.GUI["2volumeSlider"] = ImageVScale( Config.IMAGE_ROOT+"sliderEditVolume.png", self.GUI["2volumeAdjustment"], 6 ) + self.GUI["2volumeSlider"].set_inverted(True) + self.GUI["2volumeAdjustment"].connect( "value-changed", self.handleVolume ) + self.GUI["2volumeBox"].pack_start( self.GUI["2volumeSlider"] ) + self.GUI["2volumePanel"].pack_start( self.GUI["2volumeBox"] ) + # + + tempo box + self.GUI["2tempoBox"] = gtk.VBox() + self.GUI["2tempoImage"] = gtk.Image() + self.GUI["2tempoImage"].set_from_file( Config.IMAGE_ROOT+"tempo3.png" ) + self.GUI["2tempoBox"].pack_start( self.GUI["2tempoImage"], False ) + self.GUI["2tempoAdjustment"] = gtk.Adjustment( self._data["tempo"], 40, 240, 1, 1, 0 ) + self.GUI["2tempoSlider"] = ImageVScale( Config.IMAGE_ROOT+"sliderEditTempo.png", self.GUI["2tempoAdjustment"], 6 ) + self.GUI["2tempoSlider"].set_inverted(True) + self.GUI["2tempoAdjustment"].connect( "value-changed", self.handleTempo ) + self.GUI["2tempoBox"].pack_start( self.GUI["2tempoSlider"] ) + self.GUI["2volumePanel"].pack_start( self.GUI["2tempoBox"] ) + self.GUI["2leftPanel"].pack_start( self.GUI["2volumePanel"] ) + self.GUI["2main"].pack_start( self.GUI["2leftPanel"], False ) + #------------------------------------------------------------------------ # right panel self.GUI["2rightPanel"] = gtk.VBox() - # + track interface - self.trackInterface = TrackInterface( self ) - self.trackInterface.set_size_request( -1, 713 ) - self.GUI["2rightPanel"].pack_start( self.trackInterface, False, False, 0 ) - # + tool panel - self.GUI["2toolPanel"] = gtk.HBox() - self.GUI["2toolPanel"].set_size_request( -1, 75 ) - # + + tool box - self.GUI["2toolBox"] = formatRoundBox( RoundHBox(), "#6C9790" ) - self.GUI["2toolBox"].set_size_request( 146, -1 ) - self.GUI["2toolPointerButton"] = ImageRadioButton( None, Config.IMAGE_ROOT+"pointer.png", Config.IMAGE_ROOT+"pointerDown.png", backgroundFill = "#6C9790" ) - self.GUI["2toolPointerButton"].connect( "clicked", self.handleToolClick , "Default" ) - self.GUI["2toolBox"].pack_start( self.GUI["2toolPointerButton"] ) - self.GUI["2toolPencilButton"] = ImageRadioButton( self.GUI["2toolPointerButton"], Config.IMAGE_ROOT+"pencil.png", Config.IMAGE_ROOT+"pencilDown.png", backgroundFill = "#6C9790" ) - self.GUI["2toolPencilButton"].connect( "clicked", self.handleToolClick , "Draw" ) - self.GUI["2toolBox"].pack_start( self.GUI["2toolPencilButton"] ) - self.GUI["2toolPanel"].pack_start( self.GUI["2toolBox"], False, False ) - self.GUI["2rightPanel"].pack_start( self.GUI["2toolPanel"], False ) - # + + context box (for context sensitive buttons, nothing to do with CAIRO) - contextWidth = 592 - self.GUI["2contextBox"] = formatRoundBox( RoundFixed(), "#6C9790" ) - self.GUI["2contextBox"].set_size_request( contextWidth, -1 ) - self.GUI["2contextPrevButton"] = gtk.Button("<") - self.GUI["2contextPrevButton"].connect( "clicked", lambda a1:self.prevContext() ) - self.GUI["2contextBox"].put( self.GUI["2contextPrevButton"], 0, 0 ) - self.GUI["2contextNextButton"] = gtk.Button(">") - self.GUI["2contextNextButton"].connect( "clicked", lambda a1:self.nextContext() ) - self.GUI["2contextBox"].put( self.GUI["2contextNextButton"], contextWidth-25, 0 ) - # + + + page box - self.GUI["2pageBox"] = gtk.HBox() - self.GUI["2pageBox"].set_size_request( contextWidth-50, -1 ) - self.GUI["2pageDeleteButton"] = gtk.Button("Delete") - self.GUI["2pageDeleteButton"].connect( "clicked", lambda a1:self.removePages() ) - self.GUI["2pageBox"].pack_start( self.GUI["2pageDeleteButton"] ) - self.GUI["2pageNewButton"] = gtk.Button("New") - self.GUI["2pageNewButton"].connect( "clicked", lambda a1:self.addPage() ) - self.GUI["2pageBox"].pack_start( self.GUI["2pageNewButton"] ) - self.GUI["2pageDuplicateButton"] = gtk.Button("Duplicate") - self.GUI["2pageDuplicateButton"].connect( "clicked", lambda a1:self.duplicatePages() ) - self.GUI["2pageBox"].pack_start( self.GUI["2pageDuplicateButton"] ) - self.GUI["2contextBox"].put( self.GUI["2pageBox"], 25, 0 ) - # + + + track box - self.GUI["2trackBox"] = gtk.HBox() - self.GUI["2trackBox"].set_size_request( contextWidth-50, -1 ) - self.GUI["2trackDeleteButton"] = gtk.Button("tDelete") - self.GUI["2trackDeleteButton"].connect( "clicked", lambda a1:self.removePages() ) - self.GUI["2trackBox"].pack_start( self.GUI["2trackDeleteButton"] ) - self.GUI["2trackNewButton"] = gtk.Button("tNew") - self.GUI["2trackNewButton"].connect( "clicked", lambda a1:self.addPage() ) - self.GUI["2trackBox"].pack_start( self.GUI["2trackNewButton"] ) - self.GUI["2trackDuplicateButton"] = gtk.Button("tDuplicate") - self.GUI["2trackDuplicateButton"].connect( "clicked", lambda a1:self.duplicatePages() ) - self.GUI["2trackBox"].pack_start( self.GUI["2trackDuplicateButton"] ) - self.GUI["2contextBox"].put( self.GUI["2trackBox"], 25, 0 ) - # + + + note box - self.GUI["2noteBox"] = gtk.HBox() - self.GUI["2noteBox"].set_size_request( contextWidth-50, -1 ) - self.GUI["2noteDeleteButton"] = gtk.Button("nDelete") - self.GUI["2noteDeleteButton"].connect( "clicked", lambda a1:self.removePages() ) - self.GUI["2noteBox"].pack_start( self.GUI["2noteDeleteButton"] ) - self.GUI["2noteNewButton"] = gtk.Button("nNew") - self.GUI["2noteNewButton"].connect( "clicked", lambda a1:self.addPage() ) - self.GUI["2noteBox"].pack_start( self.GUI["2noteNewButton"] ) - self.GUI["2noteDuplicateButton"] = gtk.Button("nDuplicate") - self.GUI["2noteDuplicateButton"].connect( "clicked", lambda a1:self.duplicatePages() ) - self.GUI["2noteBox"].pack_start( self.GUI["2noteDuplicateButton"] ) - self.GUI["2contextBox"].put( self.GUI["2noteBox"], 25, 0 ) - self.GUI["2toolPanel"].pack_start( self.GUI["2contextBox"], False ) - # + + transport box - self.GUI["2transportBox"] = formatRoundBox( RoundHBox(), "#6C9790" ) - self.GUI["2generateButton"] = gtk.Button("G") - self.GUI["2generateButton"].connect( "clicked", self.handleGenerate, None ) - self.GUI["2transportBox"].pack_start( self.GUI["2generateButton"] ) - self.GUI["2recordButton"] = gtk.Button("R") - self.GUI["2transportBox"].pack_start( self.GUI["2recordButton"] ) - self.GUI["2playButton"] = gtk.ToggleButton("P") - self.GUI["2playButton"].connect( "toggled", self.handlePlay, "Page Play" ) - self.GUI["2transportBox"].pack_start( self.GUI["2playButton"] ) - self.GUI["2loopButton"] = gtk.Button("L") - self.GUI["2transportBox"].pack_start( self.GUI["2loopButton"] ) - self.GUI["2toolPanel"].pack_start( self.GUI["2transportBox"] ) - # + tune box - self.GUI["2tuneBox"] = formatRoundBox( RoundVBox(), "#6C9790" ) - self.GUI["2tuneScrolledWindow"] = gtk.ScrolledWindow() - self.GUI["2tuneScrolledWindow"].set_policy( gtk.POLICY_ALWAYS, gtk.POLICY_NEVER ) - self.GUI["2tuneScrolledWindow"].set_shadow_type(gtk.SHADOW_NONE) - self.tuneInterface = TuneInterface( self ) - self.GUI["2tuneScrolledWindow"].add_with_viewport( self.tuneInterface ) - self.GUI["2tuneBox"].pack_start( self.GUI["2tuneScrolledWindow"] ) - self.GUI["2rightPanel"].pack_start( self.GUI["2tuneBox"] ) - self.GUI["2main"].pack_start( self.GUI["2rightPanel"] ) - + if 1: # + track interface + self.trackInterface = TrackInterface( self.noteDB, self ) + self.noteDB.addListener( self.trackInterface, TrackInterfaceParasite ) + self.trackInterface.set_size_request( -1, 713 ) + self.GUI["2rightPanel"].pack_start( self.trackInterface, False, False, 0 ) + # + tool panel + self.GUI["2toolPanel"] = gtk.HBox() + self.GUI["2toolPanel"].set_size_request( -1, 75 ) + # + + tool box + self.GUI["2toolBox"] = formatRoundBox( RoundHBox(), "#6C9790" ) + self.GUI["2toolBox"].set_size_request( 146, -1 ) + self.GUI["2toolPointerButton"] = ImageRadioButton( None, Config.IMAGE_ROOT+"pointer.png", Config.IMAGE_ROOT+"pointerDown.png", backgroundFill = "#6C9790" ) + self.GUI["2toolPointerButton"].connect( "clicked", self.handleToolClick , "Default" ) + self.GUI["2toolBox"].pack_start( self.GUI["2toolPointerButton"] ) + self.GUI["2toolPencilButton"] = ImageRadioButton( self.GUI["2toolPointerButton"], Config.IMAGE_ROOT+"pencil.png", Config.IMAGE_ROOT+"pencilDown.png", backgroundFill = "#6C9790" ) + self.GUI["2toolPencilButton"].connect( "clicked", self.handleToolClick , "Draw" ) + self.GUI["2toolBox"].pack_start( self.GUI["2toolPencilButton"] ) + self.GUI["2toolPanel"].pack_start( self.GUI["2toolBox"], False, False ) + self.GUI["2rightPanel"].pack_start( self.GUI["2toolPanel"], False ) + # + + context box (for context sensitive buttons, nothing to do with CAIRO) + contextWidth = 592 + self.GUI["2contextBox"] = formatRoundBox( RoundFixed(), "#6C9790" ) + self.GUI["2contextBox"].set_size_request( contextWidth, -1 ) + self.GUI["2contextPrevButton"] = gtk.Button("<") + self.GUI["2contextPrevButton"].connect( "clicked", lambda a1:self.prevContext() ) + self.GUI["2contextBox"].put( self.GUI["2contextPrevButton"], 0, 0 ) + self.GUI["2contextNextButton"] = gtk.Button(">") + self.GUI["2contextNextButton"].connect( "clicked", lambda a1:self.nextContext() ) + self.GUI["2contextBox"].put( self.GUI["2contextNextButton"], contextWidth-25, 0 ) + # + + + page box + self.GUI["2pageBox"] = gtk.HBox() + self.GUI["2pageBox"].set_size_request( contextWidth-50, -1 ) + self.GUI["2pageDeleteButton"] = gtk.Button("Delete") + self.GUI["2pageDeleteButton"].connect( "clicked", lambda a1:self.removePages() ) + self.GUI["2pageBox"].pack_start( self.GUI["2pageDeleteButton"] ) + self.GUI["2pageNewButton"] = gtk.Button("New") + self.GUI["2pageNewButton"].connect( "clicked", lambda a1:self.addPage() ) + self.GUI["2pageBox"].pack_start( self.GUI["2pageNewButton"] ) + self.GUI["2pageDuplicateButton"] = gtk.Button("Duplicate") + self.GUI["2pageDuplicateButton"].connect( "clicked", lambda a1:self.duplicatePages() ) + self.GUI["2pageBox"].pack_start( self.GUI["2pageDuplicateButton"] ) + self.GUI["2contextBox"].put( self.GUI["2pageBox"], 25, 0 ) + # + + + track box + self.GUI["2trackBox"] = gtk.HBox() + self.GUI["2trackBox"].set_size_request( contextWidth-50, -1 ) + self.GUI["2trackDeleteButton"] = gtk.Button("tDelete") + self.GUI["2trackDeleteButton"].connect( "clicked", lambda a1:self.removePages() ) + self.GUI["2trackBox"].pack_start( self.GUI["2trackDeleteButton"] ) + self.GUI["2trackNewButton"] = gtk.Button("tNew") + self.GUI["2trackNewButton"].connect( "clicked", lambda a1:self.addPage() ) + self.GUI["2trackBox"].pack_start( self.GUI["2trackNewButton"] ) + self.GUI["2trackDuplicateButton"] = gtk.Button("tDuplicate") + self.GUI["2trackDuplicateButton"].connect( "clicked", lambda a1:self.duplicatePages() ) + self.GUI["2trackBox"].pack_start( self.GUI["2trackDuplicateButton"] ) + self.GUI["2contextBox"].put( self.GUI["2trackBox"], 25, 0 ) + # + + + note box + self.GUI["2noteBox"] = gtk.HBox() + self.GUI["2noteBox"].set_size_request( contextWidth-50, -1 ) + self.GUI["2noteDeleteButton"] = gtk.Button("nDelete") + self.GUI["2noteDeleteButton"].connect( "clicked", lambda a1:self.removePages() ) + self.GUI["2noteBox"].pack_start( self.GUI["2noteDeleteButton"] ) + self.GUI["2noteNewButton"] = gtk.Button("nNew") + self.GUI["2noteNewButton"].connect( "clicked", lambda a1:self.addPage() ) + self.GUI["2noteBox"].pack_start( self.GUI["2noteNewButton"] ) + self.GUI["2noteDuplicateButton"] = gtk.Button("nDuplicate") + self.GUI["2noteDuplicateButton"].connect( "clicked", lambda a1:self.duplicatePages() ) + self.GUI["2noteBox"].pack_start( self.GUI["2noteDuplicateButton"] ) + self.GUI["2contextBox"].put( self.GUI["2noteBox"], 25, 0 ) + self.GUI["2toolPanel"].pack_start( self.GUI["2contextBox"], False ) + # + + transport box + self.GUI["2transportBox"] = formatRoundBox( RoundHBox(), "#6C9790" ) + self.GUI["2generateButton"] = gtk.Button("G") + self.GUI["2generateButton"].connect( "clicked", self.handleGenerate, None ) + self.GUI["2transportBox"].pack_start( self.GUI["2generateButton"] ) + self.GUI["2recordButton"] = gtk.ToggleButton("R") + self.GUI["2transportBox"].pack_start( self.GUI["2recordButton"] ) + self.GUI["2playButton"] = gtk.ToggleButton("P") + self.GUI["2playButton"].connect( "toggled", self.handlePlay, "Page Play" ) + self.GUI["2transportBox"].pack_start( self.GUI["2playButton"] ) + self.GUI["2loopButton"] = gtk.Button("L") + self.GUI["2transportBox"].pack_start( self.GUI["2loopButton"] ) + self.GUI["2toolPanel"].pack_start( self.GUI["2transportBox"] ) + # + tune box + self.GUI["2tuneBox"] = formatRoundBox( RoundVBox(), "#6C9790" ) + self.GUI["2tuneScrolledWindow"] = gtk.ScrolledWindow() + self.GUI["2tuneScrolledWindow"].set_policy( gtk.POLICY_ALWAYS, gtk.POLICY_NEVER ) + self.GUI["2tuneScrolledWindow"].set_shadow_type(gtk.SHADOW_NONE) + self.tuneInterface = TuneInterface( self.noteDB, self ) + self.noteDB.addListener( self.tuneInterface, TuneInterfaceParasite, True ) + self.GUI["2tuneScrolledWindow"].add_with_viewport( self.tuneInterface ) + self.GUI["2tuneBox"].pack_start( self.GUI["2tuneScrolledWindow"] ) + self.GUI["2rightPanel"].pack_start( self.GUI["2tuneBox"] ) + self.GUI["2main"].pack_start( self.GUI["2rightPanel"] ) + self.add( self.GUI["2main"] ) - - self.addPage( 0, 4 ) # yeah! a page! - - - def init_data( ): - self._data = {} - #[ volume, ... ] - self._data['track_volume'] = [ Config.DEFAULT_VOLUME ] * Config.NUMBER_OF_TRACKS - self._data['track_mute'] = [ 1.0 ] * Config.NUMBER_OF_TRACKS + self.generationParametersWindow = GenerationParametersWindow( self.generate, self.variate, self.handleCloseGenerationParametersWindow ) - #[ instrument index, ... ] - track_inst = [ - Config.FLUTE, - Config.KOTO, - Config.GAM, - Config.GUIT, - Config.DRUM1KIT ] - if len(track_inst) != Config.NUMBER_OF_TRACKS: raise 'error' - - self._data['track_inst'] = track_inst + [Config.FLUTE] * (Config.NUMBER_OF_TRACKS - len( track_inst) ) - #{ pageId: { [track 0 = note list], [track 2 = note list], ... ] } - npages = 40 - nbeats = 4 - - self._data['volume'] = Config.DEFAULT_VOLUME - self._data['page_beats'] = [nbeats for p in range(npages)] - self._data['tempo'] = Config.PLAYER_TEMPO - self._data['tune'] = [] - self._data['notebin'] = [] - self._noteId = {} - self._noteIdBase = 0 - - self._data["pages"] = [] - self._pageIdBase = 0 - - # these helper functions do not - # run in any particular order.... - # TODO: give these functions better names, put them in execution order, cut hierarchy - - def setupGUI( ): - - self.volumeFunctions = {} - - self.generateParametersWindow = GenerationParametersWindow( self.generate, self.variate, self.handleCloseGenerateWindow ) - - setupGlobalControls() - setupPageControls() - setupTrackControls() - #setupMainView() - - #self.tuneView = TuneView( self.onTuneViewSelect ) - #self.pageBankView = PageBankView( self.onPageBankSelect, self.onPageBankDrop ) - - self.mainWindowBox = gtk.HBox( False, 5 ) - - self.globalControlsBox = gtk.VBox( False ) - - self.fpsText = gtk.Label( "" ) - self.globalControlsBox.pack_start( self.fpsText, False ) - self.globalControlsBox.pack_start( self.globalControlsFrame, True ) - - self.mainWindowBox.pack_start( self.globalControlsBox, False ) - - - controlsBox = gtk.VBox( False, 5 ) - controlsBox.pack_start( self.trackControlsBox, False ) - #TODO: this Label is temporary!! - controlsBox.pack_start( gtk.Label( "" ), True ) - controlsBox.pack_start( self.pageControlsBox, False ) - self.mainWindowBox.pack_start( controlsBox, False ) - - self.trackPagesBox = gtk.VBox( False ) - #self.trackPagesBox.pack_start( self.mainView, True ) - #self.trackPagesBox.pack_start( self.tuneView, False ) - #self.trackPagesBox.pack_start( self.pageBankView, False, True, 5 ) - - self.mainWindowBox.pack_start( self.trackPagesBox ) - - #self.add( self.mainWindowBox ) - - # contains TAM-TAM and OLPC labels, as well as the volume and tempo sliders - def setupGlobalControls( ): - self.globalControlsFrame = gtk.Frame() - self.globalControlsFrame.set_shadow_type( gtk.SHADOW_ETCHED_OUT ) - - self.globalControlsBox = gtk.VBox() - - self.tamTamLabel = gtk.Label( " TAM - TAM " ) - self.globalControlsBox.pack_start( self.tamTamLabel ) - - - self.beatsPerPageAdjustment = gtk.Adjustment( 4, 1, 8, 1, 1, 0 ) - self.beatsPerPageAdjustment.connect( "value_changed", self.updateNumberOfBars, None ) - self.barsSlider = gtk.VScale( self.beatsPerPageAdjustment ) - self.barsSlider.set_draw_value( False ) - self.barsSlider.set_digits( 0 ) - self.barsSlider.set_inverted( True ) - self.barsSlider.set_increments( 1, 1 ) - self.barsSlider.set_update_policy( gtk.UPDATE_DELAYED ) - #self.mainSlidersBox.pack_start( self.barsSlider ) - - #self.globalControlsBox.pack_start( self.mainSlidersBox ) - - self.olpcLabel = gtk.Label( "OLPC" ) - self.globalControlsBox.pack_start( self.olpcLabel ) - - self.saveButton = gtk.Button("Save") - self.loadButton = gtk.Button("Open") - - fileBox = gtk.HBox() - fileBox.pack_start( self.saveButton, True ) - fileBox.pack_start( self.loadButton, True ) - self.globalControlsBox.pack_start( fileBox, False ) - self.saveButton.connect("clicked", self.handleSave, None ) - self.loadButton.connect("clicked", self.handleLoad, None ) - self.globalControlsFrame.add( self.globalControlsBox ) - - def setupPageControls( ): - self.pageControlsBox = gtk.VBox( False ) - - self.generateButton = gtk.ToggleButton( "Generate" ) - self.playButton = gtk.ToggleButton( "Play" ) - self.keyboardButton = gtk.ToggleButton( "K" ) - self.keyboardRecordButton = gtk.ToggleButton( "Record" ) - - self.pageControlsBox.pack_start( self.generateButton, False ) - self.pageControlsBox.pack_start( self.playButton, False ) - - keyboardBox = gtk.HBox() - keyboardBox.pack_start( self.keyboardButton, False ) - keyboardBox.pack_start( self.keyboardRecordButton ) - self.pageControlsBox.pack_start( keyboardBox, False ) - - self.generateButton.connect( "toggled", self.handleGenerate, None ) - self.playButton.connect( "toggled", self.handlePlay, "Page Play" ) - self.keyboardButton.connect( "toggled", self.onKeyboardButton, None ) - self.keyboardRecordButton.connect( "toggled", self.onKeyboardRecordButton, None ) - - def setupTrackControls( ): - self.trackControlsBox = gtk.VBox() - self.instrumentRecordButtons = {} - for trackId in range( Config.NUMBER_OF_TRACKS): - trackControlsBox = gtk.HBox() - - #setup instrument controls - instrumentControlsBox = gtk.VBox() - - instrumentMenu = gtk.Menu() - instrumentMenuItem = gtk.MenuItem( "Instrument" ) - instrumentMenuItem.set_submenu( instrumentMenu ) - - instrumentNames = [] - instrumentFolderNames = Config.INSTRUMENTS.keys() - for instrumentName in instrumentFolderNames: - if not instrumentName[0: 4] == 'drum': - instrumentNames.append( instrumentName ) - - instrumentNames.append( 'drum1kit' ) - instrumentNames.sort() - for instrumentName in instrumentNames: - menuItem = gtk.MenuItem( instrumentName ) - menuItem.connect_object( "activate", self.handleInstrumentChanged, ( trackId, instrumentName ) ) - instrumentMenu.append( menuItem ) - - instrumentMenuBar = gtk.MenuBar() - instrumentMenuBar.append( instrumentMenuItem ) - instrumentControlsBox.pack_start( instrumentMenuBar ) - - recordButton = gtk.Button() - recordButton.set_size_request( 15, 15 ) - self.instrumentRecordButtons[ trackId ] = recordButton - instrumentControlsBox.pack_start( recordButton, False ) - - trackControlsBox.pack_start( instrumentControlsBox ) - - #setup playback controls - playbackControlsBox = gtk.VBox() - - muteButton = gtk.ToggleButton() - muteButton.set_size_request( 15, 15 ) - playbackControlsBox.pack_start( muteButton, False ) - - volumeAdjustment = gtk.Adjustment( 0.8, 0, 1, 0.01, 0.01, 0 ) - volumeAdjustment.connect( "value_changed", self.onTrackVolumeChanged, trackId ) - self.volumeFunctions[ trackId ] = volumeAdjustment.get_value - volumeSlider = gtk.VScale( volumeAdjustment ) - volumeSlider.set_update_policy( 0 ) - volumeSlider.set_digits( 2 ) - volumeSlider.set_draw_value( False ) - volumeSlider.set_digits( 0 ) - volumeSlider.set_inverted( True ) - playbackControlsBox.pack_start( volumeSlider, True ) - - trackControlsBox.pack_start( playbackControlsBox ) - - trackName = "Track %i" % trackId - muteButton.connect( "toggled", self.onMuteTrack, trackId ) - - self.trackControlsBox.pack_start( trackControlsBox ) - + #=================================================== + # begin initialization gtk.EventBox.__init__( self ) - + # keyboard variables self.kb_active = False self.kb_record = False @@ -478,27 +316,24 @@ class MainWindow( gtk.EventBox ): self.fpsFrameCount = 0 self.fpsN = 100 # how many frames to average FPS over self.fpsLastTime = time.time() # fps will be borked for the first few frames but who cares? - + self.context = -1 # invalidate self.contextTrackActive = False self.contextNoteActive = False init_data() #above - setupGUI() #above #TEMP init_GUI() #above self.csnd.setMasterVolume( self.getVolume() ) - - #for pageId in range( GUIConfig.NUMBER_OF_PAGE_BANK_ROWS * GUIConfig.NUMBER_OF_PAGE_BANK_COLUMNS ): - # self.pageBankView.addPage( pageId, False ) - + for tid in range(Config.NUMBER_OF_TRACKS): self.handleInstrumentChanged( ( tid, self._data['track_inst'][tid] ) ) - #self.handleConfigureEvent( None, None ) # needs to come after pages have been added in initialize() - + first = self.noteDB.addPage( 4 ) + self.displayPage( first ) + self.show_all() #gtk command - + #self.GUI["2pageBox"].hide() self.GUI["2trackBox"].hide() self.GUI["2noteBox"].hide() @@ -528,15 +363,29 @@ class MainWindow( gtk.EventBox ): #TODO: check for track activation, to not take all self.pages_playing = self.tuneInterface.getSelectedIds() - trackset = set(range(Config.NUMBER_OF_TRACKS)) - pageset = set(self.pages_playing) - notes = [n for n in self._data['notebin'] if n.pageId in pageset and n.trackId in trackset] + + trackset = set( [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackSelected[i] ] ) + + notes = [] + if len(trackset) == 0 or len(trackset) == Config.NUMBER_OF_TRACKS: + for page in self.pages_playing: + notes += self.noteDB.getCSNotesByPage( page ) + else: + for page in self.pages_playing: + for track in trackset: + notes += self.noteDB.getCSNotesByTrack( page, track ) + self.playing = True self.playbackTimeout = gobject.timeout_add( 100, self.onTimeout ) - numticks = sum([self._data["pages"][id]["beats"] for id in self.pages_playing ] ) * Config.TICKS_PER_BEAT + if len(self.pages_playing) > 1: + self.displayPage( self.pages_playing[0], self.pages_playing[1] ) + else: + self.displayPage( self.pages_playing[0] ) + + numticks = sum([self.noteDB.getPage(id).ticks for id in self.pages_playing ] ) print 'play!' - print 'pageset : ', pageset + print 'pages : ', self.pages_playing print 'trackset : ', trackset print 'numticks : ', numticks print 'notes : ', len(notes), 'notes' @@ -561,20 +410,21 @@ class MainWindow( gtk.EventBox ): self.playing = False - self.kb_record = self.playButton.get_active() and self.keyboardRecordButton.get_active() and self.keyboardButton.get_active() + self.kb_record = self.GUI["2playButton"].get_active() and self.GUI["2recordButton"].get_active() def onTimeout(self): self.updateFPS() curtick = self.csnd.loopGetTick() curIdx = curtick / ( 4 * Config.TICKS_PER_BEAT) #TODO handle each pages_playing length - self.tuneInterface.displayPage( self.pages_playing[curIdx], 0 ) - self.trackInterface.displayPage(self.pages_playing[curIdx], 4 ) #TODO: use page_beats + if curIdx + 1 < len(self.pages_playing): predraw = self.pages_playing[curIdx+1] + else: predraw = self.pages_playing[0] + self.displayPage( self.pages_playing[curIdx], predraw ) return True def onMuteTrack( self, widget, trackId ): - self._data['track_mute'][trackId] = not self._data['track_mute'][trackId] + self._data['track_mute'][trackId] = not self._data['track_mute'][trackId] #if self._data['track_mute'][trackId]: #self.noteLooper.setMute( trackId, 0.0 ) #else: @@ -584,7 +434,7 @@ class MainWindow( gtk.EventBox ): v = widget.get_value() / 100.0 self._data['track_volume'][trackId] = v #self.noteLooper.setVolume( trackId, v ) - + # data is tuple ( trackId, instrumentName ) def handleInstrumentChanged( self, data ): (id, instrumentName) = data @@ -592,117 +442,69 @@ class MainWindow( gtk.EventBox ): print id, instrumentName #self.noteLooper.setInstrument(id, instrumentName) - recordButton = self.instrumentRecordButtons[ id ] - if instrumentName in Config.RECORDABLE_INSTRUMENTS: - recordButton.show() - recordButton.connect( "clicked", - self.handleMicRecord, - Config.RECORDABLE_INSTRUMENT_CSOUND_IDS[ instrumentName ] ) - else: - recordButton.hide() + #recordButton = self.instrumentRecordButtons[ id ] + #if instrumentName in Config.RECORDABLE_INSTRUMENTS: + # recordButton.show() + # recordButton.connect( "clicked", + # self.handleMicRecord, + # Config.RECORDABLE_INSTRUMENT_CSOUND_IDS[ instrumentName ] ) + #else: + # recordButton.hide() def handleVolume( self, widget ): self._data["volume"] = round( widget.get_value() ) self.csnd.setMasterVolume(self._data["volume"]) img = min(3,int(4*self._data["volume"]/100)) # volume 0-3 self.GUI["2volumeImage"].set_from_file( Config.IMAGE_ROOT+"volume"+str(img)+".png" ) - + def handleTrackVolume( self, widget, track ): self._data["track_volume"][track] = round( widget.get_value() ) - + def handleTempo( self, widget ): self._data['tempo'] = round( widget.get_value() ) img = min(7,int(8*(self._data["tempo"]-widget.lower)/(widget.upper-widget.lower)))+1# tempo 1-8 self.GUI["2tempoImage"].set_from_file( Config.IMAGE_ROOT+"tempo"+str(img)+".png" ) - - + + def handleToolClick( self, widget, mode ): if widget.get_active(): self.trackInterface.setInterfaceMode( mode ) def onKeyboardButton( self, widget, data ): self.kb_active = widget.get_active() - + def onKeyboardRecordButton( self, widget, data ): - if not self.kb_active: - self.keyboardButton.set_active( True ) - - self.kb_record = self.playButton.get_active() and self.keyboardRecordButton.get_active() - def onScoreChange( self, action, noteList ): - pass - - def onNoteDrag( self, dragList ): - for (id, pitch, onset, duration) in dragList: - print "ERROR: ignoring note drag" - return + self.kb_record = self.GUI["playButton"].get_active() and self.GUI["2recordButton"].get_active() #----------------------------------- # generation functions #----------------------------------- def handleGenerate( self, widget, data ): #if widget.get_active(): - self.generateParametersWindow.show_all() + self.generationParametersWindow.show_all() #else: - # self.handleCloseGenerateWindow() - - def handleCloseGenerateWindow( self, widget = None, data = None ): - self.generateParametersWindow.hide_all() + # self.handleCloseGeneratonParametersWindow() + + def handleCloseGenerationParametersWindow( self, widget = None, data = None ): + self.generationParametersWindow.hide_all() #self.generateButton.set_active( False ) - - def addNotesToTrackInterface( self, notes ): - pageList = [] - trackList = [] - noteList = [] - csnoteList = [] - beatList = [] - - for n in notes: - pageList.append( n.pageId ) - trackList.append( n.trackId ) - noteList.append( n.noteId ) - csnoteList.append( n ) - beatList.append( p["beats"] for p in self._data["pages"] if p["pageId"] == n.pageId ) - - self.trackInterface.addNotes( - { "page":pageList, - "track":trackList, - "note":noteList, - "csnote":csnoteList, - "beatCount":beatList}, - len(notes) ) - - def removeNotesFromTrackInterface( self, notes ): - pageList = [] - trackList = [] - noteList = [] - - for n in notes: - pageList.append( n.pageId ) - trackList.append( n.trackId ) - noteList.append( n.noteId ) - - self.trackInterface.deleteNotes( - { "page":pageList, - "track":trackList, - "note":noteList }, - len(notes) ) - + def recompose( self, algo, params): - def none_to_all(tracks): - print 'tracks = ',tracks - if tracks == []: return set(range(0,Config.NUMBER_OF_TRACKS)) - else: return set(tracks) + # this seems excessive!? dict = {} for t in range(Config.NUMBER_OF_TRACKS): dict[t] = {} for p in range(Config.NUMBER_OF_PAGES): dict[t][p] = [] - newtracks = none_to_all( self.trackInterface.getSelectedTracks()) + if self.trackSelected == [ 0 for i in range(Config.NUMBER_OF_TRACKS) ]: + newtracks = set(range(Config.NUMBER_OF_TRACKS)) + else: + newtracks = set( [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackSelected[i] ] ) newpages = self.tuneInterface.getSelectedIds() - algo( + algo( params, self._data['track_volume'][:], self._data['track_inst'][:], @@ -721,166 +523,107 @@ class MainWindow( gtk.EventBox ): note.pageId = page note.trackId = track - #add notes to self._data + # add the new notes newnotes = [] for tid in dict: for pid in dict[tid]: newnotes += dict[tid][pid] - #delete the old pages & tracks! - togo = [n for n in self._data['notebin'] if (n.trackId in newtracks and n.pageId in newpages) ] - self.trackInterface.deleteNotes( - { "page": [n.pageId for n in togo], - "track":[n.trackId for n in togo] , - "note": [n.noteId for n in togo]}, - len(togo)) - self.addNotesToTrackInterface( newnotes ) - - self._data['notebin'] = [n for n in self._data['notebin'] if not (n.noteId in togo)] + newnotes + # delete the notes and add the new + stream = [] + for page in newpages: + for track in newtracks: + self.noteDB.deleteNotesByTrack( page, track ) + stream += [ page, track, len(dict[track][page]) ] + stream += dict[track][page] + stream += [-1] + self.noteDB.addNotes( stream ) - self.handleCloseGenerateWindow( None, None ) - #self.handleConfigureEvent( None, None ) + self.handleCloseGenerationParametersWindow( None, None ) def generate( self, params ): self.recompose( generator1, params) def variate( self, params ): self.recompose( variate, params) - + + #======================================================= + # Track Functions + + def toggleTrack( self, trackN, exclusive ): + if exclusive: + for i in range(Config.NUMBER_OF_TRACKS): + self.trackSelected[i] = False + self.trackSelected[trackN] = True + self.trackInterface.trackToggled() # invalidate whole page + self.setContextState( CONTEXT.TRACK, True ) + self.setContext( CONTEXT.TRACK ) + else: + self.trackSelected[trackN] = not self.trackSelected[trackN] + self.trackInterface.trackToggled( trackN ) + for i in range(Config.NUMBER_OF_TRACKS): + if self.trackSelected[i]: + self.setContextState( CONTEXT.TRACK, True ) + self.setContext( CONTEXT.TRACK ) + return + self.setContextState( CONTEXT.TRACK, False ) + + def getTrackSelected( self, trackN ): + return self.trackSelected[trackN] + + #======================================================= + # NoteDB notifications + + def notifyPageAdd( self, id, at ): + self.displayPage( id ) + + def notifyPageDelete( self, which, safe ): + if self.displayedPage in which: + self.displayPage( safe ) + + def notifyPageDuplicate( self, new, at ): + self.displayPage( new[self.displayedPage] ) + + def notifyPageMove( self, which, low, high ): + return + #----------------------------------- # tune functions - #----------------------------------- - + #----------------------------------- + def scrollTune( self, scroll ): adj = self.GUI["2tuneScrolledWindow"].get_hadjustment() adj.set_value( scroll ) - - def displayPage( self, pageId, beats = -1 ): - - if beats == -1: - for page in self._data["pages"]: - if pageId == page["pageId"]: break - beats = page["beats"] - + + def displayPage( self, pageId, nextId = -1 ): + self.displayedPage = pageId - self.displayedBeats = beats - + adj = self.GUI["2tuneScrolledWindow"].get_hadjustment() scroll = self.tuneInterface.displayPage( pageId, adj.get_value() ) if scroll >= 0: adj.set_value(scroll) - - self.trackInterface.displayPage( pageId, beats ) - - def addPage( self, insert = -1, beats = -1 ): - - if insert == -1: insert = self.tuneInterface.getLastSelected() + 1 - if beats == -1: beats = self.displayedBeats - - pageId = self._pageIdBase - self._pageIdBase += 1 - while pageId in [ x["pageId"] for x in self._data["pages"] ]: - pageId = self._pageIdBase - self._pageIdBase += 1 - - newpage = { "pageId": pageId, "beats": beats } - self._data["pages"].insert( insert, newpage ) - - self.tuneInterface.clearSelection() - self.tuneInterface.insertPage( pageId, insert ) - - self.displayPage( pageId, beats ) - - def duplicatePages( self, insert = -1, pageIds = -1 ): - - if insert == -1: insert = self.tuneInterface.getLastSelected() + 1 - if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds() - - nextDisplay = -1 - nextBeats = -1 - newpages = [] - for id in pageIds: - for page in self._data["pages"]: - if id == page["pageId"]: break - - pageId = self._pageIdBase - self._pageIdBase += 1 - while pageId in [ x["pageId"] for x in self._data["pages"] ]: - pageId = self._pageIdBase - self._pageIdBase += 1 - - if self.displayedPage == id: - nextDisplay = pageId - nextBeats = page["beats"] - - newnotes = [ n.clone() for n in self._data["notebin"] if n.pageId == id ] - for n in newnotes: n.pageId = pageId - - self._data["notebin"].extend( newnotes ) - self.addNotesToTrackInterface( newnotes ) - - newpages.append( { "pageId": pageId, "beats": page["beats"] } ) - - self.tuneInterface.insertPages( [ p["pageId"] for p in newpages ], insert, True, True ) - - for page in newpages: - self._data["pages"].insert( insert, page ) - insert += 1 - - self.displayPage( nextDisplay, nextBeats ) - + + self.trackInterface.displayPage( pageId, nextId ) + + def addPage( self, after = -1, beats = False ): + + if after == -1: after = self.tuneInterface.getLastSelected() + if not beats: beats = self.noteDB.getPage( self.displayedPage ).beats + + self.noteDB.addPage( beats, after ) + + def duplicatePages( self, after = -1, pageIds = False ): + + if after == -1: after = self.tuneInterface.getLastSelected() + if not pageIds: pageIds = self.tuneInterface.getSelectedIds() + + self.noteDB.duplicatePages( pageIds, after ) + def removePages( self, pageIds = -1 ): - - if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds() - - next = self.tuneInterface.getLastSelected() + 1 - if next == len(self._data["pages"]): - next -= 2 - while next >= 0 and self._data["pages"][next]["pageId"] in pageIds: - next -= 1 - - if next == -1: - self.addPage() - else: - self.tuneInterface.selectPage( self._data["pages"][next]["pageId"], True ) # exclusive select - self.displayPage( self._data["pages"][next]["pageId"], self._data["pages"][next]["beats"] ) - - self.tuneInterface.removePages( pageIds ) - self.tuneInterface.selectPage( self._data["pages"][next]["pageId"] ) - - for id in pageIds: - for page in self._data["pages"]: - if id == page["pageId"]: break - - self._data["pages"].remove(page) - - notes = [ n for n in self._data["notebin"] if n.pageId == id ] - self.removeNotesFromTrackInterface( notes ) - self._data["notebin"] = [ n for n in self._data["notebin"] if n.pageId != id ] - - def movePages( self, insert = -1, pageIds = -1 ): - + if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds() - if insert == -1: insert = self.tuneInterface.getLastSelected() + 1 - len(pageIds) - - for id in pageIds: - remove = 0 - for page in self._data["pages"]: - if id == page["pageId"]: break - remove += 1 - - self.tuneInterface.movePage( remove, insert ) - - if remove == insert: - insert += 1 - continue - elif remove < insert: - if remove == insert-1: continue - insert -= 1 - - self._data["pages"].pop(remove) - self._data["pages"].insert( insert, page ) - - insert += 1 + + self.noteDB.deletePages( pageIds ) #----------------------------------- # load and save functions @@ -891,12 +634,12 @@ class MainWindow( gtk.EventBox ): 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: + try: print 'INFO: serialize to file %s' % chooser.get_filename() f = open( chooser.get_filename(), 'w') pickle.dump( self._data, f ) f.close() - except IOError: + except IOError: print 'ERROR: failed to serialize to file %s' % chooser.get_filename() chooser.destroy() @@ -905,11 +648,11 @@ class MainWindow( gtk.EventBox ): 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: + try: print 'INFO: unserialize from file %s' % chooser.get_filename() f = open( chooser.get_filename(), 'r') self._data = pickle.load( f ) - except IOError: + except IOError: print 'ERROR: failed to unserialize from file %s' % chooser.get_filename() chooser.destroy() @@ -928,11 +671,11 @@ class MainWindow( gtk.EventBox ): # callback functions #----------------------------------- def onKeyPress(self,widget,event): - + Config.ModKeys.keyPress( event.hardware_keycode ) - key = event.hardware_keycode - + key = event.hardware_keycode + if key == 53 and Config.ModKeys.ctrlDown: # q == 53 self.destroy( self ) @@ -940,11 +683,11 @@ class MainWindow( gtk.EventBox ): return if self.kb_record: self.kb_mono = False - + # If the key is already in the dictionnary, exit function (to avoir key repeats) if self.kb_keydict.has_key(key): return - # Assign on which track the note will be created according to the number of keys pressed + # Assign on which track the note will be created according to the number of keys pressed track = len(self.kb_keydict)+10 if self.kb_mono: track = 10 @@ -958,7 +701,7 @@ class MainWindow( gtk.EventBox ): # get instrument from top selected track if a track is selected if self.getSelectedtrackIds(): instrument = self._data['track_inst'][min(self.getSelectedtrackIds())] - + if instrument == 'drum1kit': if GenerationConfig.DRUMPITCH.has_key( pitch ): instrument = Config.DRUM1INSTRUMENTS[ GenerationConfig.DRUMPITCH[ pitch ] ] @@ -966,10 +709,10 @@ class MainWindow( gtk.EventBox ): instrument = Config.DRUM1INSTRUMENTS[ pitch ] pitch = 36 duration = 100 - + if Config.INSTRUMENTS[instrument].csoundInstrumentID == 102: duration = 100 - + # Create and play the note self.kb_keydict[key] = CSoundNote(onset = 0, pitch = pitch, @@ -981,15 +724,15 @@ class MainWindow( gtk.EventBox ): instrument = instrument, instrumentFlag = instrument) self.kb_keydict[key].playNow() - + def onKeyRelease(self,widget,event): Config.ModKeys.keyRelease( event.hardware_keycode ) if not self.kb_active: return - key = event.hardware_keycode - + key = event.hardware_keycode + if KEY_MAP.has_key(key): self.kb_keydict[key].duration = 0 self.kb_keydict[key].amplitude = 0 @@ -1013,30 +756,7 @@ class MainWindow( gtk.EventBox ): print TP.PrintAll() gtk.main_quit() - - def updateNumberOfBars( self, widget = None, data = None ): - self.trackInterface.updateBeatCount( int(round( self.beatsPerPageAdjustment.value)) ) - - def updateSelection( self ): - print 'WARNING: wtf is this?' - - def updatePage( self ): - TP.ProfileBegin( "updatePage" ) - - if self.playingTune: - self.tuneView.selectPage( self.currentpageId, False ) - self.pageBankView.selectPage(self.pageBankView.NO_PAGE,False) - else: - self.tuneView.deselectAll() - self.tuneView.selectPage(self.tuneView.NO_PAGE,False) - # temp - self.trackInterface.displayPage(0,int(round( self.beatsPerPageAdjustment.value))) - - self.handleConfigureEvent( None, None ) - - print TP.ProfileEndAndPrint( "updatePage" ) - def updateContextNavButtons( self ): if self.context == CONTEXT.PAGE: self.GUI["2contextPrevButton"].hide() @@ -1104,24 +824,6 @@ class MainWindow( gtk.EventBox ): else: self.setContext( CONTEXT.NOTE ) - - # handle resize (TODO: this could probably be done more efficiently) - #def handleConfigureEvent( self, widget, event ): - # mainBoxRect = self.trackPagesBox.get_allocation() - - #self.tuneView.set_size_request( mainBoxRect.width, GUIConfig.PAGE_HEIGHT + - # self.tuneView.get_hscrollbar().get_allocation().height + 10 ) - #self.tuneView.show_all() - - #self.pageBankView.set_size_request( mainBoxRect.width, GUIConfig.PAGE_HEIGHT * GUIConfig.NUMBER_OF_PAGE_BANK_ROWS ) - #self.pageBankView.show_all() - - #mainViewRect = self.mainView.get_allocation() - - #self.trackInterface.set_size_request( mainViewRect.width, mainViewRect.height ) - - #self.trackControlsBox.set_size_request( 100, mainViewRect.height ) - #----------------------------------- # access functions (not sure if this is the best way to go about doing this) #----------------------------------- diff --git a/Edit/NoteInterface.py b/Edit/NoteInterface.py index e927444..9dc9e43 100755..100644 --- a/Edit/NoteInterface.py +++ b/Edit/NoteInterface.py @@ -4,15 +4,16 @@ import gtk import Config +from Util.NoteDB import PARAMETER + class NoteInterface: - def __init__( self, parent, page, track, note, pitch, onset, duration, amplitude, image, imageSelected, colors ): - self.parent = parent - self.page = page - self.track = track - self.note = note # note id, not csnote! + def __init__( self, noteDB, owner, note ): + self.noteDB = noteDB + self.owner = owner + self.note = note - self.origin = self.parent.getTrackOrigin( self.track ) + self.origin = self.owner.getTrackOrigin( note.track ) self.firstTransform = True self.x = 0 self.y = 0 @@ -25,89 +26,86 @@ class NoteInterface: self.selected = False self.potentialDeselect = False - + self.oldOnset = -1 self.oldEnd = -1 self.oldPitch = -1 + self.oldAmplitude = -1 self.lastDragO = 0 self.lastDragP = 0 self.lastDragD = 0 - - self.image = image - self.imageSelected = imageSelected - self.baseColors = colors - self.updateParams( pitch, onset, duration, amplitude ) + self.image, self.imageSelected, self.colormap, self.baseColors = self.owner.getDrawingPackage( note.track ) + + self.updateParameter( None, None ) def destroy( self ): - self.parent.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.page, True ) - - def updateParams( self, pitch, onset, duration, amplitude): - self.pitch = pitch - self.onset = onset - self.duration = duration - self.end = onset + duration - - self.amplitude = amplitude - r = self.baseColors[0][0] + int(self.baseColors[1][0]*amplitude) - g = self.baseColors[0][1] + int(self.baseColors[1][1]*amplitude) - b = self.baseColors[0][2] + int(self.baseColors[1][2]*amplitude) - self.color = self.parent.drawingArea.get_colormap().alloc_color( r, g, b, True, True ) - - self.updateTransform() - + self.owner.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page, True ) + + def updateParameter( self, parameter, value ): + self.end = self.note.cs.onset + self.note.cs.duration + + if self.oldAmplitude != self.note.cs.amplitude: + r = self.baseColors[0][0] + int(self.baseColors[1][0]*self.note.cs.amplitude) + g = self.baseColors[0][1] + int(self.baseColors[1][1]*self.note.cs.amplitude) + b = self.baseColors[0][2] + int(self.baseColors[1][2]*self.note.cs.amplitude) + self.color = self.colormap.alloc_color( r, g, b, True, True ) + self.oldAmplitude = self.note.cs.amplitude + + self.updateTransform() + def getId( self ): - return self.note + return self.note.id def getStartTick( self ): - return self.onset + return self.note.cs.onset def getEndTick( self ): - return self.end + return self.end def testOnset( self, start, stop ): - return self.onset >= start and self.onset < stop - + return self.note.cs.onset >= start and self.note.cs.onset < stop + def getPitch( self ): - return self.pitch + return self.note.cs.pitch def updateTransform( self ): - if self.page == self.parent.curPage and not self.firstTransform: + if self.note.page == self.owner.curPage and not self.firstTransform: oldX = self.imgX oldY = self.imgY oldEndX = self.imgX + self.imgWidth - - if self.onset != self.oldOnset: - self.x = self.parent.ticksToPixels( self.onset ) + + if self.note.cs.onset != self.oldOnset: + self.x = self.owner.ticksToPixels( self.noteDB.getPage( self.note.page).beats, self.note.cs.onset ) self.x += self.origin[0] self.imgX = self.x - Config.NOTE_IMAGE_PADDING - self.oldOnset = self.onset - if self.end != self.oldEnd or self.onset != self.oldOnset: - self.width = self.parent.ticksToPixels( self.end ) - self.x - self.origin[0] + self.oldOnset = self.note.cs.onset + if self.end != self.oldEnd or self.note.cs.onset != self.oldOnset: + self.width = self.owner.ticksToPixels( self.noteDB.getPage( self.note.page).beats, self.end ) - self.x - self.origin[0] self.imgWidth = self.width + Config.NOTE_IMAGE_PADDING_MUL2 self.oldEnd = self.end - if self.pitch != self.oldPitch: - self.y = self.parent.pitchToPixels( self.pitch ) + self.origin[1] + if self.note.cs.pitch != self.oldPitch: + self.y = self.owner.pitchToPixels( self.note.cs.pitch ) + self.origin[1] self.imgY = self.y - Config.NOTE_IMAGE_PADDING - self.oldPitch = self.pitch - + self.oldPitch = self.note.cs.pitch + if self.firstTransform: - self.parent.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.page, True ) + self.owner.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page, True ) self.firstTransform = False else: x = min( self.imgX, oldX ) y = min( self.imgY, oldY ) endx = max( self.imgX + self.imgWidth, oldEndX ) endy = max( self.imgY, oldY ) + self.imgHeight - self.parent.invalidate_rect( x, y, endx-x, endy-y, self.page, True ) + self.owner.invalidate_rect( x, y, endx-x, endy-y, self.note.page, True ) def updateDragLimits( self, dragLimits, leftBound, rightBound, widthBound, maxRightBound ): - left = leftBound - self.onset - right = rightBound - self.duration - self.onset - up = Config.MAXIMUM_PITCH - self.pitch - down = Config.MINIMUM_PITCH - self.pitch - short = Config.MINIMUM_NOTE_DURATION - self.duration - long = widthBound - self.duration - self.onset + left = leftBound - self.note.cs.onset + right = rightBound - self.note.cs.duration - self.note.cs.onset + up = Config.MAXIMUM_PITCH - self.note.cs.pitch + down = Config.MINIMUM_PITCH - self.note.cs.pitch + short = Config.MINIMUM_NOTE_DURATION - self.note.cs.duration + long = widthBound - self.note.cs.duration - self.note.cs.onset if dragLimits[0][0] < left: dragLimits[0][0] = left if dragLimits[0][1] > right: dragLimits[0][1] = right @@ -117,9 +115,9 @@ class NoteInterface: if dragLimits[2][1] > long: dragLimits[2][1] = long # store the current loc as a reference point - self.baseOnset = self.onset - self.basePitch = self.pitch - self.baseDuration = self.duration + self.baseOnset = self.note.cs.onset + self.basePitch = self.note.cs.pitch + self.baseDuration = self.note.cs.duration def updateSampleNote( self, pitch ): return @@ -141,93 +139,83 @@ class NoteInterface: return -1 # event occurs before us, no point in checking further if eX > self.width: return 0 # no X overlap - + eY = event.y - self.y if eY < 0 or eY > self.height: return -2 # not a hit, but it was in our X range - + if event.button == 3: - print "Show some note parameters!?!" - #self.noteParameters = NoteParametersWindow( self.note, self.getNoteParameters ) + print "Show some note parameters!?!" + #self.noteParameters = NoteParametersWindow( self.note, self.getNoteParameters ) return 1 # handled if event.type == gtk.gdk._2BUTTON_PRESS: # select bar self.potentialDeselect = False start = 0 - check = self.onset - Config.TICKS_PER_BEAT + check = self.note.cs.onset - Config.TICKS_PER_BEAT while start <= check: start += Config.TICKS_PER_BEAT stop = start + Config.TICKS_PER_BEAT - check += self.duration + check += self.note.cs.duration while stop < check: stop += Config.TICKS_PER_BEAT - emitter.selectNotesByBar( self.track, start, stop ) + emitter.selectNotesByBar( self.note.track, start, stop ) elif event.type == gtk.gdk._3BUTTON_PRESS: # select track self.potentialDeselect = False - emitter.selectNotesByTrack( self.track ) + emitter.selectNotesByTrack( self.note.track ) else: - if self.getSelected(): # we already selected, might want to delected + if self.selected: # we already selected, might want to delected self.potentialDeselect = True else: - emitter.selectNotes( { self.track: [ self ] } ) - self.updateSampleNote( self.pitch ) - + emitter.selectNotes( { self.note.track: [ self ] } ) + self.updateSampleNote( self.note.cs.pitch ) + percent = eX/self.width if percent < 0.3: emitter.setCurrentAction( "note-drag-onset", self ) elif percent > 0.7: emitter.setCurrentAction( "note-drag-duration", self ) else: emitter.setCurrentAction( "note-drag-pitch", self ) - + return 1 def handleButtonRelease( self, emitter, event, buttonPressCount ): if self.potentialDeselect: self.potentialDeselect = False - emitter.deselectNotes( { self.track: [ self ] } ) + emitter.deselectNotes( { self.note.track: [ self ] } ) self.clearSampleNote() - + emitter.doneCurrentAction() return True def noteDrag( self, emitter, do, dp, dd ): self.potentialDeselect = False - changed = False if do != self.lastDragO: self.lastDragO = do - self.onset = self.baseOnset + do - self.end = self.onset + self.duration - changed = True + self.noteDB.updateNote( self.note.page, self.note.track, self.note.id, PARAMETER.ONSET, self.baseOnset + do ) + self.end = self.note.cs.onset + self.note.cs.duration if dp != self.lastDragP: self.lastDragP = dp newPitch = self.basePitch + dp - self.pitch = newPitch + self.noteDB.updateNote( self.note.page, self.note.track, self.note.id, PARAMETER.PITCH, newPitch ) self.updateSampleNote( newPitch ) - changed = True if dd != self.lastDragD: self.lastDragD = dd - self.duration = self.baseDuration + dd - self.end = self.onset + self.duration - changed = True - - self.updateTransform() - - if changed: return (self.note, self.pitch, self.onset, self.duration ) - else: return False + self.noteDB.updateNote( self.note.page, self.note.track, self.note.id, PARAMETER.DURATION, self.baseDuration + dd ) + self.end = self.note.cs.onset + self.note.cs.duration def doneNoteDrag( self, emitter ): - self.baseOnset = self.onset - self.basePitch = self.pitch - self.baseDuration = self.duration - + self.baseOnset = self.note.cs.onset + self.basePitch = self.note.cs.pitch + self.baseDuration = self.note.cs.duration + self.lastDragO = 0 self.lastDragP = 0 self.lastDragD = 0 self.clearSampleNote() - def handleMarqueeSelect( self, emitter, start, stop ): intersectionY = [ max(start[1],self.y), min(stop[1],self.y+self.height) ] @@ -239,7 +227,7 @@ class NoteInterface: return False return True - + # updateTooltip returns: # -2, not a hit but there was X overlap # -1, event occurs before us so don't bother checking any later notes @@ -251,26 +239,26 @@ class NoteInterface: return -1 # event occurs before us, no point in checking further if eX > self.width: return 0 # no X overlap - + eY = event.y - self.y if eY < 0 or eY > self.height: return -2 # not a hit, but it was in our X range - + percent = eX/self.width if percent < 0.3: emitter.setCursor("drag-onset") elif percent > 0.7: emitter.setCursor("drag-duration") else: emitter.setCursor("drag-pitch") - + return 1 # we handled it #======================================================= # Selection - + def setSelected( self, state ): if self.selected != state: self.selected = state - if self.page == self.parent.curPage: - self.parent.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.page ) + if self.note.page == self.owner.curPage: + self.owner.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page ) return True # state changed return False # state is the same @@ -293,4 +281,4 @@ class NoteInterface: win.draw_pixbuf( gc, img, Config.NOTE_IMAGE_TAIL, 0, self.imgX+self.imgWidth-Config.NOTE_IMAGE_ENDLENGTH, self.imgY, Config.NOTE_IMAGE_ENDLENGTH, self.imgHeight, gtk.gdk.RGB_DITHER_NONE ) return True # we drew something - + diff --git a/Edit/TrackInterface.py b/Edit/TrackInterface.py index da205f4..a3b3986 100644 --- a/Edit/TrackInterface.py +++ b/Edit/TrackInterface.py @@ -8,7 +8,6 @@ import Config from Edit.NoteInterface import NoteInterface from Edit.HitInterface import HitInterface from Edit.MainWindow import CONTEXT -#from GUI.Core.NoteParametersWindow import NoteParametersWindow from Util.Profiler import TP @@ -25,37 +24,39 @@ class INTERFACEMODE: DRAW = 1 PASTE = 2 +class TrackInterfaceParasite: + def __init__( self, noteDB, owner, note ): + if note.track == Config.NUMBER_OF_TRACKS-1: # drum track + self.parasite = HitInterface( noteDB, owner, note ) + else: + self.parasite = NoteInterface( noteDB, owner, note ) + + def attach( self ): + return self.parasite + class TrackInterface( gtk.EventBox ): - - def __init__( self, owner ): + + def __init__( self, noteDB, owner ): gtk.EventBox.__init__( self ) + self.noteDB = noteDB self.owner = owner self.drawingArea = gtk.DrawingArea() self.drawingAreaDirty = False # are we waiting to draw? self.add( self.drawingArea ) self.dirtyRectToAdd = gtk.gdk.Rectangle() # used by the invalidate_rect function - + self.fullWidth = 1 # store the maximum allowed width self.width = 1 self.height = 1 self.interfaceMode = INTERFACEMODE.DEFAULT - - self.note = {} # list of pages, tracks, and notes: self.note[pageId][trackId][noteId] - self.pageBeatCount = {} # keep track of the beat count for each page - self.pageNoteCount = {} # keep track of how many notes are on a page (so we can get rid of them when they're empty) - self.noteMap = {} # maps note ids to self.note[p][t][i]s self.curPage = -1 # this isn't a real page at all! - self.beatCount = 4 + self.curBeats = 4 - self.trackSelected = [] - self.selectedNotes = [] - for i in range(0,Config.NUMBER_OF_TRACKS): - self.trackSelected.insert( 0, False ) - self.selectedNotes.insert( 0, [] ) + self.selectedNotes = [ [] for i in range(Config.NUMBER_OF_TRACKS) ] self.curAction = False # stores the current mouse action self.curActionObject = False # stores the object that in handling the action @@ -64,7 +65,7 @@ class TrackInterface( gtk.EventBox ): self.clickLoc = [0,0] # location of the last click self.marqueeLoc = False # current drag location of the marquee self.marqueeRect = [[0,0],[0,0]] - + self.playheadX = Config.TRACK_SPACING_DIV2 self.cursor = { \ @@ -79,7 +80,7 @@ class TrackInterface( gtk.EventBox ): self.add_events(gtk.gdk.POINTER_MOTION_MASK|gtk.gdk.POINTER_MOTION_HINT_MASK) self.connect( "size-allocate", self.size_allocate ) - + self.drawingArea.connect( "expose-event", self.expose ) self.connect( "button-press-event", self.handleButtonPress ) self.connect( "button-release-event", self.handleButtonRelease ) @@ -87,18 +88,18 @@ class TrackInterface( gtk.EventBox ): # prepare drawing stuff hexToInt = { "0":0, "1":1, "2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9, "A":10, "B":11, "C":12, "D":13, "E":14, "F":15, "a":10, "b":11, "c":12, "d":13, "e":14, "f":15 } - self.trackColors = [] + self.trackColors = [] for i in Config.TRACK_COLORS: low = ( 256*(hexToInt[i[0][1]]*16+hexToInt[i[0][2]]), 256*(hexToInt[i[0][3]]*16+hexToInt[i[0][4]]), 256*(hexToInt[i[0][5]]*16+hexToInt[i[0][6]]) ) high = ( 256*(hexToInt[i[1][1]]*16+hexToInt[i[1][2]]), 256*(hexToInt[i[1][3]]*16+hexToInt[i[1][4]]), 256*(hexToInt[i[1][5]]*16+hexToInt[i[1][6]]) ) delta = ( high[0]-low[0], high[1]-low[1], high[2]-low[2] ) self.trackColors.append( (low, delta) ) - + colormap = self.drawingArea.get_colormap() self.beatColor = colormap.alloc_color( Config.BEAT_COLOR, True, True ) self.playheadColor = colormap.alloc_color( Config.PLAYHEAD_COLOR, True, True ) self.marqueeColor = colormap.alloc_color( Config.MARQUEE_COLOR, True, True ) - + self.image = {} img = gtk.Image() win = gtk.gdk.get_default_root_window() @@ -113,7 +114,7 @@ class TrackInterface( gtk.EventBox ): newimg = gtk.Image() newimg.set_from_file( Config.IMAGE_ROOT+name+".png" ) self.image[name] = newimg.get_pixbuf() - + prepareDrawable( "trackBG" ) prepareDrawable( "trackBGSelected" ) prepareDrawable( "trackBGDrum" ) @@ -146,112 +147,47 @@ class TrackInterface( gtk.EventBox ): self.pitchPerPixelDrum = float(Config.NUMBER_OF_POSSIBLE_PITCHES_DRUM-1)*Config.PITCH_STEP_DRUM / (self.trackHeightDrum - Config.HIT_HEIGHT) self.pixelsPerPitchDrum = float(self.trackHeightDrum-Config.HIT_HEIGHT)/(Config.MAXIMUM_PITCH_DRUM - Config.MINIMUM_PITCH_DRUM ) + self.pixelsPerTick = [0] + [ self.trackWidth//(i*Config.TICKS_PER_BEAT) for i in range(1,Config.MAXIMUM_BEATS+1) ] + + self.ticksPerPixel = [0] + [ 1.0/self.pixelsPerTick[i] for i in range(1,Config.MAXIMUM_BEATS+1) ] + + self.beatSpacing = [0] + [ self.pixelsPerTick[i]*Config.TICKS_PER_BEAT for i in range(1,Config.MAXIMUM_BEATS+1) ] + # screen buffers self.screenBuf = [ gtk.gdk.Pixmap( win, self.width, self.height ), \ gtk.gdk.Pixmap( win, self.width, self.height ) ] self.screenBufPage = [ -1, -1 ] + self.screenBufBeats = [ -1, -1 ] self.screenBufDirtyRect = [ gtk.gdk.Rectangle(), gtk.gdk.Rectangle() ] self.screenBufDirty = [ False, False ] self.screenBufResume = [ [0,0], [0,0] ] # allows for stopping and restarting in the middle of a draw self.curScreen = 0 self.preScreen = 1 - #======================================================= # Module Interface - # noteParams: { "page":pagelist, "track":tracklist, "note":noteIDlist } - def addNotes( self, noteParams, noteCount ): - at = {} - - for i in range(noteCount): - p = noteParams["page"][i] - t = noteParams["track"][i] - if p not in at: - at[p] = [0] * Config.NUMBER_OF_TRACKS - if p not in self.note: - self.note[p] = map(lambda x:[], range(Config.NUMBER_OF_TRACKS)) - self.pageBeatCount[p] = noteParams["beatCount"][i] - self.pageNoteCount[p] = 0 - csnote = noteParams["csnote"][i] - if noteParams["track"][i] == self.drumIndex: - note = HitInterface( self, p, noteParams["track"][i], noteParams["note"][i], \ - csnote.pitch, csnote.onset, csnote.duration, csnote.amplitude, \ - self.image["hit"], self.image["hitSelected"], self.trackColors[noteParams["track"][i]] ) - else: - note = NoteInterface( self, p, noteParams["track"][i], noteParams["note"][i], \ - csnote.pitch, csnote.onset, csnote.duration, csnote.amplitude, \ - self.image["note"], self.image["noteSelected"], self.trackColors[noteParams["track"][i]] ) - while at[p][t] > 0: - startT = self.note[p][t][at[p][t]-1].getStartTick() - if startT <= csnote.onset: - if startT < csnote.onset: break - elif self.note[p][t][at[p][t]-1].getPitch <= csnote.pitch: break - at[p][t] -= 1 - last = len(self.note[p][t]) - while at[p][t] < last: - startT = self.note[p][t][at[p][t]].getStartTick() - if startT >= csnote.onset: - if startT > csnote.onset: break - elif self.note[p][t][at[p][t]].getPitch >= csnote.pitch: break - at[p][t] += 1 - self.note[p][t].insert( at[p][t], note ) - self.pageNoteCount[p] += 1 - at[p][t] += 1 # assume the next note will fall after this one - - for page in at: - self.updateNoteMap( page ) - - # noteParams: { "page":pagelist, "track":tracklist, "note":noteIDlist } - def updateNotes( self, noteParams, noteCount ): - for i in range(noteCount): - p = noteParams["page"][i] - t = noteParams["track"][i] - id = noteParams["note"] - csnote = noteParams["csnote"] - self.resortNote( p, t, id, csnote.pitch, csnote.onset ) - self.note[p][t][self.noteMap[p][id]].updateParams( csnote.pitch, csnote.onset, csnote.duration, csnote.amplitude ) - - # noteParams: { "page":pagelist, "track":tracklist, "note":noteIDlist } - def deleteNotes( self, noteParams, noteCount ): - modified = {} - for i in range(noteCount): - p = noteParams["page"][i] - t = noteParams["track"][i] - id = noteParams["note"][i] - if not p in modified: modified[p] = True - if p == self.curPage and self.note[p][t][self.noteMap[p][id]].getSelected(): - self.deselectNotes( { t: [ self.note[p][t][self.noteMap[p][id]] ] } ) - self.note[p][t][self.noteMap[p][id]].destroy() - self.note[p][t][self.noteMap[p][id]] = None # flag for removal - self.pageNoteCount[p] -= 1 - if self.pageNoteCount[p] == 0: - del self.note[p] - del self.pageNoteCount[p] - del self.noteMap[p] - del modified[p] - - for page in modified: - for i in range(Config.NUMBER_OF_TRACKS): - j = len(self.note[page][i])-1 - while j >= 0: - if self.note[page][i][j] == None: del self.note[page][i][j] - j -= 1 - self.updateNoteMap( page ) + def getDrawingPackage( self, track ): + if track == self.drumIndex: + return ( self.image["hit"], self.image["hitSelected"], self.drawingArea.get_colormap(), self.trackColors[track] ) + else: + return ( self.image["note"], self.image["noteSelected"], self.drawingArea.get_colormap(), self.trackColors[track] ) def predrawPage( self, page ): if self.screenBufPage[self.preScreen] != page: self.screenBufPage[self.preScreen] = page + self.screenBufBeats[self.preScreen] = self.noteDB.getPage(page).beats self.invalidate_rect( 0, 0, self.width, self.height, page ) - - def displayPage( self, page, beatCount, predraw = -1 ): - if page == self.curPage and self.beatCount == beatCount: return - + + def displayPage( self, page, predraw = -1 ): + if page == self.curPage: return + if self.curPage >= 0 and self.curPage != page: clearNotes = True else: clearNotes = False - + self.curPage = page - + self.curBeats = self.noteDB.getPage(page).beats + if self.screenBufPage[self.preScreen] == self.curPage: # we predrew this page, so smart! t = self.preScreen self.preScreen = self.curScreen @@ -259,51 +195,22 @@ class TrackInterface( gtk.EventBox ): self.invalidate_rect( 0, 0, self.width, self.height, self.curPage, False ) else: # we need to draw this page from scratch self.screenBufPage[self.curScreen] = self.curPage + self.screenBufBeats[self.curScreen] = self.curBeats self.invalidate_rect( 0, 0, self.width, self.height, self.curPage ) - + if predraw >= 0 and self.screenBufPage[self.preScreen] != predraw: self.screenBufPage[self.preScreen] = predraw + self.screenBufBeats[self.preScreen] = self.noteDB.getPage(predraw).beats self.invalidate_rect( 0, 0, self.width, self.height, predraw ) - + if clearNotes: # clear the notes now that we've sorted out the screen buffers - self.clearSelectedNotes() - - if page not in self.note: # create a blank page if the page doesn't already exist - self.note[page] = [] - for i in range(Config.NUMBER_OF_TRACKS): - self.note[page].append( [] ) - self.pageBeatCount[page] = beatCount - self.pageNoteCount[page] = 0 - - self.updateBeatCount( beatCount ) - - def updateBeatCount( self, beatCount ): - self.beatCount = beatCount - - self.pixelsPerTick = self.trackWidth//(self.beatCount*Config.TICKS_PER_BEAT) - self.ticksPerPixel = 1.0/self.pixelsPerTick - self.beatSpacing = self.pixelsPerTick*Config.TICKS_PER_BEAT - - if self.pageBeatCount[self.curPage] != beatCount: - self.pageBeatCount[self.curPage] = beatCount - for i in range(Config.NUMBER_OF_TRACKS): - track = self.note[self.curPage][i] - map( lambda note:note.updateTransform( True ), track ) - - if self.window != None: - self.invalidate_rect( 0, 0, self.fullWidth, self.height, self.curPage ) - + self.clearSelectedNotes() + def setPlayhead( self, ticks ): self.invalidate_rect( self.playheadX-Config.PLAYHEAD_SIZE/2, 0, Config.PLAYHEAD_SIZE, self.height, self.curPage, False ) - self.playheadX = self.ticksToPixels( ticks ) + Config.TRACK_SPACING_DIV2 + self.playheadX = self.ticksToPixels( ticks, self.screenBufBeats[self.curScreen] ) + Config.TRACK_SPACING_DIV2 self.invalidate_rect( self.playheadX-Config.PLAYHEAD_SIZE/2, 0, Config.PLAYHEAD_SIZE, self.height, self.curPage, False ) - def getSelectedTracks( self ): - r = [] - for i in range( len(self.trackSelected) ): - if self.trackSelected[i]: r.append( i ) - return r - def setInterfaceMode( self, mode ): if mode == "Draw": self.interfaceMode = INTERFACEMODE.DRAW @@ -311,39 +218,6 @@ class TrackInterface( gtk.EventBox ): self.interfaceMode = INTERFACEMODE.PASTE else: self.interfaceMode = INTERFACEMODE.DEFAULT - - # private - def updateNoteMap( self, page ): - self.noteMap[page] = {} - for i in range(Config.NUMBER_OF_TRACKS): - for j in range(len(self.note[page][i])): - self.noteMap[page][self.note[page][i][j].getId()] = j - - def resortNote( self, p, t, id, pitch, onset ): - ins = out = self.noteMap[p][id] - while ins > 0: # check backward - startT = self.note[p][t][ins-1].getStartTick() - if startT >= onset: - if startT > onset or self.note[p][t][ins-1].getPitch() < pitch: ins -= 1 - else: break - else: break - if ins == out: # check forward - while ins < len(self.note[p][t])-1: - startT = self.note[p][t][ins+1].getStartTick() - if startT <= onset: - if startT < onset or self.note[p][t][ins+1].getPitch() >= pitch: ins += 1 - else: break - else: break - if ins != out: # resort - if ins > out: - for j in range( out+1, ins+1 ): - self.noteMap[p][self.note[p][t][j].getId()] -= 1 - else: - for j in range( ins, out ): - self.noteMap[p][self.note[p][t][j].getId()] += 1 - self.noteMap[p][id] = ins - n = self.note[p][t].pop( out ) - self.note[p][t].insert( ins, n ) #======================================================= # Event Callbacks @@ -354,7 +228,7 @@ class TrackInterface( gtk.EventBox ): height = allocation.height self.drawingArea.set_size_request( width, height ) - + if self.window != None: self.invalidate_rect( 0, 0, width, height, self.curPage, False ) @@ -367,19 +241,19 @@ class TrackInterface( gtk.EventBox ): else: self.buttonPressCount = 1 self.clickLoc = [ int(event.x), int(event.y) ] - + # check if we clicked on the playhead if event.x >= self.playheadX and event.x <= self.playheadX + Config.PLAYHEAD_SIZE: self.setCurrentAction( "playhead-drag", self ) TP.ProfileEnd( "TI::handleButtonPress" ) - return + return for i in range(Config.NUMBER_OF_TRACKS): if self.trackLimits[i][0] > event.y: break if self.trackLimits[i][1] < event.y: continue - + handled = 0 - notes = self.note[self.curPage][i] + notes = self.noteDB.getNotesByTrack( self.curPage, i, self ) last = len(notes)-1 for n in range(last+1): if i == self.drumIndex and n < last: # check to see if the next hit overlaps this one @@ -394,7 +268,7 @@ class TrackInterface( gtk.EventBox ): return else: # all other options mean we can stop looking break - + if self.interfaceMode == INTERFACEMODE.DRAW: if handled == -1: # event occured before this note and didn't overlap with the previous note, so we can draw print "draw a note" @@ -413,12 +287,12 @@ class TrackInterface( gtk.EventBox ): if not self.curAction: #do track selection stuff here so that we can also handle marquee selection for i in range(Config.NUMBER_OF_TRACKS): if self.trackLimits[i][0] > event.y: break - if self.trackLimits[i][1] < event.y: continue + if self.trackLimits[i][1] < event.y: continue if event.button == 1: - if self.buttonPressCount == 1: self.toggleTrack( i, False ) - else: self.toggleTrack( i, True ) + if self.buttonPressCount == 1: self.owner.toggleTrack( i, False ) + else: self.owner.toggleTrack( i, True ) break - + TP.ProfileEnd( "TI::handleButtonRelease" ) return @@ -451,37 +325,37 @@ class TrackInterface( gtk.EventBox ): event.state = state TP.ProfileEnd( "TI::handleMotion::Common" ) - + if event.state & gtk.gdk.BUTTON1_MASK: TP.ProfileBegin( "TI::handleMotion::Drag" ) - + if not self.curAction: # no action is in progress yet we're dragging, start a marquee self.setCurrentAction( "marquee", self ) - if self.curAction == "note-drag-onset": + if self.curAction == "note-drag-onset": self.noteDragOnset( event ) - elif self.curAction == "note-drag-duration": + elif self.curAction == "note-drag-duration": self.noteDragDuration( event ) - elif self.curAction == "note-drag-pitch": + elif self.curAction == "note-drag-pitch": self.noteDragPitch( event ) - + elif self.curAction == "note-drag-pitch-drum": self.noteDragPitch( event, True ) - elif self.curAction == "marquee": + elif self.curAction == "marquee": self.updateMarquee( event ) - + elif self.curAction == "playhead-drag": self.updatePlayhead( event ) - + TP.ProfileEnd( "TI::handleMotion::Drag" ) else: TP.ProfileBegin( "TI::handleMotion::Hover" ) self.updateTooltip( event ) TP.ProfileEnd( "TI::handleMotion::Hover" ) - + return #======================================================= @@ -508,23 +382,9 @@ class TrackInterface( gtk.EventBox ): self.curAction = False self.curActionObject = False - def toggleTrack( self, trackN, exclusive ): - if exclusive: - for i in range(Config.NUMBER_OF_TRACKS): - self.trackSelected[i] = False - self.trackSelected[trackN] = True - self.invalidate_rect( 0, 0, self.width, self.height, self.curPage ) - self.owner.setContextState( CONTEXT.TRACK, True ) - self.owner.setContext( CONTEXT.TRACK ) - else: - self.trackSelected[trackN] = not self.trackSelected[trackN] - self.invalidate_rect( 0, self.trackLimits[trackN][0], self.width, self.trackLimits[trackN][1]-self.trackLimits[trackN][0], self.curPage ) - for i in range(Config.NUMBER_OF_TRACKS): - if self.trackSelected[i]: - self.owner.setContextState( CONTEXT.TRACK, True ) - self.owner.setContext( CONTEXT.TRACK ) - return - self.owner.setContextState( CONTEXT.TRACK, False ) + def trackToggled( self, trackN = -1 ): + if trackN == -1: self.invalidate_rect( 0, 0, self.width, self.height, self.curPage ) + else: self.invalidate_rect( 0, self.trackLimits[trackN][0], self.width, self.trackLimits[trackN][1]-self.trackLimits[trackN][0], self.curPage ) def selectionChanged( self ): if self.curAction == "note-drag-onset": self.updateDragLimits() @@ -538,24 +398,22 @@ class TrackInterface( gtk.EventBox ): return self.owner.setContextState( CONTEXT.NOTE, False ) - def applyNoteSelection( self, mode, trackN, which ): if mode == SELECTNOTES.ALL: - track = self.note[self.curPage][trackN] + track = self.noteDB.getNotesByTrack( self.curPage, trackN, self ) map( lambda note:note.setSelected( True ), track ) self.selectedNotes[trackN] = [] map( lambda note:self.selectedNotes[trackN].append(note), track ) elif mode == SELECTNOTES.NONE: - if self.note.has_key(self.curPage): - track = self.note[self.curPage][trackN] - map( lambda note:note.setSelected( False ), track ) + track = self.noteDB.getNotesByTrack( self.curPage, trackN, self ) + map( lambda note:note.setSelected( False ), track ) self.selectedNotes[trackN] = [] elif mode == SELECTNOTES.ADD: - for note in which: - if note.setSelected( True ): + for note in which: + if note.setSelected( True ): self.selectedNotes[trackN].append( note ) elif mode == SELECTNOTES.REMOVE: - for note in which: + for note in which: if note.setSelected( False ): self.selectedNotes[trackN].remove( note ) elif mode == SELECTNOTES.FLIP: @@ -567,28 +425,28 @@ class TrackInterface( gtk.EventBox ): note.setSelected( True ) self.selectedNotes[trackN].append( note ) elif mode == SELECTNOTES.EXCLUSIVE: - notes = self.note[self.curPage][trackN] + notes = self.noteDB.getNotesByTrack( self.curPage, trackN, self ) for n in range(len(notes)): - if notes[n] in which: + if notes[n] in which: if notes[n].setSelected( True ): self.selectedNotes[trackN].append( notes[n] ) - else: + else: if notes[n].setSelected( False ): self.selectedNotes[trackN].remove( notes[n] ) def selectNotesByBar( self, trackN, start, stop ): for i in range(Config.NUMBER_OF_TRACKS): - if i == trackN: + if i == trackN: notes = [] - track = self.note[self.curPage][trackN] + track = self.noteDB.getNotesByTrack( self.curPage, trackN, self ) for n in range(len(track)): if track[n].testOnset( start, stop ): notes.append(track[n]) if not Config.ModKeys.ctrlDown: self.applyNoteSelection( SELECTNOTES.EXCLUSIVE, trackN, notes ) - else: self.applyNoteSelection( SELECTNOTES.ADD, trackN, notes ) + else: self.applyNoteSelection( SELECTNOTES.ADD, trackN, notes ) else: if not Config.ModKeys.ctrlDown: self.applyNoteSelection( SELECTNOTES.NONE, i, [] ) self.selectionChanged() - + def selectNotesByTrack( self, trackN ): if Config.ModKeys.ctrlDown: self.applyNoteSelection( SELECTNOTES.ALL, trackN, [] ) @@ -609,7 +467,7 @@ class TrackInterface( gtk.EventBox ): self.selectionChanged() def deselectNotes( self, noteDic ): - for i in noteDic: + for i in noteDic: self.applyNoteSelection( SELECTNOTES.REMOVE, i, noteDic[i] ) self.selectionChanged() @@ -620,12 +478,12 @@ class TrackInterface( gtk.EventBox ): def updateDragLimits( self ): self.dragLimits = [ [-9999,9999], [-9999,9999], [-9999,9999] ] # initialize to big numbers! - maxRightBound = self.beatCount * Config.TICKS_PER_BEAT - + maxRightBound = self.curBeats * Config.TICKS_PER_BEAT + for i in range(Config.NUMBER_OF_TRACKS): if not len(self.selectedNotes[i]): continue # no selected notes here - track = self.note[self.curPage][i] + track = self.noteDB.getNotesByTrack( self.curPage, i, self ) leftBound = 0 skip = True # skip the first note for n in range(len(track)): @@ -634,10 +492,10 @@ class TrackInterface( gtk.EventBox ): thisNote = track[n] continue nextNote = track[n] - if not thisNote.getSelected(): + if not thisNote.getSelected(): leftBound = thisNote.getEndTick() else: - if not nextNote.getSelected(): + if not nextNote.getSelected(): rightBound = min( nextNote.getStartTick(), maxRightBound ) widthBound = rightBound else: @@ -646,37 +504,29 @@ class TrackInterface( gtk.EventBox ): thisNote.updateDragLimits( self.dragLimits, leftBound, rightBound, widthBound, maxRightBound ) thisNote = nextNote # do the last note - if thisNote.getSelected(): + if thisNote.getSelected(): thisNote.updateDragLimits( self.dragLimits, leftBound, maxRightBound, maxRightBound, maxRightBound ) def noteDragOnset( self, event ): - do = self.pixelsToTicks( event.x - self.clickLoc[0] ) + do = self.pixelsToTicks( self.curBeats, event.x - self.clickLoc[0] ) do = min( self.dragLimits[0][1], max( self.dragLimits[0][0], do ) ) dp = 0 dd = 0 - + for i in range(Config.NUMBER_OF_TRACKS): - changed = [] for note in self.selectedNotes[i]: - ret = note.noteDrag(self, do, dp, dd) - if ret: - if i == self.drumIndex: self.resortNote( self.curPage, i, ret[0], ret[1], ret[2] ) - changed += [ret] - if len(changed): self.owner.onNoteDrag( changed ) + note.noteDrag(self, do, dp, dd) def noteDragDuration( self, event ): do = 0 dp = 0 - dd = self.pixelsToTicks( event.x - self.clickLoc[0] ) + dd = self.pixelsToTicks( self.curBeats, event.x - self.clickLoc[0] ) dd = min( self.dragLimits[2][1], max( self.dragLimits[2][0], dd ) ) for i in range(Config.NUMBER_OF_TRACKS): - changed = [] for note in self.selectedNotes[i]: - ret = note.noteDrag(self, do, dp, dd) - if ret: changed += [ret] - self.owner.onNoteDrag( changed ) - + note.noteDrag(self, do, dp, dd) + def noteDragPitch( self, event, drum = False ): do = 0 if not drum: dp = self.pixelsToPitch( event.y - self.clickLoc[1] ) @@ -685,13 +535,8 @@ class TrackInterface( gtk.EventBox ): dd = 0 for i in range(Config.NUMBER_OF_TRACKS): - changed = [] for note in self.selectedNotes[i]: - ret = note.noteDrag(self, do, dp, dd) - if ret: - if i == self.drumIndex: self.resortNote( self.curPage, i, ret[0], ret[1], ret[2] ) - changed += [ret] - self.owner.onNoteDrag( changed ) + note.noteDrag(self, do, dp, dd) def doneNoteDrag( self ): for i in range(Config.NUMBER_OF_TRACKS): @@ -708,7 +553,7 @@ class TrackInterface( gtk.EventBox ): oldX = oldEndX = self.clickLoc[0] oldY = oldEndY = self.clickLoc[1] - self.marqueeLoc = [ int(event.x), int(event.y) ] + self.marqueeLoc = [ int(event.x), int(event.y) ] if self.marqueeLoc[0] < 0: self.marqueeLoc[0] = 0 elif self.marqueeLoc[0] > self.width: self.marqueeLoc[0] = self.width if self.marqueeLoc[1] < 0: self.marqueeLoc[1] = 0 @@ -726,90 +571,89 @@ class TrackInterface( gtk.EventBox ): else: self.marqueeRect[0][1] = self.marqueeLoc[1] self.marqueeRect[1][1] = self.clickLoc[1] - self.marqueeLoc[1] - + x = min( self.marqueeRect[0][0], oldX ) width = max( self.marqueeRect[0][0] + self.marqueeRect[1][0], oldEndX ) - x y = min( self.marqueeRect[0][1], oldY ) height = max( self.marqueeRect[0][1] + self.marqueeRect[1][1], oldEndY ) - y self.invalidate_rect( x-1, y-1, width+2, height+2, self.curPage, False ) - def doneMarquee( self, event ): + def doneMarquee( self, event ): if self.marqueeLoc: stop = [ self.marqueeRect[0][0] + self.marqueeRect[1][0], self.marqueeRect[0][1] + self.marqueeRect[1][1] ] select = {} - + for i in range(Config.NUMBER_OF_TRACKS): intersectionY = [ max(self.marqueeRect[0][1],self.trackLimits[i][0]), min(stop[1],self.trackLimits[i][1]) ] if intersectionY[0] > intersectionY[1]: continue - + notes = [] - track = self.note[self.curPage][i] + track = self.noteDB.getNotesByTrack( self.curPage, i, self ) for n in range(len(track)): - hit = track[n].handleMarqueeSelect( self, + hit = track[n].handleMarqueeSelect( self, [ self.marqueeRect[0][0], intersectionY[0] ], \ - [ stop[0], intersectionY[1] ] ) + [ stop[0], intersectionY[1] ] ) if hit: notes.append(track[n]) if len(notes): select[i] = notes - + self.selectNotes( select ) - self.marqueeLoc = False + self.marqueeLoc = False self.doneCurrentAction() - + self.invalidate_rect( self.marqueeRect[0][0]-1, self.marqueeRect[0][1]-1, self.marqueeRect[1][0]+2, self.marqueeRect[1][1]+2, self.curPage, False ) - + def updatePlayhead( self, event ): - x = min( self.trackWidth - self.pixelsPerTick, max( Config.TRACK_SPACING_DIV2, event.x ) ) - self.setPlayhead( self.pixelsToTicks( x ) ) - - + x = min( self.trackWidth - self.pixelsPerTick[self.curBeats], max( Config.TRACK_SPACING_DIV2, event.x ) ) + self.setPlayhead( self.pixelsToTicks( self.curBeats, x ) ) + def donePlayhead( self, event ): - x = min( self.trackWidth - self.pixelsPerTick, max( Config.TRACK_SPACING_DIV2, event.x ) ) - ticks = self.pixelsToTicks( x ) - print "set playhead to %d ticks" % (ticks) + x = min( self.trackWidth - self.pixelsPerTick[self.curBeats], max( Config.TRACK_SPACING_DIV2, event.x ) ) + ticks = self.pixelsToTicks( self.curBeats, x ) + print "set playhead to %d ticks" % (ticks) self.doneCurrentAction() - + def updateTooltip( self, event ): - + # check clicked the playhead if event.x >= self.playheadX and event.x <= self.playheadX + Config.PLAYHEAD_SIZE: self.setCursor("drag-playhead") - return - + return + for i in range(Config.NUMBER_OF_TRACKS): if self.trackLimits[i][0] > event.y: break if self.trackLimits[i][1] < event.y: continue - - notes = self.note[self.curPage][i] + + notes = self.noteDB.getNotesByTrack( self.curPage, i, self ) handled = 0 for n in range(len(notes)): handled = notes[n].updateTooltip( self, event ) if handled == 0: continue elif handled == 1: return # event was handled else: break - + # note wasn't handled, could potentially draw a note if self.interfaceMode == INTERFACEMODE.DRAW: if handled == -2: # event X overlapped with a note self.setCursor("default") return - + self.setCursor("pencil") return - + break - + self.setCursor("default") - + def setCursor( self, cursor ): self.window.set_cursor(self.cursor[cursor]) - + #======================================================= # Drawing - + def predraw( self, buf, noescape = True ): TP.ProfileBegin( "TrackInterface::predraw" ) @@ -818,12 +662,16 @@ class TrackInterface( gtk.EventBox ): stopX = self.screenBufDirtyRect[buf].x + self.screenBufDirtyRect[buf].width stopY = self.screenBufDirtyRect[buf].y + self.screenBufDirtyRect[buf].height + beatStart = Config.TRACK_SPACING_DIV2 + beats = self.screenBufBeats[buf] + beatSpacing = self.beatSpacing[beats] + pixmap = self.screenBuf[buf] resume = self.screenBufResume[buf] self.gc.set_clip_rectangle( self.screenBufDirtyRect[buf] ) - + self.gc.set_line_attributes( Config.BEAT_LINE_SIZE, gtk.gdk.LINE_ON_OFF_DASH, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER ) # regular tracks for i in range( resume[0], self.drumIndex ): @@ -832,21 +680,20 @@ class TrackInterface( gtk.EventBox ): if stopY < self.trackLimits[i][0]: break # draw background - if self.trackSelected[i]: + if self.owner.getTrackSelected( i ): pixmap.draw_drawable( self.gc, self.image["trackBGSelected"], 0, 0, 0, self.trackLimits[i][0], self.trackFullWidth, self.trackFullHeight ) else: pixmap.draw_drawable( self.gc, self.image["trackBG"], 0, 0, 0, self.trackLimits[i][0], self.trackFullWidth, self.trackFullHeight ) - + # draw beat lines self.gc.foreground = self.beatColor - beatStart = Config.TRACK_SPACING_DIV2 - for j in range(1,self.beatCount): - x = beatStart + j*self.beatSpacing + for j in range(1,self.screenBufBeats[buf]): + x = beatStart + j*beatSpacing pixmap.draw_line( self.gc, x, self.trackRect[i].y, x, self.trackRect[i].y+self.trackRect[i].height ) - + # draw notes TP.ProfileBegin("TI::draw notes") - notes = self.note[self.curPage][i] + notes = self.noteDB.getNotesByTrack( self.curPage, i, self ) for n in range( resume[1], len(notes) ): # check escape if 0: @@ -854,33 +701,32 @@ class TrackInterface( gtk.EventBox ): resume[1] = n TP.ProfilePause( "TrackInterface::predraw" ) return False - + if not notes[n].draw( pixmap, self.gc, startX, stopX ): break TP.ProfileEnd("TI::draw notes") # finished a track, reset the resume values for the next one resume[0] = 0 - resume[1] = 0 - + resume[1] = 0 + # drum track if stopY > self.trackLimits[self.drumIndex][0]: - - if resume[0] == 0: + + if resume[0] == 0: # draw background - if self.trackSelected[self.drumIndex]: + if self.owner.getTrackSelected( self.drumIndex ): pixmap.draw_drawable( self.gc, self.image["trackBGDrumSelected"], 0, 0, 0, self.trackLimits[self.drumIndex][0], self.trackFullWidth, self.trackFullHeightDrum ) else: pixmap.draw_drawable( self.gc, self.image["trackBGDrum"], 0, 0, 0, self.trackLimits[self.drumIndex][0], self.trackFullWidth, self.trackFullHeightDrum ) - + # draw beat lines self.gc.foreground = self.beatColor - beatStart = Config.TRACK_SPACING_DIV2 - for j in range(1,self.beatCount): - x = beatStart + j*self.beatSpacing + for j in range(1,self.screenBufBeats[buf]): + x = beatStart + j*beatSpacing pixmap.draw_line( self.gc, x, self.trackRect[self.drumIndex].y, x, self.trackRect[self.drumIndex].y+self.trackRect[self.drumIndex].height ) - + # draw notes - notes = self.note[self.curPage][self.drumIndex] + notes = self.noteDB.getNotesByTrack( self.curPage, self.drumIndex, self ) for n in range( resume[1], len(notes) ): # check escape if 0: @@ -889,27 +735,27 @@ class TrackInterface( gtk.EventBox ): TP.ProfilePause( "TrackInterface::predraw" ) return False if not notes[n].draw( pixmap, self.gc, startX, stopX ): break - + self.screenBufDirty[buf] = False - + TP.ProfileEnd( "TrackInterface::predraw" ) - + return True - def expose( self, DA, event ): - + def expose( self, DA, event ): + if self.screenBufDirty[self.curScreen]: self.predraw( self.curScreen ) - + TP.ProfileBegin( "TrackInterface::expose" ) - + startX = event.area.x startY = event.area.y stopX = event.area.x + event.area.width stopY = event.area.y + event.area.height - + #print "%d %d %d %d" % (startX,startY,stopX,stopY) - + self.gc.set_clip_rectangle( event.area ) # draw base @@ -919,22 +765,23 @@ class TrackInterface( gtk.EventBox ): self.gc.set_line_attributes( Config.PLAYHEAD_SIZE, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER ) self.gc.foreground = self.playheadColor DA.window.draw_line( self.gc, self.playheadX, startY, self.playheadX, stopY ) - + if self.marqueeLoc: # draw the selection rect self.gc.set_line_attributes( Config.MARQUEE_SIZE, gtk.gdk.LINE_ON_OFF_DASH, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER ) self.gc.foreground = self.marqueeColor DA.window.draw_rectangle( self.gc, False, self.marqueeRect[0][0], self.marqueeRect[0][1], self.marqueeRect[1][0], self.marqueeRect[1][1] ) - + self.drawingAreaDirty = False - TP.ProfileEnd( "TrackInterface::expose" ) - + TP.ProfileEnd( "TrackInterface::expose" ) + def invalidate_rect( self, x, y, width, height, page, base = True ): + #print "%d %d %d %d Page %d CurPage %d" % (x,y,width,height,page,self.curPage) self.dirtyRectToAdd.x = x self.dirtyRectToAdd.y = y self.dirtyRectToAdd.width = width self.dirtyRectToAdd.height = height - + #print "dirty %d %d %d %d %d %d" % (x, y, width, height, x+width, y+height) if page == self.curPage: if base: # the base image has been dirtied @@ -950,7 +797,7 @@ class TrackInterface( gtk.EventBox ): if self.drawingArea.window != None: self.drawingArea.window.invalidate_rect( self.dirtyRectToAdd, True ) self.drawingAreaDirty = True - + elif page == self.screenBufPage[self.preScreen]: if not self.screenBufDirty[self.preScreen]: self.screenBufDirtyRect[self.preScreen].x = x @@ -961,16 +808,16 @@ class TrackInterface( gtk.EventBox ): self.screenBufDirtyRect[self.preScreen] = self.screenBufDirtyRect[self.preScreen].union( self.dirtyRectToAdd ) self.screenBufResume[self.preScreen] = [0,0] self.screenBufDirty[self.preScreen] = True - + #self.queue_draw() def getTrackOrigin( self, track ): return ( self.trackRect[track].x, self.trackRect[track].y ) - def ticksToPixels( self, ticks ): - return int(round( ticks * self.pixelsPerTick )) - def pixelsToTicks( self, pixels ): - return int(round( pixels * self.ticksPerPixel )) + def ticksToPixels( self, beats, ticks ): + return int(round( ticks * self.pixelsPerTick[beats] )) + def pixelsToTicks( self, beats, pixels ): + return int(round( pixels * self.ticksPerPixel[beats] )) def pitchToPixels( self, pitch ): return int(round( ( Config.MAXIMUM_PITCH - pitch ) * self.pixelsPerPitch )) def pixelsToPitch( self, pixels ): diff --git a/Edit/TuneInterface.py b/Edit/TuneInterface.py index fd9bc3a..e6790b9 100644 --- a/Edit/TuneInterface.py +++ b/Edit/TuneInterface.py @@ -7,45 +7,63 @@ import Config from Util.Profiler import TP from Edit.MainWindow import CONTEXT +class TuneInterfaceParasite: + + def __init__( self, noteDB, owner, note ): + self.parasite = self + self.noteDB = noteDB + self.owner = owner + self.note = note + + def attach( self ): + return self + + def destroy( self ): + return + + def updateParameter( self, parameter, value ): + return + class TuneInterface( gtk.EventBox ): - + + DRAG_BLOCK = -1 # block other drag events DRAG_SELECT = 1 DRAG_DESELECT = 2 DRAG_MOVE = 3 - - def __init__( self, owner ): + + def __init__( self, noteDB, owner ): gtk.EventBox.__init__( self ) - + + self.noteDB = noteDB self.owner = owner - + self.drawingArea = gtk.DrawingArea() self.drawingAreaDirty = False # is the drawingArea waiting to draw? self.add( self.drawingArea ) self.dirtyRectToAdd = gtk.gdk.Rectangle() # used by the invalidate_rect function - self.pages = [] self.selectedIds = [] self.displayedPage = -1 - + self.alloced = False self.width = self.baseWidth = self.height = -1 self.waitingForAlloc = True self.scrollTo = -1 self.clickX = -1 - + self.set_size_request( self.width, self.height ) self.pageSpacing = Config.PAGE_THUMBNAIL_WIDTH + Config.PAGE_THUMBNAIL_PADDING_MUL2 self.pageOffset = Config.PAGE_THUMBNAIL_PADDING + Config.PAGE_THUMBNAIL_PADDING_DIV2 - + self.dragMode = None self.dropAt = -1 - + self.connect( "size-allocate", self.size_allocated ) self.drawingArea.connect( "expose-event", self.draw ) self.connect( "button-press-event", self.handleButtonPress ) self.connect( "button-release-event", self.handleButtonRelease ) self.connect( "motion-notify-event", self.handleMotion ) - + def size_allocated( self, widget, allocation ): if not self.alloced: self.baseWidth = allocation.width @@ -54,35 +72,37 @@ class TuneInterface( gtk.EventBox ): self.width = allocation.width self.height = allocation.height self.drawingArea.set_size_request( self.width, self.height ) - + self.pageY = (self.height-Config.PAGE_THUMBNAIL_HEIGHT)//2 - + if self.scrollTo >= 0: self.owner.scrollTune( self.scrollTo ) self.scrollTo = -1 - + self.waitingForAlloc = False def updateSize( self ): if not self.alloced: return - width = len(self.pages)*(Config.PAGE_THUMBNAIL_WIDTH + Config.PAGE_THUMBNAIL_PADDING_MUL2) + Config.PAGE_THUMBNAIL_PADDING_MUL2 + width = self.noteDB.getPageCount()*(Config.PAGE_THUMBNAIL_WIDTH + Config.PAGE_THUMBNAIL_PADDING_MUL2) + Config.PAGE_THUMBNAIL_PADDING_MUL2 self.waitingForAlloc = True self.set_size_request( max( self.baseWidth, width), -1 ) def handleButtonPress( self, widget, event ): ind = int(event.x-self.pageOffset)//self.pageSpacing - if ind >= len(self.pages): return + if ind >= self.noteDB.getPageCount(): + self.dragMode = self.DRAG_BLOCK + return if ind < 0: ind = 0 self.clickX = event.x - id = self.pages[ ind ] + id = self.noteDB.getPageByIndex( ind ) if event.type == gtk.gdk._2BUTTON_PRESS: # double click -> exclusive select self.selectPage( id ) self.owner.displayPage( id ) else: - if Config.ModKeys.ctrlDown: + if Config.ModKeys.ctrlDown: if id in self.selectedIds: # ctrl click, selected page -> remove page from selection if self.deselectPage( id ): self.dragMode = self.DRAG_DESELECT @@ -98,141 +118,101 @@ class TuneInterface( gtk.EventBox ): self.owner.displayPage( id ) self.owner.setContext( CONTEXT.PAGE ) - + def handleButtonRelease( self, widget, event ): - + if self.dragMode == self.DRAG_MOVE: self.invalidate_rect( 0, 0, self.width, self.height ) # drop head - self.owner.movePages( self.dropAt, self.selectedIds ) + + if self.dropAt > 0: after = self.noteDB.getPageByIndex( self.dropAt-1 ) + else: after = False + + self.noteDB.movePages( self.selectedIds, after ) self.dropAt = -1 - + self.dragMode = None - + def handleMotion( self, widget, event ): - - if Config.ModKeys.ctrlDown and (self.dragMode == None or self.dragMode == self.DRAG_MOVE): + + if Config.ModKeys.ctrlDown and (self.dragMode == None or self.dragMode == self.DRAG_MOVE): self.dropAt = -1 self.dragMode = self.DRAG_SELECT - + if self.dragMode == self.DRAG_SELECT: # select on drag ind = int(event.x-self.pageOffset)//self.pageSpacing - if ind < len(self.pages): self.selectPage( self.pages[ind], False ) - elif self.dragMode == self.DRAG_DESELECT: # deselect on drag + if ind < self.noteDB.getPageCount(): self.selectPage( self.noteDB.getPageByIndex(ind), False ) + elif self.dragMode == self.DRAG_DESELECT: # deselect on drag ind = int(event.x-self.pageOffset)//self.pageSpacing - if ind < len(self.pages): self.deselectPage( self.pages[ind] ) + if ind < self.noteDB.getPageCount(): self.deselectPage( self.noteDB.getPageByIndex(ind) ) elif self.dragMode == None and abs(self.clickX-event.x) > 20: # drag and drop self.dragMode = self.DRAG_MOVE - + if self.dragMode == self.DRAG_MOVE: self.dropAt = int(event.x-self.pageOffset+Config.PAGE_THUMBNAIL_WIDTH_DIV2)//self.pageSpacing - if self.dropAt > len(self.pages): self.dropAt = len(self.pages) + c = self.noteDB.getPageCount() + if self.dropAt > c: self.dropAt = c self.invalidate_rect( 0, 0, self.width, self.height ) - + def displayPage( self, id, scroll ): if self.displayedPage == id: return -1 - + self.displayedPage = id + + if id not in self.selectedIds: + self.selectPage( id ) - for ind in range(len(self.pages)): - if self.pages[ind] == id: break + ind = self.noteDB.getPageIndex( id ) startX = self.pageOffset + ind*self.pageSpacing stopX = startX + self.pageSpacing self.invalidate_rect( 0, 0, self.width, self.height ) - + if scroll > startX: scroll = startX elif scroll + self.baseWidth < stopX: - scroll = stopX - self.baseWidth + scroll = stopX - self.baseWidth if self.waitingForAlloc: - self.scrollTo = scroll - return -1 + self.scrollTo = scroll + return -1 else: scroll = stopX - self.baseWidth return scroll - - def insertPages( self, ids, insert, select = True, exclusive = True ): - if exclusive: - self.clearSelection() - for id in ids: - self.insertPage( id, insert, select, False, False ) - insert += 1 - - self.updateSize() - - def insertPage( self, id, insert, select = True, exclusive = True, resize = True ): - self.pages.insert( insert, id ) - - if select: self.selectPage( id, exclusive ) - else: self.invalidate_rect( 0, 0, self.width, self.height ) - - if resize: self.updateSize() - - def removePages( self, ids ): - for id in ids: - self.removePage( id, False ) - - self.updateSize() - - def removePage( self, id, resize = True ): - - if id in self.selectedIds: self.selectedIds.remove(id) - - self.pages.remove(id) - - self.invalidate_rect( 0, 0, self.width, self.height ) - - if resize: self.updateSize() - - def movePage( self, remove, insert ): - if remove == insert: return - elif remove < insert: - insert -= 1 - if remove == insert: return - - id = self.pages[remove] - self.pages.pop(remove) - self.pages.insert( insert, id ) - - self.invalidate_rect( 0, 0, self.width, self.height ) def selectPage( self, id, exclusive = True ): if exclusive: self.selectedIds = [] - + if id in self.selectedIds: return False # no change - + + ind = self.noteDB.getPageIndex( id ) l = len(self.selectedIds) - j = 0 - for i in range(len(self.pages)): - if j == l: break - if self.pages[i] == self.selectedIds[j]: j += 1 - elif id == self.pages[i]: break - - self.selectedIds.insert( j, id ) - + i = 0 # in case len(self.selectedIds) == 0 + while i < l: + if self.noteDB.getPageIndex( self.selectedIds[i] ) > ind: break + i += 1 + + self.selectedIds.insert( i, id ) + self.invalidate_rect( 0, 0, self.width, self.height ) - + return True # page added to selection def deselectPage( self, id, force = False ): if not id in self.selectedIds: return False # page isn't selected - + if not force: if len(self.selectedIds) <= 1: return False # don't deselect the last page if self.displayedPage == id: - for i in range(len(self.selectedIds)): - if self.selectedIds[i] == id: - if i == 0: self.owner.displayPage( self.selectedIds[1] ) - else: self.owner.displayPage( self.selectedIds[i-1] ) - break - - self.selectedIds.remove( id ) + i = self.selectedIds.index(id) + if i == 0: self.owner.displayPage( self.selectedIds[1] ) + else: self.owner.displayPage( self.selectedIds[i-1] ) + + self.selectedIds.remove( id ) self.invalidate_rect( 0, 0, self.width, self.height ) - + return True # page removed from the selection - + def clearSelection( self ): self.selectedIds = [] @@ -241,23 +221,34 @@ class TuneInterface( gtk.EventBox ): def getSelectedIds( self ): return self.selectedIds - def getAllIds( self ): - return self.pages - def getLastSelected( self ): - if len(self.selectedIds): - id = self.selectedIds[-1] - i = 0 - for pageId in self.pages: - if pageId == id: break - i += 1 - return i - else: return 0 - + return self.selectedIds[-1] + + #======================================================= + # NoteDB notifications + + def notifyPageAdd( self, id, at ): + self.selectPage( id ) + self.updateSize() + + def notifyPageDelete( self, which, safe ): + for id in self.selectedIds: + if id in which: self.deselectPage( id, True ) + self.updateSize() + + def notifyPageDuplicate( self, new, at ): + self.clearSelection() + for k in new.keys(): + self.selectPage( new[k], False ) + self.updateSize() + + def notifyPageMove( self, which, low, high ): + self.invalidate_rect( 0, 0, self.width, self.height ) + #======================================================= # Drawing - def draw( self, drawingArea, event ): + def draw( self, drawingArea, event ): startX = event.area.x startY = event.area.y @@ -266,7 +257,7 @@ class TuneInterface( gtk.EventBox ): context = drawingArea.window.cairo_create() context.set_antialias(0) # I don't know what to set this to to turn it off, and it doesn't seem to work anyway!? - context.set_line_width( 2 ) + context.set_line_width( 2 ) context.move_to( 0, 0 ) context.rel_line_to( self.width, 0 ) @@ -276,13 +267,13 @@ class TuneInterface( gtk.EventBox ): #draw background context.set_source_rgb( 0.75, 0.05, 0.0 ) - context.fill_preserve() - + context.fill_preserve() + # draw pages x = Config.PAGE_THUMBNAIL_PADDING_MUL2 # double padding on first page! l = len(self.selectedIds) j = 0 - for pageId in self.pages: + for pageId in self.noteDB.getTune(): if pageId == self.displayedPage: # displayed page border context.set_source_rgb( 1.0, 1.0, 1.0 ) if j<l and self.selectedIds[j] == pageId: j += 1 @@ -297,24 +288,23 @@ class TuneInterface( gtk.EventBox ): context.rel_line_to( 0, Config.PAGE_THUMBNAIL_HEIGHT ) context.rel_line_to( -Config.PAGE_THUMBNAIL_WIDTH, 0 ) context.close_path() - context.stroke() - + context.stroke() + x += self.pageSpacing - + # draw drop marker if self.dropAt >= 0: - context.set_line_width( Config.PAGE_THUMBNAIL_PADDING ) + context.set_line_width( Config.PAGE_THUMBNAIL_PADDING ) context.set_source_rgb( 0.0, 0.0, 0.0 ) context.move_to( Config.PAGE_THUMBNAIL_PADDING + self.pageSpacing*self.dropAt, self.pageY - 4 ) context.rel_line_to( 0, Config.PAGE_THUMBNAIL_HEIGHT + 8 ) - context.stroke() + context.stroke() def invalidate_rect( self, x, y, width, height ): - if self.alloced == False: return + if self.alloced == False: return self.dirtyRectToAdd.x = x self.dirtyRectToAdd.y = y self.dirtyRectToAdd.width = width self.dirtyRectToAdd.height = height self.drawingArea.window.invalidate_rect( self.dirtyRectToAdd, True ) self.drawingAreaDirty = True - #self.queue_draw() diff --git a/Util/NoteDB.py b/Util/NoteDB.py new file mode 100644 index 0000000..ff6f2d4 --- /dev/null +++ b/Util/NoteDB.py @@ -0,0 +1,461 @@ + +import Config + +class PARAMETER: + ONSET = 0 + PITCH = 1 + AMPLITUDE = 2 + DURATION = 3 + +class Note: + def __init__( self, page, track, id, cs ): + self.page = page + self.track = track + self.id = id + self.cs = cs + +class Page: + def __init__( self, beats ): + self.beats = beats + self.ticks = beats*Config.TICKS_PER_BEAT + self.nextNoteId = 0 # first note will be 1 + + def genId( self ): + self.nextNoteId += 1 + if self.nextNoteId == 65536: # short + print "note id overflow!" + # TODO think of how to handle this!? + return self.nextNoteId + +class NoteDB: + def __init__( self ): + self.noteD = {} # bins containing all the notes by page, track, and id + # structure self.noteD[pageId][trackIndex][noteId] + + self.noteS = {} # bins containing all the notes by page and track + # first sorted by onset then by pitch (for drum hits) + # structure self.noteS[pageId][trackIndex][noteIndex] + + self.pages = {} # dict of Pages indexed by pageId + + self.tune = [] # list of pageIds ordered by tune + + self.listeners = [] # complete list of listeners + self.pageListeners = [] # list of listeners who want page notifications + self.noteListeners = [] # list of listeners who want note notifications + self.parasiteList = {} # dict of parasites indexed by listener + + self.parasiteD = {} # bin of parasites indexed by listener + self.parasiteS = {} # parasites sorted as in self.noteS, + + self.nextId = 0 # base id, first page will be 1 + + #-- private -------------------------------------------- + def _genId( self ): + self.nextId += 1 + if self.nextId == 65536: # short + print "page id overflow!" + # TODO think of how to handle this!? + return self.nextId + + #======================================================= + # Page Functions + + def addPage( self, beats, after = False ): + id = self._newPage( beats ) + at = self._insertPage( id, after ) + + for l in self.pageListeners: + l.notifyPageAdd( id, at ) + + return id + + def deletePages( self, which ): + beats = self.pages[self.tune[0]].beats + + low = 999999 + ind = -1 + for id in which: + ind = self.tune.index(id) + if ind < low: low = ind + + for t in range(Config.NUMBER_OF_TRACKS): + for n in self.noteD[id][t].keys(): + self.deleteNote( id, t, n ) + + del self.noteD[id] + del self.noteS[id] + del self.parasiteD[id] + del self.parasiteS[id] + del self.pages[id] + + at = self.tune.index( id ) + self.tune.pop(at) + + if not len(self.tune): + self.addPage( beats ) # always have at least one page + safe = self.tune[0] + else: + safe = self.tune[max(ind-1,0)] + + for l in self.pageListeners: + l.notifyPageDelete( which, safe ) + + def duplicatePages( self, which, after = False ): + sorted = [] + if after: first = self.tune.index(after)+1 + else: first = 0 + + i = j = 0 + while i < len(self.tune) and j < len(which): + if self.tune[i] in which: + sorted.append(self.tune[i]) + j += 1 + i += 1 + + new = {} + for cp in sorted: + id = self._newPage( self.pages[cp].beats ) + for t in range(Config.NUMBER_OF_TRACKS): + for n in self.noteD[cp][t].keys(): + self.duplicateNote( cp, t, n, id, t, 0 ) + self._insertPage( id, after ) + after = id + new[cp] = id + + for l in self.pageListeners: + l.notifyPageDuplicate( new, first ) + + def movePages( self, which, after = False ): + sorted = [] + if after: at = self.tune.index(after)+1 + else: at = 0 + low = high = at + + i = j = 0 + while i < len(self.tune): + if self.tune[i] in which: + sorted.append(self.tune[i]) + self.tune.pop(i) + if i < low: low = i + if i > high: high = i + if i < at: at -= 1 + j += 1 + else: + i += 1 + + self.tune = self.tune[:at] + sorted + self.tune[at:] + + for l in self.pageListeners: + l.notifyPageMove( sorted, low, high ) + + #-- private -------------------------------------------- + def _newPage( self, beats ): + id = self._genId() + self.pages[id] = Page( beats ) + self.noteD[id] = [ {} for i in range(Config.NUMBER_OF_TRACKS) ] + self.noteS[id] = [ [] for i in range(Config.NUMBER_OF_TRACKS) ] + self.parasiteD[id] = [ {} for i in range(Config.NUMBER_OF_TRACKS) ] + self.parasiteS[id] = [ {} for i in range(Config.NUMBER_OF_TRACKS) ] + for i in range(Config.NUMBER_OF_TRACKS): + for par in self.parasiteList.keys(): + self.parasiteD[id][i][par] = {} + self.parasiteS[id][i][par] = [] + return id + + def _insertPage( self, id, after ): + if not after: at = 0 + else: at = self.tune.index(after)+1 + self.tune.insert( at, id ) + + return at + + + #======================================================= + # Track Functions + + #def deleteTracks( self, pages, which ): + + #def duplicateTracks( self, pages, which, insert ): + + + #======================================================= + # Note Functions + + def addNote( self, page, track, cs, hint = False ): + id = self.pages[page].genId() + n = self.noteD[page][track][id] = Note( page, track, id, cs ) + + if not hint: at = 0 + else: at = hint[0] + while at > 0: + onset = self.noteS[page][track][at-1].cs.onset + if onset <= cs.onset: + if onset < cs.onset: break + elif self.noteS[page][track][at-1].cs.pitch <= cs.pitch: break + at -= 1 + last = len(self.noteS[page][track]) + while at < last: + onset = self.noteS[page][track][at].cs.onset + if onset >= cs.onset: + if onset > cs.onset: break + elif self.noteS[page][track][at].cs.pitch >= cs.pitch: break + at += 1 + + self.noteS[page][track].insert( at, n ) + + for par in self.parasiteList.keys(): + parasite = self.parasiteList[par]( self, par, n ) + self.parasiteD[page][track][par][id] = parasite.attach() # give parasites the option of return something other than themselves + self.parasiteS[page][track][par].insert( at, parasite.attach() ) + + if hint: hint[0] = at + 1 # assume the next note will fall after this one + + for l in self.noteListeners: + l.notifyNoteAdd( id ) + + return id + + # stream format: + # page id + # track index + # number of following notes (N) + # cs pointer + # ... up to N + # page id or -1 to exit + def addNotes( self, stream ): + i = [-1] + p = self._readstream(stream,i) + while p != -1: + t = self._readstream(stream,i) + N = self._readstream(stream,i) + hint = [0] + for j in range(N): + self.addNote( p, t, self._readstream(stream,i), hint ) + p = self._readstream(stream,i) + + def deleteNote( self, page, track, id ): + ind = self.noteS[page][track].index( self.noteD[page][track][id] ) + + for par in self.parasiteList.keys(): + self.parasiteD[page][track][par][id].destroy() + self.parasiteS[page][track][par].pop(ind) + del self.parasiteD[page][track][par][id] + + self.noteS[page][track].pop(ind) + del self.noteD[page][track][id] + + for l in self.noteListeners: + l.notifyNoteDelete( id ) + + # stream format: + # page id + # track index + # number of following notes (N) + # note id + # ... up to N + # page id or -1 to exit + def deleteNotes( self, stream ): + i = [-1] + p = self._readstream(stream,i) + while p != -1: + t = self._readstream(stream,i) + N = self._readstream(stream,i) + for j in range(N): + self.deleteNote( p, t, self._readstream(stream,i) ) + p = self._readstream(stream,i) + + def deleteNotesByTrack( self, page, track ): + notes = self.noteS[page][track][:] + for n in notes: + self.deleteNote( page, track, n.id ) + + def duplicateNote( self, page, track, id, toPage, toTrack, offset ): + cs = self.noteD[page][track][id].cs.clone() + cs.track = toTrack + cs.onset += offset + ticks = self.pages[toPage].ticks + if cs.onset >= ticks: return False # off the end of the page + if cs.onset + cs.duration > ticks: + cs.duration = ticks - cs.onset + + return self.addNote( toPage, toTrack, cs ) + + # stream format: + # page id + # track index + # toPage id + # toTrack index + # offset + # number of following notes (N) + # note id + # ... up to N + # page id or -1 to exit + def duplicateNotes( self, stream ): + i = [-1] + p = self._readstream(stream,i) + while p != -1: + t = self._readstream(stream,i) + toP = self._readstream(stream,i) + toT = self._readstream(stream,i) + offset = self._readstream(stream,i) + N = self._readstream(stream,i) + for j in range(N): + self.duplicateNote( p, t, self._readstream(stream,i), toP, toT, offset ) + p = self._readstream(stream,i) + + + def updateNote( self, page, track, id, parameter, value ): + if parameter == PARAMETER.ONSET: + self.noteD[page][track][id].cs.onset = value + self._resortNote( page, track, id ) + elif parameter == PARAMETER.PITCH: + self.noteD[page][track][id].cs.pitch= value + self._resortNote( page, track, id ) + elif parameter == PARAMETER.AMPLITUDE: + self.noteD[page][track][id].cs.amplitude = value + elif parameter == PARAMETER.DURATION: + self.noteD[page][track][id].cs.duration = value + + for par in self.parasiteList.keys(): + self.parasiteD[page][track][par][id].updateParameter( parameter, value ) + + for l in self.noteListeners: + l.notifyNoteUpdate( id, parameter, value ) + + # stream format: + # page id + # track index + # parameter id + # number of following notes (N) + # note id + # value + # ... up to N + # page id or -1 to exit + def updateNotes( self, stream ): + i = [-1] + p = self._readstream(stream,i) + while p != -1: + t = self._readstream(stream,i) + param = self._readstream(stream,i) + N = self._readstream(stream,i) + for j in range(N): + self.updateNote( p, t, self._readstream(stream,i), param, stream[inc(i)] ) + p = self._readstream(stream,i) + + #-- private -------------------------------------------- + def _readstream( self, stream, i ): + i[0] += 1 + return stream[i[0]] + + def _resortNote( self, page, track, id ): + ins = out = self.noteS[page][track].index(self.noteD[page][track][id]) + cs = self.noteD[page][track][id].cs + while ins > 0: # check backward + onset = self.noteS[page][track][ins-1].cs.onset + if onset <= cs.onset: + if onset < cs.onset: break + elif self.noteS[page][track][ins-1].cs.pitch <= cs.pitch: break + ins -= 1 + if ins == out: # check forward + ins += 1 + last = len(self.noteS[page][track]) + while ins < last: + onset = self.noteS[page][track][ins].cs.onset + if onset >= cs.onset: + if onset > cs.onset: break + elif self.noteS[page][track][ins].cs.pitch >= cs.pitch: break + ins += 1 + + if ins != out: # resort + if ins > out: ins -= 1 + n = self.noteS[page][track].pop( out ) + self.noteS[page][track].insert( ins, n ) + for par in self.parasiteList.keys(): + p = self.parasiteS[page][track][par].pop( out ) + self.parasiteS[page][track][par].insert( ins, p ) + + + #======================================================= + # Listener Functions + + def addListener( self, listener, parasite = None, page = False, note = False ): + if listener in self.listeners: + return # only one listener per object + + if parasite: + self.parasiteList[listener] = parasite + self._addParasite( listener, parasite ) + + if page: self.pageListeners.append( listener ) + if note: self.noteListeners.append( listener ) + self.listeners.append( listener ) + + def deleteListener( self, listener ): + self.listeners.remove( listener ) + if listener in self.pageListeners: + self.pageListeners.remove( listener ) + if listener in self.noteListeners: + self.noteListeners.remove( listener ) + if self.parasites.has_key( listener ): + self._deleteParasite( listener ) + del self.parasiteList[listener] + + #-- private -------------------------------------------- + def _addParasite( self, listener, parasite ): + for p in self.tune: + for t in range(Config.NUMBER_OF_TRACKS): + self.parasiteD[p][t][listener] = {} + self.parasiteS[p][t][listener] = [] + for n in self.noteD[p][t].keys(): + parasite( self, listener, self.noteD[p][t][n] ) + self.parasiteD[p][t][listener][n] = parasite.attach() # give parasites the option of returning something other than themselves + self.parasiteS[p][t][listener].insert( self.noteS[p][t].index( self.noteD[p][t][n]), parasite.attach() ) + + def _deleteParasite( self, listener ): + for p in self.tune: + for t in range(Config.NUMBER_OF_TRACKS): + for n in self.notes[p][t].keys(): + self.parasiteD[p][t][listener][n].destroy() + del self.parasiteD[p][t][listener] + del self.parasiteS[p][t][listener] + + #======================================================= + # Get Functions + + def getPageCount( self ): + return len(self.pages) + + def getTune( self ): + return self.tune + + def getPage( self, page ): + return self.pages[page] + + def getPageByIndex( self, ind ): + return self.tune[ind] + + def getPageIndex( self, page ): + return self.tune.index(page) + + def getNotesByPage( self, page, listener = None ): + notes = [] + if listener: + for i in range(Config.NUMBER_OF_TRACKS): + notes.extend( self.parasiteS[page][i][listener] ) + else: + for i in range(Config.NUMBER_OF_TRACKS): + notes.extend( self.noteS[page][i] ) + return notes + + def getNotesByTrack( self, page, track, listener = None ): + if listener: + return self.parasiteS[page][track][listener] + else: + return self.noteS[page][track] + + def getCSNotesByPage( self, page ): + return map( lambda n: n.cs, self.getNotesByPage( page ) ) + + def getCSNotesByTrack( self, page, track ): + return map( lambda n: n.cs, self.getNotesByTrack( page, track ) ) |