Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Config.py23
-rw-r--r--Edit/HitInterface.py57
-rw-r--r--Edit/MainWindow.py350
-rw-r--r--Edit/NoteInterface.py97
-rw-r--r--Edit/TrackInterface.py420
-rw-r--r--Edit/TuneInterface.py89
-rw-r--r--Player/KeyboardStandAlone.py2
-rw-r--r--Player/StandalonePlayer.py2
-rw-r--r--Player/Trackpad.py84
-rw-r--r--Resources/univorc.csd14
-rw-r--r--SynthLab/Parameter.py2
-rw-r--r--SynthLab/SynthLabParametersWindow.py3
-rw-r--r--SynthLab/SynthLabWindow.py27
-rw-r--r--Util/CSoundClient.py8
-rw-r--r--Util/Clooper/Makefile2
-rw-r--r--Util/Clooper/SClient.py2
-rw-r--r--Util/Clooper/SoundClient.cpp39
-rw-r--r--Util/Clooper/SoundClient.h2
-rwxr-xr-xUtil/Clooper/_SClient.sobin132068 -> 133980 bytes
-rwxr-xr-xUtil/Clooper/_ttest.sobin39840 -> 0 bytes
-rw-r--r--Util/NoteDB.py164
-rw-r--r--scripts/clean_trailing_whitespace.py2
22 files changed, 1143 insertions, 246 deletions
diff --git a/Config.py b/Config.py
index c35c59f..8c1ca45 100644
--- a/Config.py
+++ b/Config.py
@@ -389,11 +389,16 @@ HIT_IMAGE_PADDING = 6
HIT_IMAGE_PADDING_MUL2 = HIT_IMAGE_PADDING*2
TRACK_SPACING = 4
TRACK_SPACING_DIV2 = TRACK_SPACING//2
-TRACK_COLORS = [ ( "#00591B", "#00E847" ), \
- ( "#6F1200", "#E72500" ), \
- ( "#004682", "#0090EA" ), \
- ( "#716D00", "#F9EF00" ), \
- ( "#37187B", "#4A00ED" ) ]
+TRACK_COLORS = [ ( "#00290B", "#00E847" ), \
+ ( "#3F0200", "#E72500" ), \
+ ( "#002642", "#0090EA" ), \
+ ( "#313D00", "#F9EF00" ), \
+ ( "#17083B", "#4A00ED" ) ]
+#TRACK_COLORS = [ ( "#00591B", "#00E847" ), \
+# ( "#6F1200", "#E72500" ), \
+# ( "#004682", "#0090EA" ), \
+# ( "#716D00", "#F9EF00" ), \
+# ( "#37187B", "#4A00ED" ) ]
BEAT_COLOR = "#999999"
BEAT_LINE_SIZE = 2
PLAYHEAD_COLOR = "#666666"
@@ -472,12 +477,12 @@ PLAYER_TEMPO_UPPER = 200
DEFAULT_VOLUME = 80
#NUMERICAL CONSTANTS
-NUMBER_OF_POSSIBLE_PITCHES = 25.0
-MINIMUM_PITCH = 24.0
+NUMBER_OF_POSSIBLE_PITCHES = 25
+MINIMUM_PITCH = 24
MAXIMUM_PITCH = MINIMUM_PITCH + NUMBER_OF_POSSIBLE_PITCHES - 1
-NUMBER_OF_POSSIBLE_PITCHES_DRUM = 13.0
+NUMBER_OF_POSSIBLE_PITCHES_DRUM = 13
PITCH_STEP_DRUM = 2
-MINIMUM_PITCH_DRUM = 24.0
+MINIMUM_PITCH_DRUM = 24
MAXIMUM_PITCH_DRUM = MINIMUM_PITCH_DRUM + PITCH_STEP_DRUM*(NUMBER_OF_POSSIBLE_PITCHES_DRUM - 1)
MINIMUM_NOTE_DURATION = 1 # ticks
MS_PER_MINUTE = 60000.0
diff --git a/Edit/HitInterface.py b/Edit/HitInterface.py
index 1301a33..1b12b1f 100644
--- a/Edit/HitInterface.py
+++ b/Edit/HitInterface.py
@@ -18,10 +18,14 @@ class HitInterface( NoteInterface ):
self.updateTransform()
def updateTransform( self ):
- if self.note.page == self.owner.curPage and not self.firstTransform:
- oldX = self.imgX
- oldY = self.imgY
- oldEndX = self.imgX + self.imgWidth
+ if self.note.page in self.owner.getActivePages():
+ if not self.firstTransform:
+ oldX = self.imgX
+ oldY = self.imgY
+ oldEndX = self.imgX + self.imgWidth
+ dirty = True
+ else:
+ dirty = False
if self.note.cs.onset != self.oldOnset:
self.x = self.owner.ticksToPixels( self.noteDB.getPage(self.note.page).beats, self.note.cs.onset )
@@ -33,7 +37,7 @@ class HitInterface( NoteInterface ):
self.imgY = self.y - Config.NOTE_IMAGE_PADDING
self.oldPitch = self.note.cs.pitch
- if self.note.page == self.owner.curPage:
+ if dirty:
if self.firstTransform:
self.owner.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page )
self.firstTransform = False
@@ -107,19 +111,42 @@ class HitInterface( NoteInterface ):
return 1
- def noteDrag( self, emitter, do, dp, dd ):
+ def noteDragPitch( self, dp, stream ):
self.potentialDeselect = False
-
- if do != self.lastDragO:
- self.lastDragO = do
- 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.noteDB.updateNote( self.note.page, self.note.track, self.note.id, PARAMETER.PITCH, newPitch )
- self.updateSampleNote( newPitch )
+ stream += [ self.note.id, self.basePitch + dp ]
+
+ def noteDragDuration( self, dd, stream ):
+ return
+
+ def noteDecOnset( self, step, leftBound, stream ):
+ if self.selected:
+ if leftBound < self.note.cs.onset:
+ onset = max( self.note.cs.onset+step, leftBound )
+ stream += [ self.note.id, onset ]
+ return leftBound
+
+ def noteIncOnset( self, step, rightBound, stream ):
+ if self.selected:
+ if rightBound > self.end:
+ onset = min( self.end+step, rightBound ) - self.note.cs.duration
+ stream += [ self.note.id, onset ]
+ return rightBound
+
+ def noteDecPitch( self, step, stream ):
+ if self.note.cs.pitch > Config.MINIMUM_PITCH_DRUM:
+ stream += [ self.note.id, max( self.note.cs.pitch+2*step, Config.MINIMUM_PITCH_DRUM ) ]
+
+ def noteIncPitch( self, step, stream ):
+ if self.note.cs.pitch < Config.MAXIMUM_PITCH_DRUM:
+ stream += [ self.note.id, min( self.note.cs.pitch+2*step, Config.MAXIMUM_PITCH_DRUM ) ]
+
+ def noteDecDuration( self, step, stream ):
+ return
+
+ def noteIncDuration( self, step, rightBound, stream ):
+ return
# updateTooltip returns:
# -1, event occurs before us so don't bother checking any later notes
diff --git a/Edit/MainWindow.py b/Edit/MainWindow.py
index 2a8465b..4514f3e 100644
--- a/Edit/MainWindow.py
+++ b/Edit/MainWindow.py
@@ -207,17 +207,17 @@ class MainWindow( gtk.EventBox ):
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["2toolBox"].set_size_request( 154, -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["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["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
+ contextWidth = 674
self.GUI["2contextBox"] = formatRoundBox( RoundFixed(), "#6C9790" )
self.GUI["2contextBox"].set_size_request( contextWidth, -1 )
self.GUI["2contextPrevButton"] = gtk.Button("<")
@@ -229,48 +229,92 @@ class MainWindow( gtk.EventBox ):
# + + + page box
self.GUI["2pageBox"] = gtk.HBox()
self.GUI["2pageBox"].set_size_request( contextWidth-50, -1 )
+ self.GUI["2pageGenerateButton"] = gtk.Button("Gen")
+ self.GUI["2pageGenerateButton"].connect( "clicked", lambda a1:self.pageGenerate() )
+ self.GUI["2pageBox"].pack_start( self.GUI["2pageGenerateButton"] )
+ self.GUI["2pagePropertiesButton"] = gtk.Button("Prop")
+ self.GUI["2pagePropertiesButton"].connect( "clicked", lambda a1:self.pageProperties() )
+ self.GUI["2pageBox"].pack_start( self.GUI["2pagePropertiesButton"] )
self.GUI["2pageDeleteButton"] = gtk.Button("Delete")
- self.GUI["2pageDeleteButton"].connect( "clicked", lambda a1:self.removePages() )
+ self.GUI["2pageDeleteButton"].connect( "clicked", lambda a1:self.pageDelete() )
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["2pageDuplicateButton"].connect( "clicked", lambda a1:self.pageDuplicate() )
self.GUI["2pageBox"].pack_start( self.GUI["2pageDuplicateButton"] )
+ self.GUI["2pageNewButton"] = gtk.Button("New")
+ self.GUI["2pageNewButton"].connect( "clicked", lambda a1:self.pageAdd() )
+ self.GUI["2pageBox"].pack_start( self.GUI["2pageNewButton"] )
+ self.GUI["2pageBeatsButton"] = gtk.Button("Beats")
+ self.GUI["2pageBeatsButton"].connect( "clicked", lambda a1:self.pageBeats() )
+ self.GUI["2pageBox"].pack_start( self.GUI["2pageBeatsButton"] )
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["2trackGenerateButton"] = gtk.Button("tGen")
+ self.GUI["2trackGenerateButton"].connect( "clicked", lambda a1:self.trackGenerate() )
+ self.GUI["2trackBox"].pack_start( self.GUI["2trackGenerateButton"] )
+ self.GUI["2trackPropertiesButton"] = gtk.Button("tProp")
+ self.GUI["2trackPropertiesButton"].connect( "clicked", lambda a1:self.trackProperties() )
+ self.GUI["2trackBox"].pack_start( self.GUI["2trackPropertiesButton"] )
self.GUI["2trackDeleteButton"] = gtk.Button("tDelete")
- self.GUI["2trackDeleteButton"].connect( "clicked", lambda a1:self.removePages() )
+ self.GUI["2trackDeleteButton"].connect( "clicked", lambda a1:self.trackDelete() )
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["2trackDuplicateButton"] = gtk.ToggleButton("tDuplicate")
+ self.GUI["2trackDuplicateButton"].connect( "toggled", lambda a1:self.trackDuplicate() )
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["2noteGenerateButton"] = gtk.Button("nGen")
+ self.GUI["2noteGenerateButton"].connect( "clicked", lambda a1:self.noteGenerate() )
+ self.GUI["2noteBox"].pack_start( self.GUI["2noteGenerateButton"] )
+ self.GUI["2notePropertiesButton"] = gtk.Button("nProp")
+ self.GUI["2notePropertiesButton"].connect( "clicked", lambda a1:self.noteProperties() )
+ self.GUI["2noteBox"].pack_start( self.GUI["2notePropertiesButton"] )
self.GUI["2noteDeleteButton"] = gtk.Button("nDelete")
- self.GUI["2noteDeleteButton"].connect( "clicked", lambda a1:self.removePages() )
+ self.GUI["2noteDeleteButton"].connect( "clicked", lambda a1:self.noteDelete() )
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["2noteDuplicateButton"] = gtk.ToggleButton("nDuplicate")
+ self.GUI["2noteDuplicateButton"].connect( "toggled", self.noteDuplicateWidget )
self.GUI["2noteBox"].pack_start( self.GUI["2noteDuplicateButton"] )
+ self.GUI["2noteOnsetBox"] = gtk.HBox()
+ self.GUI["2noteOnsetMinusButton"] = gtk.Button("<")
+ self.GUI["2noteOnsetMinusButton"].connect( "clicked", lambda a1:self.trackInterface.noteStepOnset(-1) )
+ self.GUI["2noteOnsetBox"].pack_start( self.GUI["2noteOnsetMinusButton"] )
+ self.GUI["2noteOnsetPlusButton"] = gtk.Button(">")
+ self.GUI["2noteOnsetPlusButton"].connect( "clicked", lambda a1:self.trackInterface.noteStepOnset(1) )
+ self.GUI["2noteOnsetBox"].pack_start( self.GUI["2noteOnsetPlusButton"] )
+ self.GUI["2noteBox"].pack_start( self.GUI["2noteOnsetBox"] )
+ self.GUI["2notePitchBox"] = gtk.VBox()
+ self.GUI["2notePitchPlusButton"] = gtk.Button("^")
+ self.GUI["2notePitchPlusButton"].connect( "clicked", lambda a1:self.trackInterface.noteStepPitch(1) )
+ self.GUI["2notePitchBox"].pack_start( self.GUI["2notePitchPlusButton"] )
+ self.GUI["2notePitchMinusButton"] = gtk.Button("v")
+ self.GUI["2notePitchMinusButton"].connect( "clicked", lambda a1:self.trackInterface.noteStepPitch(-1) )
+ self.GUI["2notePitchBox"].pack_start( self.GUI["2notePitchMinusButton"] )
+ self.GUI["2noteBox"].pack_start( self.GUI["2notePitchBox"] )
+ self.GUI["2noteDurationBox"] = gtk.HBox()
+ self.GUI["2noteDurationMinusButton"] = gtk.Button("<")
+ self.GUI["2noteDurationMinusButton"].connect( "clicked", lambda a1:self.trackInterface.noteStepDuration(-1) )
+ self.GUI["2noteDurationBox"].pack_start( self.GUI["2noteDurationMinusButton"] )
+ self.GUI["2noteDurationPlusButton"] = gtk.Button(">")
+ self.GUI["2noteDurationPlusButton"].connect( "clicked", lambda a1:self.trackInterface.noteStepDuration(1) )
+ self.GUI["2noteDurationBox"].pack_start( self.GUI["2noteDurationPlusButton"] )
+ self.GUI["2noteBox"].pack_start( self.GUI["2noteDurationBox"] )
+ self.GUI["2noteVolumeBox"] = gtk.VBox()
+ self.GUI["2noteVolumePlusButton"] = gtk.Button("^")
+ self.GUI["2noteVolumePlusButton"].connect( "clicked", lambda a1:self.trackInterface.noteStepVolume(0.05) )
+ self.GUI["2noteVolumeBox"].pack_start( self.GUI["2noteVolumePlusButton"] )
+ self.GUI["2noteVolumeMinusButton"] = gtk.Button("v")
+ self.GUI["2noteVolumeMinusButton"].connect( "clicked", lambda a1:self.trackInterface.noteStepVolume(-0.05) )
+ self.GUI["2noteVolumeBox"].pack_start( self.GUI["2noteVolumeMinusButton"] )
+ self.GUI["2noteBox"].pack_start( self.GUI["2noteVolumeBox"] )
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")
@@ -311,6 +355,10 @@ class MainWindow( gtk.EventBox ):
self.currentpageId = 0
self.playingTuneIdx = 0
+ # timers
+ self.predrawTimeout = False
+ self.playbackTimeout = False
+
# FPS stuff
self.fpsTotalTime = 0
self.fpsFrameCount = 0
@@ -323,7 +371,7 @@ class MainWindow( gtk.EventBox ):
init_data() #above
init_GUI() #above
-
+
self.csnd.setMasterVolume( self.getVolume() )
for tid in range(Config.NUMBER_OF_TRACKS):
@@ -376,7 +424,10 @@ class MainWindow( gtk.EventBox ):
notes += self.noteDB.getCSNotesByTrack( page, track )
self.playing = True
- self.playbackTimeout = gobject.timeout_add( 100, self.onTimeout )
+ if self.predrawTimeout:
+ gobject.source_remove( self.predrawTimeout )
+ self.predrawTimeout = False
+ self.playbackTimeout = gobject.timeout_add( 50, self.onTimeout )
if len(self.pages_playing) > 1:
self.displayPage( self.pages_playing[0], self.pages_playing[1] )
@@ -407,6 +458,7 @@ class MainWindow( gtk.EventBox ):
else: #stop
gobject.source_remove( self.playbackTimeout )
+ self.playbackTimeout = False
if False:
#This is causing csound to stop working...
# reimplement this with real CSoundNotes and it should be ok.
@@ -426,10 +478,22 @@ class MainWindow( gtk.EventBox ):
curtick = self.csnd.loopGetTick()
curIdx = curtick / ( 4 * Config.TICKS_PER_BEAT) #TODO handle each pages_playing length
- 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 )
+ # TODO update playhead
+
+ if self.pages_playing[curIdx] != self.displayedPage:
+ 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 )
+ else:
+ self.trackInterface.predrawPage( time.time() + 0.020 ) # 20 ms time limit
+
+ return True
+
+ def onPredrawTimeout( self ):
+ if self.trackInterface.predrawPage( time.time() + 0.020 ): # 20 ms time limit
+ self.predrawTimeout = False
+ return False
return True
def onMuteTrack( self, widget, trackId ):
@@ -478,6 +542,10 @@ class MainWindow( gtk.EventBox ):
def handleToolClick( self, widget, mode ):
if widget.get_active(): self.trackInterface.setInterfaceMode( mode )
+ def getTool( self ):
+ if self.GUI["2toolPointerButton"].get_active(): return "default"
+ else: return "draw"
+
def onKeyboardButton( self, widget, data ):
self.kb_active = widget.get_active()
@@ -488,11 +556,6 @@ class MainWindow( gtk.EventBox ):
#-----------------------------------
# generation functions
#-----------------------------------
- def handleGenerate( self, widget, data ):
- #if widget.get_active():
- self.generationParametersWindow.show_all()
- #else:
- # self.handleCloseGeneratonParametersWindow()
def handleCloseGenerationParametersWindow( self, widget = None, data = None ):
self.generationParametersWindow.hide_all()
@@ -507,11 +570,19 @@ class MainWindow( gtk.EventBox ):
for p in range(Config.NUMBER_OF_PAGES):
dict[t][p] = []
- if self.trackSelected == [ 0 for i in range(Config.NUMBER_OF_TRACKS) ]:
+ if self.generateMode == "note":
+ # unsupported!?
+ self.handleCloseGenerationParametersWindow( None, None )
+ return
+ elif self.generateMode == "track":
+ 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()
+ else: # page mode
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()
+ newpages = self.tuneInterface.getSelectedIds()
algo(
params,
@@ -532,17 +603,21 @@ class MainWindow( gtk.EventBox ):
note.pageId = page
note.trackId = track
- # add the new notes
+ # prepare the new notes
newnotes = []
for tid in dict:
for pid in dict[tid]:
newnotes += dict[tid][pid]
# delete the notes and add the new
+ if self.generateMode == "note":
+ print "TODO generateMode == note"
+ else: # track or page mode
+ self.noteDB.deleteNotesByTrack( newpages, newtracks )
+
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]
@@ -557,14 +632,96 @@ class MainWindow( gtk.EventBox ):
self.recompose( variate, params)
#=======================================================
+ # Clipboard Functions
+
+ def getClipboardArea( self, page = -1 ):
+ if page == -1: page = self.displayedPage
+ ids = self.tuneInterface.getSelectedIds()
+ return self.noteDB.getClipboardArea( ids.index(page) )
+
+ def pasteClipboard( self, offset, trackMap ):
+ pages = self.tuneInterface.getSelectedIds()
+ return self.noteDB.pasteClipboard( pages, offset, trackMap )
+
+ def cleanupClipboard( self ):
+ if self.GUI["2noteDuplicateButton"].get_active():
+ self.GUI["2noteDuplicateButton"].set_active(False)
+ if self.GUI["2trackDuplicateButton"].get_active():
+ self.GUI["2trackDuplicateButton"].set_active(False)
+ self.trackInterface.donePaste()
+
+
+ #=======================================================
+ # Note Functions
+
+ def noteGenerate( self ):
+ self.generateMode = "note"
+ self.generationParametersWindow.show_all()
+ return
+
+ def noteProperties( self ):
+ # TODO
+ return
+
+ def noteDelete( self ):
+ ids = self.trackInterface.getSelectedNotes()
+ stream = []
+ for t in range(Config.NUMBER_OF_TRACKS):
+ N = len(ids[t])
+ if not N: continue
+ stream += [ self.displayedPage, t, N ] + ids[t]
+ if len(stream):
+ self.noteDB.deleteNotes( stream + [-1] )
+
+ def noteDuplicate( self ):
+ ids = self.trackInterface.getSelectedNotes()
+ stream = []
+ for t in range(Config.NUMBER_OF_TRACKS):
+ N = len(ids[t])
+ if not N: continue
+ stream += [ self.displayedPage, t, N ] + ids[t]
+ if len(stream):
+ if self.GUI["2trackDuplicateButton"].get_active():
+ self.GUI["2trackDuplicateButton"].set_active( False )
+ self.noteDB.notesToClipboard( stream + [-1] )
+ self.trackInterface.setInterfaceMode("paste_notes")
+ return True
+ return False
+
+ def noteDuplicateWidget( self, widget ):
+ if widget.get_active():
+ if self.noteDuplicate(): # duplicate succeeded
+ return
+ # cancel duplicate
+ self.trackInterface.setInterfaceMode("tool")
+ widget.set_active(False)
+
+ def noteOnset( self, step ):
+ self.trackInterface.noteStepOnset( step )
+
+ def notePitch( self, step ):
+ # TODO
+ return
+
+ def noteDuration( self, step ):
+ # TODO
+ return
+
+ def noteVolume( self, step ):
+ # TODO
+ return
+
+ #=======================================================
# Track Functions
def toggleTrack( self, trackN, exclusive ):
if exclusive:
for i in range(Config.NUMBER_OF_TRACKS):
- self.trackSelected[i] = False
+ if self.trackSelected[i]:
+ self.trackSelected[i] = False
+ self.trackInterface.trackToggled( i )
self.trackSelected[trackN] = True
- self.trackInterface.trackToggled() # invalidate whole page
+ self.trackInterface.trackToggled( trackN )
self.setContextState( CONTEXT.TRACK, True )
self.setContext( CONTEXT.TRACK )
else:
@@ -577,27 +734,60 @@ class MainWindow( gtk.EventBox ):
return
self.setContextState( CONTEXT.TRACK, False )
+ def setTrack( self, trackN, state ):
+ if self.trackSelected[trackN] != state:
+ self.trackSelected[trackN] = state
+ self.trackInterface.trackToggled( trackN )
+
+ def clearTracks( self ):
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackSelected[i]:
+ self.trackSelected[i]= False
+ self.trackInterface.trackToggled( i )
+
+ self.setContextState( CONTEXT.TRACK, False )
+
def getTrackSelected( self, trackN ):
return self.trackSelected[trackN]
- #=======================================================
- # NoteDB notifications
+ def trackGenerate( self ):
+ self.generateMode = "track"
+ self.generationParametersWindow.show_all()
- def notifyPageAdd( self, id, at ):
- self.displayPage( id )
+ def trackProperties( self, trackIds = -1 ):
+ # TODO
+ return
- def notifyPageDelete( self, which, safe ):
- if self.displayedPage in which:
- self.displayPage( safe )
+ def trackDelete( self, pageIds = -1, trackIds = -1 ):
- def notifyPageDuplicate( self, new, at ):
- self.displayPage( new[self.displayedPage] )
+ if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds()
+ if trackIds == -1: trackIds = [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackSelected[i] ]
- def notifyPageMove( self, which, low, high ):
- return
+ self.noteDB.deleteNotesByTrack( pageIds, trackIds )
+
+ def trackDuplicate( self, pageIds = -1, trackIds = -1 ):
+
+ if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds()
+ if trackIds == -1: trackIds = [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackSelected[i] ]
+
+ if len(trackIds):
+ if self.GUI["2noteDuplicateButton"].get_active():
+ self.GUI["2noteDuplicateButton"].set_active( False )
+ self.noteDB.tracksToClipboard( pageIds, trackIds )
+ self.trackInterface.setInterfaceMode("paste_tracks")
+ return True
+ return False
+
+ def trackDuplicateWidget( self, widget ):
+ if widget.get_active():
+ if self.trackDuplicate(): # duplicate succeeded
+ return
+ # cancel duplicate
+ self.trackInterface.setInterfaceMode("tool")
+ widget.set_active(False)
#-----------------------------------
- # tune functions
+ # tune/page functions
#-----------------------------------
def scrollTune( self, scroll ):
@@ -614,25 +804,63 @@ class MainWindow( gtk.EventBox ):
self.trackInterface.displayPage( pageId, nextId )
- def addPage( self, after = -1, beats = False ):
+ def predrawPage( self, pageId ):
+ if self.playbackTimeout: return # we're playing, predrawing is already handled
+ if self.trackInterface.setPredrawPage( pageId ): # page needs to be drawn
+ if not self.predrawTimeout:
+ self.predrawTimeout = gobject.timeout_add( 50, self.onPredrawTimeout )
- if after == -1: after = self.tuneInterface.getLastSelected()
- if not beats: beats = self.noteDB.getPage( self.displayedPage ).beats
+ def pageGenerate( self ):
+ self.generateMode = "page"
+ self.generationParametersWindow.show_all()
- self.noteDB.addPage( beats, after )
+ def pageProperties( self, pageIds = -1 ):
- def duplicatePages( self, after = -1, pageIds = False ):
+ if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds()
+
+ # TODO show properties or something
+
+ def pageDelete( self, pageIds = -1 ):
+
+ if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds()
+
+ self.noteDB.deletePages( pageIds )
+
+ def pageDuplicate( 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 ):
+ def pageAdd( 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 pageBeats( self, pageIds = -1 ):
if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds()
- self.noteDB.deletePages( pageIds )
+ # TODO change the beats
+
+ #=======================================================
+ # 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
#-----------------------------------
# load and save functions
diff --git a/Edit/NoteInterface.py b/Edit/NoteInterface.py
index 9dc9e43..25a160d 100644
--- a/Edit/NoteInterface.py
+++ b/Edit/NoteInterface.py
@@ -40,7 +40,11 @@ class NoteInterface:
self.updateParameter( None, None )
def destroy( self ):
- self.owner.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page, True )
+ if self.selected:
+ print "destroy", self.note.id
+ self.owner.deselectNotes( { self.note.track: [self] } )
+ else: # if we were deselected above the rect has already been invalidated
+ 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
@@ -70,10 +74,14 @@ class NoteInterface:
return self.note.cs.pitch
def updateTransform( self ):
- if self.note.page == self.owner.curPage and not self.firstTransform:
- oldX = self.imgX
- oldY = self.imgY
- oldEndX = self.imgX + self.imgWidth
+ if self.note.page in self.owner.getActivePages():
+ if not self.firstTransform:
+ oldX = self.imgX
+ oldY = self.imgY
+ oldEndX = self.imgX + self.imgWidth
+ dirty = True
+ else:
+ dirty = False
if self.note.cs.onset != self.oldOnset:
self.x = self.owner.ticksToPixels( self.noteDB.getPage( self.note.page).beats, self.note.cs.onset )
@@ -89,15 +97,16 @@ class NoteInterface:
self.imgY = self.y - Config.NOTE_IMAGE_PADDING
self.oldPitch = self.note.cs.pitch
- if self.firstTransform:
- 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.owner.invalidate_rect( x, y, endx-x, endy-y, self.note.page, True )
+ if dirty:
+ if self.firstTransform:
+ 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.owner.invalidate_rect( x, y, endx-x, endy-y, self.note.page, True )
def updateDragLimits( self, dragLimits, leftBound, rightBound, widthBound, maxRightBound ):
left = leftBound - self.note.cs.onset
@@ -187,24 +196,23 @@ class NoteInterface:
return True
- def noteDrag( self, emitter, do, dp, dd ):
- self.potentialDeselect = False
-
+ def noteDragOnset( self, do, stream ):
+ self. potentialDeselect = False
if do != self.lastDragO:
self.lastDragO = do
- 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
+ stream += [ self.note.id, self.baseOnset + do ]
+ def noteDragPitch( self, dp, stream ):
+ self.potentialDeselect = False
if dp != self.lastDragP:
self.lastDragP = dp
- newPitch = self.basePitch + dp
- self.noteDB.updateNote( self.note.page, self.note.track, self.note.id, PARAMETER.PITCH, newPitch )
- self.updateSampleNote( newPitch )
+ stream += [ self.note.id, self.basePitch + dp ]
+ def noteDragDuration( self, dd, stream ):
+ self.potentialDeselect = False
if dd != self.lastDragD:
self.lastDragD = dd
- 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
+ stream += [ self.note.id, self.baseDuration + dd ]
def doneNoteDrag( self, emitter ):
self.baseOnset = self.note.cs.onset
@@ -217,6 +225,47 @@ class NoteInterface:
self.clearSampleNote()
+ def noteDecOnset( self, step, leftBound, stream ):
+ if self.selected:
+ if leftBound < self.note.cs.onset:
+ onset = max( self.note.cs.onset+step, leftBound )
+ stream += [ self.note.id, onset ]
+ return onset + self.note.cs.duration
+ return self.end
+
+ def noteIncOnset( self, step, rightBound, stream ):
+ if self.selected:
+ if rightBound > self.end:
+ onset = min( self.end+step, rightBound ) - self.note.cs.duration
+ stream += [ self.note.id, onset ]
+ return onset
+ return self.note.cs.onset
+
+ def noteDecPitch( self, step, stream ):
+ if self.note.cs.pitch > Config.MINIMUM_PITCH:
+ stream += [ self.note.id, max( self.note.cs.pitch+step, Config.MINIMUM_PITCH ) ]
+
+ def noteIncPitch( self, step, stream ):
+ if self.note.cs.pitch < Config.MAXIMUM_PITCH:
+ stream += [ self.note.id, min( self.note.cs.pitch+step, Config.MAXIMUM_PITCH ) ]
+
+ def noteDecDuration( self, step, stream ):
+ if self.note.cs.duration > Config.MINIMUM_NOTE_DURATION:
+ stream += [ self.note.id, max( self.note.cs.duration+step, Config.MINIMUM_NOTE_DURATION ) ]
+
+ def noteIncDuration( self, step, rightBound, stream ):
+ if self.selected:
+ if self.end < rightBound:
+ stream += [ self.note.id, min( self.end+step, rightBound ) - self.note.cs.onset ]
+
+ def noteDecVolume( self, step, stream ):
+ if self.note.cs.amplitude > 0:
+ stream += [ self.note.id, max( self.note.cs.amplitude+step, 0 ) ]
+
+ def noteIncVolume( self, step, stream ):
+ if self.note.cs.amplitude < 1:
+ stream += [ self.note.id, min( self.note.cs.amplitude+step, 1 ) ]
+
def handleMarqueeSelect( self, emitter, start, stop ):
intersectionY = [ max(start[1],self.y), min(stop[1],self.y+self.height) ]
if intersectionY[0] > intersectionY[1]:
diff --git a/Edit/TrackInterface.py b/Edit/TrackInterface.py
index a3b3986..a5b7593 100644
--- a/Edit/TrackInterface.py
+++ b/Edit/TrackInterface.py
@@ -3,12 +3,15 @@ pygtk.require( '2.0' )
import gtk
from math import floor
+import time
import Config
from Edit.NoteInterface import NoteInterface
from Edit.HitInterface import HitInterface
from Edit.MainWindow import CONTEXT
+from Util.NoteDB import PARAMETER
+
from Util.Profiler import TP
class SELECTNOTES:
@@ -22,7 +25,8 @@ class SELECTNOTES:
class INTERFACEMODE:
DEFAULT = 0
DRAW = 1
- PASTE = 2
+ PASTE_NOTES = 2
+ PASTE_TRACKS = 3
class TrackInterfaceParasite:
def __init__( self, noteDB, owner, note ):
@@ -66,15 +70,20 @@ class TrackInterface( gtk.EventBox ):
self.marqueeLoc = False # current drag location of the marquee
self.marqueeRect = [[0,0],[0,0]]
+ self.pasteTick = -1
+ self.pasteTrack = -1
+ self.pasteRect = False
+
self.playheadX = Config.TRACK_SPACING_DIV2
self.cursor = { \
"default": None, \
"drag-onset": gtk.gdk.Cursor(gtk.gdk.SB_RIGHT_ARROW), \
- "drag-pitch": gtk.gdk.Cursor(gtk.gdk.SB_V_DOUBLE_ARROW), \
- "drag-duration": gtk.gdk.Cursor(gtk.gdk.SB_H_DOUBLE_ARROW), \
- "drag-playhead": gtk.gdk.Cursor(gtk.gdk.LEFT_SIDE), \
+ "drag-pitch": gtk.gdk.Cursor(gtk.gdk.BOTTOM_SIDE), \
+ "drag-duration": gtk.gdk.Cursor(gtk.gdk.RIGHT_SIDE), \
+ "drag-playhead": gtk.gdk.Cursor(gtk.gdk.SB_H_DOUBLE_ARROW), \
"pencil": gtk.gdk.Cursor(gtk.gdk.PENCIL), \
+ "paste": gtk.gdk.Cursor(gtk.gdk.CENTER_PTR), \
"error": None }
self.add_events(gtk.gdk.POINTER_MOTION_MASK|gtk.gdk.POINTER_MOTION_HINT_MASK)
@@ -164,6 +173,16 @@ class TrackInterface( gtk.EventBox ):
self.curScreen = 0
self.preScreen = 1
+ #-- private --------------------------------------------
+
+ def _updateClipboardArea( self ):
+ self.clipboardArea = self.owner.getClipboardArea( self.curPage )
+ self.clipboardTrackTop = 0
+ for t in range(self.drumIndex):
+ if self.clipboardArea["tracks"][t]: break
+ self.clipboardTrackTop += 1
+ self.clipboardDrumTrack = self.clipboardArea["tracks"][self.drumIndex]
+
#=======================================================
# Module Interface
@@ -173,18 +192,32 @@ class TrackInterface( gtk.EventBox ):
else:
return ( self.image["note"], self.image["noteSelected"], self.drawingArea.get_colormap(), self.trackColors[track] )
- def predrawPage( self, page ):
+ def getActivePages( self ):
+ return self.screenBufPage
+
+ def setPredrawPage( 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 )
+ return True
+ return False
+
+ def predrawPage( self, timeout ):
+ return self.draw( self.preScreen, False, timeout )
def displayPage( self, page, predraw = -1 ):
- if page == self.curPage: return
+ if page == 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 )
+ return
if self.curPage >= 0 and self.curPage != page: clearNotes = True
else: clearNotes = False
+ oldPage = self.curPage
self.curPage = page
self.curBeats = self.noteDB.getPage(page).beats
@@ -202,23 +235,43 @@ class TrackInterface( gtk.EventBox ):
self.screenBufPage[self.preScreen] = predraw
self.screenBufBeats[self.preScreen] = self.noteDB.getPage(predraw).beats
self.invalidate_rect( 0, 0, self.width, self.height, predraw )
+ elif self.screenBufPage[self.preScreen] == -1: # make sure predraw is assigned to a valid page at least
+ self.screenBufPage[self.preScreen] = self.screenBufPage[self.curScreen]
if clearNotes: # clear the notes now that we've sorted out the screen buffers
- self.clearSelectedNotes()
+ self.clearSelectedNotes( oldPage )
+
+ if self.curAction == "paste":
+ self._updateClipboardArea()
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, self.screenBufBeats[self.curScreen] ) + Config.TRACK_SPACING_DIV2
+ self.playheadX = self.ticksToPixels( self.curBeats, ticks ) + Config.TRACK_SPACING_DIV2
self.invalidate_rect( self.playheadX-Config.PLAYHEAD_SIZE/2, 0, Config.PLAYHEAD_SIZE, self.height, self.curPage, False )
def setInterfaceMode( self, mode ):
- if mode == "Draw":
+ self.doneCurrentAction()
+
+ if mode == "tool":
+ mode = self.owner.getTool()
+
+ if mode == "draw":
self.interfaceMode = INTERFACEMODE.DRAW
- elif mode == "Paste":
- self.interfaceMode = INTERFACEMODE.PASTE
+ elif mode == "paste_notes":
+ self.interfaceMode = INTERFACEMODE.PASTE_NOTES
+ self.setCurrentAction("paste", self)
+ elif mode == "paste_tracks":
+ self.interfaceMode = INTERFACEMODE.PASTE_TRACKS
+ self.setCurrentAction("paste", self )
else:
self.interfaceMode = INTERFACEMODE.DEFAULT
+ def getSelectedNotes( self ):
+ ids = []
+ for t in range(Config.NUMBER_OF_TRACKS):
+ ids.append( [ n.note.id for n in self.selectedNotes[t] ] )
+ return ids
+
#=======================================================
# Event Callbacks
@@ -226,7 +279,7 @@ class TrackInterface( gtk.EventBox ):
self.alloc = allocation
width = allocation.width
height = allocation.height
-
+
self.drawingArea.set_size_request( width, height )
if self.window != None:
@@ -242,6 +295,13 @@ class TrackInterface( gtk.EventBox ):
self.clickLoc = [ int(event.x), int(event.y) ]
+ if self.curAction == "paste":
+ self.doPaste()
+ self.setCurrentAction("block-track-select")
+ TP.ProfileEnd( "TI::handleButtonPress" )
+ return
+
+
# 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 )
@@ -289,8 +349,9 @@ class TrackInterface( gtk.EventBox ):
if self.trackLimits[i][0] > event.y: break
if self.trackLimits[i][1] < event.y: continue
if event.button == 1:
- if self.buttonPressCount == 1: self.owner.toggleTrack( i, False )
- else: self.owner.toggleTrack( i, True )
+ if self.buttonPressCount == 1: self.owner.toggleTrack( i, False )
+ elif self.buttonPressCount == 2: self.owner.toggleTrack( i, True )
+ else: self.owner.clearTracks()
break
TP.ProfileEnd( "TI::handleButtonRelease" )
@@ -317,7 +378,6 @@ class TrackInterface( gtk.EventBox ):
def handleMotion( self, widget, event ):
TP.ProfileBegin( "TI::handleMotion::Common" )
-
if event.is_hint:
x, y, state = self.window.get_pointer()
event.x = float(x)
@@ -326,7 +386,17 @@ class TrackInterface( gtk.EventBox ):
TP.ProfileEnd( "TI::handleMotion::Common" )
- if event.state & gtk.gdk.BUTTON1_MASK:
+ if self.curAction == "paste":
+ TP.ProfileBegin( "TI::handleMotion::Paste" )
+ top = Config.NUMBER_OF_TRACKS
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackLimits[i][0] > event.y: break
+ if self.trackLimits[i][1] < event.y: continue
+ top = i
+ break
+ self.updatePaste( self.pixelsToTicks( self.curBeats, event.x ), top )
+ TP.ProfileEnd( "TI::handleMotion::Paste" )
+ elif 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
@@ -361,9 +431,9 @@ class TrackInterface( gtk.EventBox ):
#=======================================================
# Actions
- def setCurrentAction( self, action, obj ):
+ def setCurrentAction( self, action, obj = None ):
if self.curAction:
- print "BackgroundView - Action already in progress!"
+ self.doneCurrentAction()
self.curAction = action
self.curActionObject = obj
@@ -372,19 +442,25 @@ class TrackInterface( gtk.EventBox ):
elif action == "note-drag-duration": self.updateDragLimits()
elif action == "note-drag-pitch": self.updateDragLimits()
elif action == "note-drag-pitch-drum": self.updateDragLimits()
+ elif action == "paste":
+ self._updateClipboardArea()
+ self.setCursor("paste")
def doneCurrentAction( self ):
- if self.curAction == "note-drag-onset": self.doneNoteDrag()
- elif self.curAction == "note-drag-duration": self.doneNoteDrag()
- elif self.curAction == "note-drag-pitch": self.doneNoteDrag()
- elif self.curAction == "note-drag-pitch-drum": self.doneNoteDrag()
-
+ if not self.curAction: return
+ action = self.curAction
self.curAction = False
- self.curActionObject = False
+
+ if action == "note-drag-onset": self.doneNoteDrag()
+ elif action == "note-drag-duration": self.doneNoteDrag()
+ elif action == "note-drag-pitch": self.doneNoteDrag()
+ elif action == "note-drag-pitch-drum": self.doneNoteDrag()
+ elif action == "paste":
+ self.owner.cleanupClipboard()
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 )
+ if trackN == -1: self.invalidate_rect( 0, 0, self.width, self.height )
+ else: self.invalidate_rect( 0, self.trackLimits[trackN][0], self.width, self.trackLimits[trackN][1]-self.trackLimits[trackN][0] )
def selectionChanged( self ):
if self.curAction == "note-drag-onset": self.updateDragLimits()
@@ -398,14 +474,15 @@ class TrackInterface( gtk.EventBox ):
return
self.owner.setContextState( CONTEXT.NOTE, False )
- def applyNoteSelection( self, mode, trackN, which ):
+ def applyNoteSelection( self, mode, trackN, which, page = -1 ):
+ if page == -1: page = self.curPage
if mode == SELECTNOTES.ALL:
- track = self.noteDB.getNotesByTrack( self.curPage, trackN, self )
+ track = self.noteDB.getNotesByTrack( page, 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:
- track = self.noteDB.getNotesByTrack( self.curPage, trackN, self )
+ track = self.noteDB.getNotesByTrack( page, trackN, self )
map( lambda note:note.setSelected( False ), track )
self.selectedNotes[trackN] = []
elif mode == SELECTNOTES.ADD:
@@ -425,7 +502,7 @@ class TrackInterface( gtk.EventBox ):
note.setSelected( True )
self.selectedNotes[trackN].append( note )
elif mode == SELECTNOTES.EXCLUSIVE:
- notes = self.noteDB.getNotesByTrack( self.curPage, trackN, self )
+ notes = self.noteDB.getNotesByTrack( page, trackN, self )
for n in range(len(notes)):
if notes[n] in which:
if notes[n].setSelected( True ):
@@ -434,51 +511,51 @@ class TrackInterface( gtk.EventBox ):
if notes[n].setSelected( False ):
self.selectedNotes[trackN].remove( notes[n] )
- def selectNotesByBar( self, trackN, start, stop ):
+ def selectNotesByBar( self, trackN, start, stop, page = -1 ):
for i in range(Config.NUMBER_OF_TRACKS):
if i == trackN:
notes = []
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 )
+ if not Config.ModKeys.ctrlDown: self.applyNoteSelection( SELECTNOTES.EXCLUSIVE, trackN, notes, page )
+ else: self.applyNoteSelection( SELECTNOTES.ADD, trackN, notes, page )
else:
- if not Config.ModKeys.ctrlDown: self.applyNoteSelection( SELECTNOTES.NONE, i, [] )
+ if not Config.ModKeys.ctrlDown: self.applyNoteSelection( SELECTNOTES.NONE, i, [], page )
self.selectionChanged()
- def selectNotesByTrack( self, trackN ):
+ def selectNotesByTrack( self, trackN, page = -1 ):
if Config.ModKeys.ctrlDown:
- self.applyNoteSelection( SELECTNOTES.ALL, trackN, [] )
+ self.applyNoteSelection( SELECTNOTES.ALL, trackN, [], page )
else:
for i in range(Config.NUMBER_OF_TRACKS):
- if i == trackN: self.applyNoteSelection( SELECTNOTES.ALL, trackN, [] )
- else: self.applyNoteSelection( SELECTNOTES.NONE, i, [] )
+ if i == trackN: self.applyNoteSelection( SELECTNOTES.ALL, trackN, [], page )
+ else: self.applyNoteSelection( SELECTNOTES.NONE, i, [], page )
self.selectionChanged()
- def selectNotes( self, noteDic ):
- if Config.ModKeys.ctrlDown:
+ def selectNotes( self, noteDic, ignoreCtrl = False, page = -1 ):
+ if Config.ModKeys.ctrlDown and not ignoreCtrl:
for i in noteDic:
- self.applyNoteSelection( SELECTNOTES.FLIP, i, noteDic[i] )
+ self.applyNoteSelection( SELECTNOTES.FLIP, i, noteDic[i], page )
else:
for i in range(Config.NUMBER_OF_TRACKS):
- if i in noteDic: self.applyNoteSelection( SELECTNOTES.EXCLUSIVE, i, noteDic[i] )
- else: self.applyNoteSelection( SELECTNOTES.NONE, i, [] )
+ if i in noteDic: self.applyNoteSelection( SELECTNOTES.EXCLUSIVE, i, noteDic[i], page )
+ else: self.applyNoteSelection( SELECTNOTES.NONE, i, [], page )
self.selectionChanged()
- def deselectNotes( self, noteDic ):
+ def deselectNotes( self, noteDic, page = -1 ):
for i in noteDic:
- self.applyNoteSelection( SELECTNOTES.REMOVE, i, noteDic[i] )
+ self.applyNoteSelection( SELECTNOTES.REMOVE, i, noteDic[i], page )
self.selectionChanged()
- def clearSelectedNotes( self ):
+ def clearSelectedNotes( self, page = -1 ):
for i in range(Config.NUMBER_OF_TRACKS):
- self.applyNoteSelection( SELECTNOTES.NONE, i, [] )
+ self.applyNoteSelection( SELECTNOTES.NONE, i, [], page )
self.selectionChanged()
def updateDragLimits( self ):
self.dragLimits = [ [-9999,9999], [-9999,9999], [-9999,9999] ] # initialize to big numbers!
- maxRightBound = self.curBeats * Config.TICKS_PER_BEAT
+ maxRightBound = self.noteDB.getPage(self.curPage).ticks
for i in range(Config.NUMBER_OF_TRACKS):
if not len(self.selectedNotes[i]): continue # no selected notes here
@@ -510,39 +587,133 @@ class TrackInterface( gtk.EventBox ):
def noteDragOnset( self, event ):
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
+ stream = []
for i in range(Config.NUMBER_OF_TRACKS):
+ tstream = []
for note in self.selectedNotes[i]:
- note.noteDrag(self, do, dp, dd)
+ note.noteDragOnset( do, tstream )
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.ONSET, len(tstream)//2 ] + tstream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
def noteDragDuration( self, event ):
- do = 0
- dp = 0
dd = self.pixelsToTicks( self.curBeats, event.x - self.clickLoc[0] )
dd = min( self.dragLimits[2][1], max( self.dragLimits[2][0], dd ) )
+ stream = []
for i in range(Config.NUMBER_OF_TRACKS):
+ tstream = []
for note in self.selectedNotes[i]:
- note.noteDrag(self, do, dp, dd)
+ note.noteDragDuration( dd, tstream )
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.DURATION, len(tstream)//2 ] + tstream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
def noteDragPitch( self, event, drum = False ):
- do = 0
if not drum: dp = self.pixelsToPitch( event.y - self.clickLoc[1] )
else: dp = self.pixelsToPitchDrum( event.y - self.clickLoc[1] )
dp = min( self.dragLimits[1][1], max( self.dragLimits[1][0], dp ) )
- dd = 0
+ stream = []
for i in range(Config.NUMBER_OF_TRACKS):
+ tstream = []
for note in self.selectedNotes[i]:
- note.noteDrag(self, do, dp, dd)
+ note.noteDragPitch( dp, tstream )
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.PITCH, len(tstream)//2 ] + tstream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
def doneNoteDrag( self ):
for i in range(Config.NUMBER_OF_TRACKS):
for note in self.selectedNotes[i]:
note.doneNoteDrag( self )
+ def noteStepOnset( self, step ):
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ tstream = []
+ track = self.noteDB.getNotesByTrack( self.curPage, i, self )
+ if step < 0: # moving to the left, iterate forwards
+ leftBound = 0
+ for n in range(len(track)):
+ leftBound = track[n].noteDecOnset( step, leftBound, tstream )
+ else: # moving to the right, iterate backwards
+ rightBound = self.noteDB.getPage(self.curPage).ticks
+ for n in range(len(track)-1, -1, -1 ):
+ rightBound = track[n].noteIncOnset( step, rightBound, tstream )
+
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.ONSET, len(tstream)//2 ] + tstream
+
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteStepPitch( self, step ):
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ tstream = []
+ if step < 0:
+ for n in self.selectedNotes[i]:
+ n.noteDecPitch( step, tstream )
+ else:
+ for n in self.selectedNotes[i]:
+ n.noteIncPitch( step, tstream )
+
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.PITCH, len(tstream)//2 ] + tstream
+
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteStepDuration( self, step ):
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ tstream = []
+ if step < 0:
+ for n in self.selectedNotes[i]:
+ n.noteDecDuration( step, tstream )
+ else:
+ track = self.noteDB.getNotesByTrack( self.curPage, i, self )
+ for j in range(len(track)-1):
+ track[j].noteIncDuration( step, track[j+1].getStartTick(), tstream )
+ track[len(track)-1].noteIncDuration( step, self.noteDB.getPage(self.curPage).ticks, tstream )
+
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.DURATION, len(tstream)//2 ] + tstream
+
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteStepVolume( self, step ):
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ tstream = []
+ if step < 0:
+ for n in self.selectedNotes[i]:
+ n.noteDecVolume( step, tstream )
+ else:
+ for n in self.selectedNotes[i]:
+ n.noteIncVolume( step, tstream )
+
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.AMPLITUDE, len(tstream)//2 ] + tstream
+
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+
def updateMarquee( self, event ):
if self.marqueeLoc:
oldX = self.marqueeRect[0][0]
@@ -616,6 +787,79 @@ class TrackInterface( gtk.EventBox ):
print "set playhead to %d ticks" % (ticks)
self.doneCurrentAction()
+ def updatePaste( self, tick, track ):
+ if self.interfaceMode == INTERFACEMODE.PASTE_TRACKS: tick = 0
+ if self.pasteTick == tick and self.pasteTrack == track: return
+ if self.noteDB.getPage(self.curPage).ticks < tick < 0 \
+ or track > self.drumIndex \
+ or ( track == self.drumIndex and not self.clipboardDrumTrack ):
+ if self.pasteRect:
+ self.invalidate_rect( self.pasteRect[0][0], self.pasteRect[0][1], self.pasteRect[1][0], self.pasteRect[1][1], self.curPage, False )
+ self.pasteTick = self.pasteTrack = -1
+ self.pasteRect = False
+ return
+ if self.pasteRect:
+ self.invalidate_rect( self.pasteRect[0][0], self.pasteRect[0][1], self.pasteRect[1][0], self.pasteRect[1][1], self.curPage, False )
+ if self.clipboardDrumTrack:
+ bottom = self.drumIndex
+ else:
+ bottom = self.drumIndex - 1
+ for t in range(self.drumIndex-1,self.clipboardTrackTop-1,-1):
+ if self.clipboardArea["tracks"][t]: break
+ bottom -= 1
+ end = -tick + min( self.noteDB.getPage(self.curPage).ticks, tick + self.clipboardArea["limit"][1]-self.clipboardArea["limit"][0] )
+ self.pasteTick = tick
+ self.pasteTrack = track
+ self.pasteRect = [ [ self.ticksToPixels( self.curBeats, tick ), \
+ self.trackLimits[track][0] ], \
+ [ self.ticksToPixels( self.curBeats, end), \
+ self.trackLimits[bottom][1] ] ]
+ self.invalidate_rect( self.pasteRect[0][0], self.pasteRect[0][1], self.pasteRect[1][0], self.pasteRect[1][1], self.curPage, False )
+
+ def doPaste( self ):
+ if self.pasteTrack == -1:
+ self.doneCurrentAction()
+ return
+
+ trackMap = {}
+ for t in range(self.pasteTrack,self.drumIndex):
+ ind = t+self.clipboardTrackTop-self.pasteTrack
+ if ind >= self.drumIndex: break
+ if not self.clipboardArea["tracks"][ind]:
+ continue
+ trackMap[t] = ind
+ if self.clipboardDrumTrack:
+ trackMap[self.drumIndex] = self.drumIndex
+ new = self.owner.pasteClipboard( self.pasteTick - self.clipboardArea["limit"][0], trackMap )
+ if self.interfaceMode == INTERFACEMODE.PASTE_NOTES and self.curPage in new:
+ noteDic = {}
+ for t in range(Config.NUMBER_OF_TRACKS):
+ if len(new[self.curPage][t]):
+ noteDic[t] = [ self.noteDB.getNote( self.curPage, t, n, self ) for n in new[self.curPage][t] ]
+ self.selectNotes(noteDic)
+ elif self.interfaceMode == INTERFACEMODE.PASTE_TRACKS:
+ for t in range(self.drumIndex):
+ ind = t + self.clipboardTrackTop - self.pasteTrack
+ if ind >= self.drumIndex or ind < 0: self.owner.setTrack( t, False )
+ else: self.owner.setTrack( t, self.clipboardArea["tracks"][ind] )
+ self.owner.setTrack( self.drumIndex, self.clipboardDrumTrack )
+
+ self.doneCurrentAction()
+
+ def donePaste( self ):
+ if self.pasteRect:
+ self.invalidate_rect( self.pasteRect[0][0], self.pasteRect[0][1], self.pasteRect[1][0], self.pasteRect[1][1], self.curPage, False )
+ self.pasteTick = self.pasteTrack = -1
+ self.pasteRect = False
+ self.setInterfaceMode("tool")
+ # make a fake event for updateTooltip
+ event = gtk.gdk.Event(gtk.gdk.MOTION_NOTIFY)
+ x, y, state = self.window.get_pointer()
+ event.x = float(x)
+ event.y = float(y)
+ event.state = state
+ self.updateTooltip( event )
+
def updateTooltip( self, event ):
# check clicked the playhead
@@ -654,8 +898,10 @@ class TrackInterface( gtk.EventBox ):
#=======================================================
# Drawing
- def predraw( self, buf, noescape = True ):
- TP.ProfileBegin( "TrackInterface::predraw" )
+ def draw( self, buf, noescape = True, timeout = 0 ):
+ if not self.screenBufDirty[buf]: return True
+
+ TP.ProfileBegin( "TrackInterface::draw" )
startX = self.screenBufDirtyRect[buf].x
startY = self.screenBufDirtyRect[buf].y
@@ -675,7 +921,7 @@ class TrackInterface( gtk.EventBox ):
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 ):
- if resume[0] == 0:
+ if resume[1] == 0:
if startY > self.trackLimits[i][1]: continue
if stopY < self.trackLimits[i][0]: break
@@ -691,28 +937,30 @@ class TrackInterface( gtk.EventBox ):
x = beatStart + j*beatSpacing
pixmap.draw_line( self.gc, x, self.trackRect[i].y, x, self.trackRect[i].y+self.trackRect[i].height )
+ resume[1] = 1 # background drawn
+
# draw notes
TP.ProfileBegin("TI::draw notes")
- notes = self.noteDB.getNotesByTrack( self.curPage, i, self )
- for n in range( resume[1], len(notes) ):
+ notes = self.noteDB.getNotesByTrack( self.screenBufPage[buf], i, self )
+ for n in range( resume[2], len(notes) ):
# check escape
- if 0:
+ if not noescape and time.time() > timeout:
resume[0] = i
- resume[1] = n
- TP.ProfilePause( "TrackInterface::predraw" )
+ resume[2] = n
+ TP.ProfilePause( "TrackInterface::draw" )
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[2] = 0
# drum track
if stopY > self.trackLimits[self.drumIndex][0]:
- if resume[0] == 0:
+ if resume[1] == 0:
# draw background
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 )
@@ -725,27 +973,29 @@ class TrackInterface( gtk.EventBox ):
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 )
+ resume[1] = 1 # background drawn
+
# draw notes
- notes = self.noteDB.getNotesByTrack( self.curPage, self.drumIndex, self )
- for n in range( resume[1], len(notes) ):
+ notes = self.noteDB.getNotesByTrack( self.screenBufPage[buf], self.drumIndex, self )
+ for n in range( resume[2], len(notes) ):
# check escape
- if 0:
+ if not noescape and time.time() > timeout:
resume[0] = i
- resume[1] = n
- TP.ProfilePause( "TrackInterface::predraw" )
+ resume[2] = n
+ TP.ProfilePause( "TrackInterface::draw" )
return False
if not notes[n].draw( pixmap, self.gc, startX, stopX ): break
self.screenBufDirty[buf] = False
- TP.ProfileEnd( "TrackInterface::predraw" )
+ TP.ProfileEnd( "TrackInterface::draw" )
return True
def expose( self, DA, event ):
if self.screenBufDirty[self.curScreen]:
- self.predraw( self.curScreen )
+ self.draw( self.curScreen )
TP.ProfileBegin( "TrackInterface::expose" )
@@ -771,11 +1021,23 @@ class TrackInterface( gtk.EventBox ):
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] )
+ if self.pasteRect: # draw the paste highlight
+ self.gc.set_function( gtk.gdk.INVERT )
+ for t in range(self.pasteTrack,self.drumIndex):
+ ind = t+self.clipboardTrackTop-self.pasteTrack
+ if ind >= self.drumIndex: break
+ if not self.clipboardArea["tracks"][ind]:
+ continue
+ DA.window.draw_rectangle( self.gc, True, self.pasteRect[0][0], self.trackLimits[t][0] + Config.TRACK_SPACING_DIV2, self.pasteRect[1][0], self.trackHeight )
+ if self.clipboardDrumTrack:
+ DA.window.draw_rectangle( self.gc, True, self.pasteRect[0][0], self.trackLimits[self.drumIndex][0] + Config.TRACK_SPACING_DIV2, self.pasteRect[1][0], self.trackHeightDrum )
+ self.gc.set_function( gtk.gdk.COPY )
+
self.drawingAreaDirty = False
TP.ProfileEnd( "TrackInterface::expose" )
- def invalidate_rect( self, x, y, width, height, page, base = True ):
+ def invalidate_rect( self, x, y, width, height, page = -1, 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
@@ -783,7 +1045,7 @@ class TrackInterface( gtk.EventBox ):
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 page == self.curPage or page == -1:
if base: # the base image has been dirtied
if not self.screenBufDirty[self.curScreen]:
self.screenBufDirtyRect[self.curScreen].x = x
@@ -792,13 +1054,13 @@ class TrackInterface( gtk.EventBox ):
self.screenBufDirtyRect[self.curScreen].height = height
else:
self.screenBufDirtyRect[self.curScreen] = self.screenBufDirtyRect[self.curScreen].union( self.dirtyRectToAdd )
- self.screenBufResume[self.curScreen] = [0,0]
+ self.screenBufResume[self.curScreen] = [0,0,0]
self.screenBufDirty[self.curScreen] = True
if self.drawingArea.window != None:
self.drawingArea.window.invalidate_rect( self.dirtyRectToAdd, True )
self.drawingAreaDirty = True
- elif page == self.screenBufPage[self.preScreen]:
+ if page == self.screenBufPage[self.preScreen] or page == -1:
if not self.screenBufDirty[self.preScreen]:
self.screenBufDirtyRect[self.preScreen].x = x
self.screenBufDirtyRect[self.preScreen].y = y
@@ -806,7 +1068,7 @@ class TrackInterface( gtk.EventBox ):
self.screenBufDirtyRect[self.preScreen].height = height
else:
self.screenBufDirtyRect[self.preScreen] = self.screenBufDirtyRect[self.preScreen].union( self.dirtyRectToAdd )
- self.screenBufResume[self.preScreen] = [0,0]
+ self.screenBufResume[self.preScreen] = [0,0,0]
self.screenBufDirty[self.preScreen] = True
#self.queue_draw()
diff --git a/Edit/TuneInterface.py b/Edit/TuneInterface.py
index e6790b9..d1d5481 100644
--- a/Edit/TuneInterface.py
+++ b/Edit/TuneInterface.py
@@ -58,6 +58,8 @@ class TuneInterface( gtk.EventBox ):
self.dragMode = None
self.dropAt = -1
+ self.add_events(gtk.gdk.POINTER_MOTION_MASK|gtk.gdk.POINTER_MOTION_HINT_MASK)
+
self.connect( "size-allocate", self.size_allocated )
self.drawingArea.connect( "expose-event", self.draw )
self.connect( "button-press-event", self.handleButtonPress )
@@ -90,15 +92,23 @@ class TuneInterface( gtk.EventBox ):
def handleButtonPress( self, widget, event ):
ind = int(event.x-self.pageOffset)//self.pageSpacing
if ind >= self.noteDB.getPageCount():
- self.dragMode = self.DRAG_BLOCK
+ if self.dragMode != self.DRAG_MOVE:
+ self.dragMode = self.DRAG_BLOCK
return
if ind < 0: ind = 0
-
+
self.clickX = event.x
id = self.noteDB.getPageByIndex( ind )
-
- if event.type == gtk.gdk._2BUTTON_PRESS: # double click -> exclusive select
+
+ if event.state & gtk.gdk.BUTTON2_MASK:
+ # bring up properties or something
+ return
+
+ if event.type == gtk.gdk._3BUTTON_PRESS: # triple click -> select all
+ self.selectAll()
+ self.owner.displayPage( id )
+ elif event.type == gtk.gdk._2BUTTON_PRESS: # double click -> exclusive select
self.selectPage( id )
self.owner.displayPage( id )
else:
@@ -120,38 +130,54 @@ class TuneInterface( gtk.EventBox ):
self.owner.setContext( CONTEXT.PAGE )
def handleButtonRelease( self, widget, event ):
-
- if self.dragMode == self.DRAG_MOVE:
+ if self.dragMode == self.DRAG_MOVE \
+ and event.button == 1:
self.invalidate_rect( 0, 0, self.width, self.height ) # drop head
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
+ self.dragMode = None
def handleMotion( self, widget, event ):
- 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 < self.noteDB.getPageCount(): self.selectPage( self.noteDB.getPageByIndex(ind), False )
- elif self.dragMode == self.DRAG_DESELECT: # deselect on drag
+ if event.is_hint:
+ x, y, state = self.window.get_pointer()
+ event.x = float(x)
+ event.y = float(y)
+ event.state = state
+
+ if event.state & gtk.gdk.BUTTON1_MASK: # clicking
+ 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 < 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 < 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
+ c = self.noteDB.getPageCount()
+ if self.dropAt > c: self.dropAt = c
+ self.invalidate_rect( 0, 0, self.width, self.height )
+
+ else: # hovering
ind = int(event.x-self.pageOffset)//self.pageSpacing
- 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 0 <= ind < self.noteDB.getPageCount():
+ id = self.noteDB.getPageByIndex(ind)
+ if id != self.displayedPage:
+ self.owner.predrawPage( id )
- if self.dragMode == self.DRAG_MOVE:
- self.dropAt = int(event.x-self.pageOffset+Config.PAGE_THUMBNAIL_WIDTH_DIV2)//self.pageSpacing
- 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
@@ -160,12 +186,12 @@ class TuneInterface( gtk.EventBox ):
if id not in self.selectedIds:
self.selectPage( id )
-
+
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
@@ -176,7 +202,7 @@ class TuneInterface( gtk.EventBox ):
return -1
else:
scroll = stopX - self.baseWidth
-
+
return scroll
def selectPage( self, id, exclusive = True ):
@@ -213,11 +239,14 @@ class TuneInterface( gtk.EventBox ):
return True # page removed from the selection
+ def selectAll( self ):
+ self.selectedIds = self.noteDB.getTune()[:]
+ self.invalidate_rect( 0, 0, self.width, self.height )
+
def clearSelection( self ):
self.selectedIds = []
-
self.invalidate_rect( 0, 0, self.width, self.height )
-
+
def getSelectedIds( self ):
return self.selectedIds
@@ -249,7 +278,7 @@ class TuneInterface( gtk.EventBox ):
# Drawing
def draw( self, drawingArea, event ):
-
+
startX = event.area.x
startY = event.area.y
stopX = event.area.x + event.area.width
diff --git a/Player/KeyboardStandAlone.py b/Player/KeyboardStandAlone.py
index 1517b5a..c3d53cf 100644
--- a/Player/KeyboardStandAlone.py
+++ b/Player/KeyboardStandAlone.py
@@ -98,7 +98,7 @@ class KeyboardStandAlone:
if KEY_MAP_PIANO.has_key(key):
if Config.INSTRUMENTS[ self.key_dict[key].instrument].csoundInstrumentId == Config.INST_TIED:
- self.key_dict[key].duration = 1
+ self.key_dict[key].duration = .5
self.key_dict[key].decay = 0.7
self.key_dict[key].amplitude = 1
self.key_dict[key].playNow(0.3)
diff --git a/Player/StandalonePlayer.py b/Player/StandalonePlayer.py
index e26c38a..9566de0 100644
--- a/Player/StandalonePlayer.py
+++ b/Player/StandalonePlayer.py
@@ -18,6 +18,7 @@ from Player.KeyboardStandAlone import KeyboardStandAlone
from Player.RythmPlayer import RythmPlayer
from Player.RythmGenerator import *
from SynthLab.SynthLabWindow import SynthLabWindow
+from Player.Trackpad import Trackpad
Tooltips = Config.Tooltips
@@ -43,6 +44,7 @@ class StandAlonePlayer( gtk.EventBox ):
self.notesList = []
time.sleep(0.001)
self.playbackTimeout = None
+ self.trackpad = Trackpad( self, self.csnd )
loopPointsTable = []
sample_names = [name for i in range( len( Config.INSTRUMENTS ) ) for name in Config.INSTRUMENTS.keys() if Config.INSTRUMENTS[ name ].instrumentId == i ]
diff --git a/Player/Trackpad.py b/Player/Trackpad.py
new file mode 100644
index 0000000..0c1d53b
--- /dev/null
+++ b/Player/Trackpad.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+import gobject
+
+import Config
+
+KEY_MAP_PIANO = Config.KEY_MAP_PIANO
+
+class Trackpad:
+ def __init__(self, win, client):
+ self.win = win
+ self.csnd = client
+ win.add_events(gtk.gdk.POINTER_MOTION_MASK)
+ win.add_events(gtk.gdk.BUTTON_PRESS_MASK)
+ win.add_events(gtk.gdk.BUTTON_RELEASE_MASK)
+ win.connect('motion-notify-event',self.handle_motion)
+ win.connect('key-press-event',self.handle_keyPress)
+ win.connect('key-release-event',self.handle_keyRelease)
+
+ self.first_x = None
+ self.current_x = None
+ self.final_x = None
+ self.first_y = None
+ self.current_y = None
+ self.final_y = None
+
+ self.buttonPressed = False
+
+ self.create_invisible_cursor()
+
+
+ def create_invisible_cursor(self):
+ pix_data = """/* XPM */
+ static char * invisible_xpm[] = {
+ "1 1 1 1",
+ " c None",
+ " "};"""
+ color = gtk.gdk.Color()
+ pix = gtk.gdk.pixmap_create_from_data(None, pix_data, 1, 1, 1, color, color)
+ self.invisible_cursor = gtk.gdk.Cursor(pix,pix,color,color,0,0)
+
+ def handle_motion(self,widget,event):
+ if event.x < 0:
+ X = 0
+ elif event.x > 1200:
+ X = 1200
+ else:
+ X = event.x
+
+ if event.y < 0:
+ Y = 0
+ elif event.y > 900:
+ Y = 900
+ else:
+ Y = event.y
+
+ self.current_x = X
+ self.current_y = Y
+ if self.buttonPressed:
+ self.final_x = X - self.first_x
+ self.final_y = Y - self.first_y
+ self.csnd.setTrackpadX(self.final_x)
+ self.csnd.setTrackpadY(self.final_y)
+
+ def handle_keyPress(self,widget,event):
+ if KEY_MAP_PIANO.has_key(event.hardware_keycode) and self.buttonPressed == False:
+ gtk.gdk.pointer_grab(self.win.window, event_mask = gtk.gdk.POINTER_MOTION_MASK, cursor = self.invisible_cursor )
+ self.buttonPressed = True
+ self.first_x = self.current_x
+ self.first_y = self.current_y
+
+ def handle_keyRelease(self,widget,event):
+ if KEY_MAP_PIANO.has_key(event.hardware_keycode):
+ gtk.gdk.pointer_ungrab(time = 0L)
+ self.buttonPressed = False
+ self.restoreDelay = gobject.timeout_add(120, self.restore)
+
+ def restore( self ):
+ self.csnd.setTrackpadX(0)
+ self.csnd.setTrackpadY(0)
+ gobject.source_remove( self.restoreDelay )
+
diff --git a/Resources/univorc.csd b/Resources/univorc.csd
index 2063556..b2ee4a7 100644
--- a/Resources/univorc.csd
+++ b/Resources/univorc.csd
@@ -400,6 +400,15 @@ Reverb + master out
instr 200
gktime timek
+
+kTrackpadX chnget "trackpadX"
+gkTrackpadX = kTrackpadX / 2400.
+gkTrackpadX limit gkTrackpadX, -1, 1
+
+kTrackpadY chnget "trackpadY"
+gkTrackpadY = kTrackpadY / 500.
+gkTrackpadY limit -gkTrackpadY, -1, 1
+
koutGain chnget "masterVolume"
koutGain = koutGain * 0.01
gkduck init 1
@@ -646,7 +655,10 @@ kls portk p13, igliss, p13
kle portk p14, igliss, p14
kcd portk p15, igliss, p15
-a1 flooper2 1, kpitch, kls, kle, kcd, p8, 0, 0, 0, iskip
+kpitchBend port gkTrackpadX, .03
+kampBend port gkTrackpadY, .03
+
+a1 flooper2 1*(1+kampBend), kpitch*(1+kpitchBend), kls, kle, kcd, p8, 0, 0, 0, iskip
if (p11-1) != -1 then
acomp = a1
diff --git a/SynthLab/Parameter.py b/SynthLab/Parameter.py
index bf158b0..add1d21 100644
--- a/SynthLab/Parameter.py
+++ b/SynthLab/Parameter.py
@@ -15,7 +15,6 @@ class Parameter( gtk.Window ):
self.modify_bg(gtk.STATE_NORMAL, color)
self.move(15, 660)
self.set_size_request(450, 40)
-# self.move(500, 50)
self.set_decorated(False)
mainBox = RoundHBox(fillcolor=Config.INST_BCK_COLOR, bordercolor=Config.INST_BCK_COLOR)
mainBox.set_border_width(4)
@@ -27,3 +26,4 @@ class Parameter( gtk.Window ):
def update( self, string ):
self.text.set_text(string)
+
diff --git a/SynthLab/SynthLabParametersWindow.py b/SynthLab/SynthLabParametersWindow.py
index b1d15c9..bfdb351 100644
--- a/SynthLab/SynthLabParametersWindow.py
+++ b/SynthLab/SynthLabParametersWindow.py
@@ -138,6 +138,7 @@ class SynthLabParametersWindow( gtk.Window ):
def onKeyPress(self,widget,event):
key = event.hardware_keycode
+ print 'from slider window: %ld' % key
if key not in Config.KEY_MAP:
return
midiPitch = Config.KEY_MAP[key]
@@ -195,7 +196,7 @@ class SynthLabParametersWindow( gtk.Window ):
def hideParameter( self, widget, data=None ):
if self.parameterOpen and not self.clockStart:
- self.windowCloseDelay = gobject.timeout_add(500, self.closeParameterWindow)
+ self.windowCloseDelay = gobject.timeout_add(300, self.closeParameterWindow)
self.clockStart = 1
self.tooltipsUpdate()
if self.instanceID != 12:
diff --git a/SynthLab/SynthLabWindow.py b/SynthLab/SynthLabWindow.py
index 3e9085a..4686915 100644
--- a/SynthLab/SynthLabWindow.py
+++ b/SynthLab/SynthLabWindow.py
@@ -381,14 +381,6 @@ class SynthLabWindow( gtk.Window ):
gate = self.testGates( i, event.x-self.locations[i][0], event.y-self.locations[i][1] )
if gate:
self.highlightGate( i, gate )
- choosen = SynthLabConstants.CHOOSE_TYPE[i/4][self.typesTable[i]]
- str = Tooltips.SYNTHTYPES[i/4][self.typesTable[i]] + ': ' + Tooltips.SYNTHPARA[choosen][gate[1]]
- if gate[0] == 1:
- if self.parameterOpen:
- self.parameterUpdate( str )
- else:
- self.parameter = Parameter( str )
- self.parameterOpen = 1
else:
self.highlightGate( None )
if self.parameterOpen:
@@ -501,6 +493,15 @@ class SynthLabWindow( gtk.Window ):
y = self.locations[self.overGateObj][1] + self.overGate[3][1] - self.overGateSizeDIV2
self.overGateLoc = ( x, y )
self.invalidate_rect( self.overGateLoc[0], self.overGateLoc[1], self.overGateSize, self.overGateSize )
+ if obj != 12:
+ choosen = SynthLabConstants.CHOOSE_TYPE[obj/4][self.typesTable[obj]]
+ str = Tooltips.SYNTHTYPES[obj/4][self.typesTable[obj]] + ': ' + Tooltips.SYNTHPARA[choosen][gate[1]]
+ if gate[0] == 1:
+ if self.parameterOpen:
+ self.parameterUpdate( str )
+ else:
+ self.parameter = Parameter( str )
+ self.parameterOpen = 1
def startDragObject( self, i ):
self.dragObject = i
@@ -795,13 +796,13 @@ class SynthLabWindow( gtk.Window ):
def writeTables( self, typesTable, controlParametersTable, sourceParametersTable, fxParametersTable ):
mess = 'f5200 0 16 -2 ' + " ".join([str(n) for n in controlParametersTable])
self.csnd.inputMessage( mess )
- time.sleep(0.01)
+ time.sleep(0.005)
mess = "f5201 0 16 -2 " + " " .join([str(n) for n in sourceParametersTable])
self.csnd.inputMessage( mess )
- time.sleep(.01)
+ time.sleep(.005)
mess = "f5202 0 16 -2 " + " " .join([str(n) for n in fxParametersTable])
self.csnd.inputMessage( mess )
- time.sleep(.01)
+ time.sleep(.005)
self.typesTable = typesTable
lastTable = [0]*12
for i in range(12):
@@ -809,7 +810,7 @@ class SynthLabWindow( gtk.Window ):
lastTable[i] = (typesTable[i]+1)
mess = "f5203 0 16 -2 " + " " .join([str(n) for n in lastTable]) + " 0 0 0 0"
self.csnd.inputMessage( mess )
- time.sleep(.01)
+ time.sleep(.005)
if lastTable[4] == 8:
snd = Config.SOUNDS_DIR + '/' + self.sample_names[int(sourceParametersTable[1])]
mess = "f5501 0 32768 -1 " + "\"%s\" 0 0 0" % snd
@@ -826,7 +827,7 @@ class SynthLabWindow( gtk.Window ):
snd = Config.SOUNDS_DIR + '/' + self.sample_names[int(sourceParametersTable[13])]
mess = "f5504 0 32768 -1 " + "\"%s\" 0 0 0" % snd
self.csnd.inputMessage( mess )
- time.sleep(.01)
+ time.sleep(.005)
self.loadPixmaps(typesTable)
self.invalidate_rect( 0, 0, self.drawingAreaWidth, self.drawingAreaHeight )
diff --git a/Util/CSoundClient.py b/Util/CSoundClient.py
index 0862282..5b3802c 100644
--- a/Util/CSoundClient.py
+++ b/Util/CSoundClient.py
@@ -171,6 +171,14 @@ class CSoundClientPlugin( CSoundClientBase ):
if self.on:
sc_setMasterVolume(volume)
+ def setTrackpadX( self, value ):
+ trackpadX = value
+ sc_setTrackpadX(trackpadX)
+
+ def setTrackpadY( self, value ):
+ trackpadY = value
+ sc_setTrackpadY(trackpadY)
+
def micRecording( self, table ):
sc_inputMessage( Config.CSOUND_MIC_RECORD % table )
diff --git a/Util/Clooper/Makefile b/Util/Clooper/Makefile
index c853a6d..87caf59 100644
--- a/Util/Clooper/Makefile
+++ b/Util/Clooper/Makefile
@@ -16,7 +16,7 @@ all : _SClient.so cmd_csound
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
clean :
- rm -f *.o _ttest.so
+ rm -f *.o _ttest.so _SClient.so
cmd_csound: cmd_csound.cpp SoundClient.o
g++ -o $@ $^ -lcsound
diff --git a/Util/Clooper/SClient.py b/Util/Clooper/SClient.py
index 5171569..d27472b 100644
--- a/Util/Clooper/SClient.py
+++ b/Util/Clooper/SClient.py
@@ -53,6 +53,8 @@ sc_initialize = _SClient.sc_initialize
sc_start = _SClient.sc_start
sc_stop = _SClient.sc_stop
sc_setMasterVolume = _SClient.sc_setMasterVolume
+sc_setTrackpadX = _SClient.sc_setTrackpadX
+sc_setTrackpadY = _SClient.sc_setTrackpadY
sc_inputMessage = _SClient.sc_inputMessage
sc_scoreEvent4 = _SClient.sc_scoreEvent4
sc_scoreEvent15 = _SClient.sc_scoreEvent15
diff --git a/Util/Clooper/SoundClient.cpp b/Util/Clooper/SoundClient.cpp
index e527c02..6bc92eb 100644
--- a/Util/Clooper/SoundClient.cpp
+++ b/Util/Clooper/SoundClient.cpp
@@ -363,6 +363,35 @@ struct TamTamSound
}
}
+ void setTrackpadX(MYFLT value)
+ {
+ if (!csound) {
+ fprintf(stderr, "skipping %s, csound==NULL\n", __FUNCTION__);
+ return ;
+ }
+ MYFLT *p;
+ if (!(csoundGetChannelPtr(csound, &p, "trackpadX", CSOUND_CONTROL_CHANNEL | CSOUND_INPUT_CHANNEL)))
+ *p = (MYFLT) value;
+ else
+ {
+ fprintf(_debug, "ERROR: failed to set trackpad X value\n");
+ }
+ }
+
+ void setTrackpadY(MYFLT value)
+ {
+ if (!csound) {
+ fprintf(stderr, "skipping %s, csound==NULL\n", __FUNCTION__);
+ return ;
+ }
+ MYFLT *p;
+ if (!(csoundGetChannelPtr(csound, &p, "trackpadY", CSOUND_CONTROL_CHANNEL | CSOUND_INPUT_CHANNEL)))
+ *p = (MYFLT) value;
+ else
+ {
+ fprintf(_debug, "ERROR: failed to set trackpad Y value\n");
+ }
+ }
};
TamTamSound * sc_tt = NULL;
@@ -400,6 +429,16 @@ void sc_setMasterVolume(MYFLT v)
sc_tt->setMasterVolume(v);
}
+void sc_setTrackpadX(MYFLT v)
+{
+ sc_tt->setTrackpadX(v);
+}
+
+void sc_setTrackpadY(MYFLT v)
+{
+ sc_tt->setTrackpadY(v);
+}
+
void sc_inputMessage(const char *msg)
{
sc_tt->inputMessage(msg);
diff --git a/Util/Clooper/SoundClient.h b/Util/Clooper/SoundClient.h
index a946ced..561529b 100644
--- a/Util/Clooper/SoundClient.h
+++ b/Util/Clooper/SoundClient.h
@@ -13,6 +13,8 @@ extern "C"
int sc_start();
int sc_stop();
void sc_setMasterVolume(float);
+ void sc_setTrackpadX(float);
+ void sc_setTrackpadY(float);
void sc_inputMessage(const char *msg);
void sc_scoreEvent4(char type, MYFLT p0, MYFLT p1, MYFLT p2, MYFLT p3);
void sc_scoreEvent15(char type, MYFLT p1, MYFLT p2, MYFLT p3, MYFLT p4, MYFLT p5, MYFLT p6, MYFLT p7, MYFLT p8, MYFLT p9, MYFLT p10, MYFLT p11, MYFLT p12, MYFLT p13, MYFLT p14, MYFLT p15);
diff --git a/Util/Clooper/_SClient.so b/Util/Clooper/_SClient.so
index adbf427..66074a6 100755
--- a/Util/Clooper/_SClient.so
+++ b/Util/Clooper/_SClient.so
Binary files differ
diff --git a/Util/Clooper/_ttest.so b/Util/Clooper/_ttest.so
deleted file mode 100755
index 2019799..0000000
--- a/Util/Clooper/_ttest.so
+++ /dev/null
Binary files differ
diff --git a/Util/NoteDB.py b/Util/NoteDB.py
index ff6f2d4..0ed1c2d 100644
--- a/Util/NoteDB.py
+++ b/Util/NoteDB.py
@@ -50,6 +50,9 @@ class NoteDB:
self.nextId = 0 # base id, first page will be 1
+ self.clipboard = [] # stores copied cs notes
+ self.clipboardArea = [] # stores the limits and tracks for each page in the clipboard
+
#-- private --------------------------------------------
def _genId( self ):
self.nextId += 1
@@ -224,16 +227,21 @@ class NoteDB:
# ... up to N
# page id or -1 to exit
def addNotes( self, stream ):
+ new = {}
i = [-1]
p = self._readstream(stream,i)
while p != -1:
+ if p not in new:
+ new[p] = [ [] for x in range(Config.NUMBER_OF_TRACKS) ]
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 )
+ new[p][t].append( self.addNote( p, t, self._readstream(stream,i), hint ) )
p = self._readstream(stream,i)
+ return new
+
def deleteNote( self, page, track, id ):
ind = self.noteS[page][track].index( self.noteD[page][track][id] )
@@ -265,14 +273,17 @@ class NoteDB:
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 deleteNotesByTrack( self, pages, tracks ):
+ for p in pages:
+ for t in tracks:
+ notes = self.noteS[p][t][:]
+ for n in notes:
+ self.deleteNote( p, t, n.id )
def duplicateNote( self, page, track, id, toPage, toTrack, offset ):
cs = self.noteD[page][track][id].cs.clone()
- cs.track = toTrack
+ cs.trackId = toTrack
+ cs.pageId = toPage
cs.onset += offset
ticks = self.pages[toPage].ticks
if cs.onset >= ticks: return False # off the end of the page
@@ -340,7 +351,7 @@ class NoteDB:
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)] )
+ self.updateNote( p, t, self._readstream(stream,i), param, self._readstream(stream,i) )
p = self._readstream(stream,i)
#-- private --------------------------------------------
@@ -354,7 +365,7 @@ class NoteDB:
while ins > 0: # check backward
onset = self.noteS[page][track][ins-1].cs.onset
if onset <= cs.onset:
- if onset < cs.onset: break
+ if onset <= cs.onset: break
elif self.noteS[page][track][ins-1].cs.pitch <= cs.pitch: break
ins -= 1
if ins == out: # check forward
@@ -364,7 +375,7 @@ class NoteDB:
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
+ elif self.noteS[page][track][ins].cs.pitch > cs.pitch: break
ins += 1
if ins != out: # resort
@@ -377,6 +388,136 @@ class NoteDB:
#=======================================================
+ # Clipboard Functions
+
+ # stream format:
+ # page id
+ # track index
+ # number of following notes (N)
+ # note id
+ # ... up to N
+ # page id or -1 to exit
+ def notesToClipboard( self, stream ):
+ self.clipboard = []
+ self.clipboardArea = []
+ i = [-1]
+ pages = []
+ p = self._readstream(stream,i)
+ while p != -1:
+ if p not in pages:
+ page = [ [] for x in range(Config.NUMBER_OF_TRACKS) ]
+ pageArea = { "limit": [ 99999, 0 ],
+ "tracks": [ 0 for x in range(Config.NUMBER_OF_TRACKS) ] }
+ pages.append(p)
+ self.clipboard.append(page)
+ self.clipboardArea.append(pageArea)
+ else:
+ ind = pages.index(p)
+ page = self.clipboard[ind]
+ pageArea = self.clipboardArea[ind]
+ t = self._readstream(stream,i)
+ pageArea["tracks"][t] = 1
+ N = self._readstream(stream,i)
+ for j in range(N):
+ cs = self.noteD[p][t][self._readstream(stream,i)].cs.clone()
+ if cs.onset < pageArea["limit"][0]: pageArea["limit"][0] = cs.onset
+ if cs.onset + cs.duration > pageArea["limit"][1]: pageArea["limit"][1] = cs.onset + cs.duration
+ page[t].append( cs )
+ p = self._readstream(stream,i)
+
+ return self.clipboardArea
+
+ def tracksToClipboard( self, pages, tracks ):
+ self.clipboard = []
+ self.clipboardOrigin = [ 0, 0 ]
+ self.clipboardArea = []
+ for p in pages:
+ page = [ [] for x in range(Config.NUMBER_OF_TRACKS) ]
+ pageArea = { "limit": [ 0, 99999 ],
+ "tracks": [ 0 for x in range(Config.NUMBER_OF_TRACKS) ] }
+ self.clipboard.append(page)
+ self.clipboardArea.append(pageArea)
+ for t in tracks:
+ pageArea["tracks"][t] = 1
+ for id in self.noteD[p][t]:
+ cs = self.noteD[p][t][id].cs.clone()
+ page[t].append( cs )
+
+ return self.clipboardArea
+
+ # trackMap = { X: Y, W: Z, ... }; X,W are track indices, Y,Z are clipboard indices
+ def pasteClipboard( self, pages, offset, trackMap ):
+ if not len(self.clipboard): return
+
+ deleteStream = []
+ updateStream = []
+ addStream = []
+
+ pp = 0
+ ppMax = len(self.clipboard)
+ for p in pages:
+ ticks = self.pages[p].ticks
+ area = self.clipboardArea[pp]
+ area["limit"][0] += offset
+ area["limit"][1] += offset
+ for t in trackMap.keys():
+ if not area["tracks"][trackMap[t]]: continue
+ tdeleteStream = []
+ tupdateOStream = []
+ tupdateDStream = []
+ taddStream = []
+ # clear area
+ for n in self.noteS[p][t]:
+ start = n.cs.onset
+ end = start + n.cs.duration
+ if area["limit"][0] <= start < area["limit"][1]: start = area["limit"][1]
+ if area["limit"][0] < end <= area["limit"][1]: end = area["limit"][0]
+ if start < area["limit"][0] and end > area["limit"][1]: end = area["limit"][0]
+ if end <= start:
+ tdeleteStream.append( n.id )
+ elif start != n.cs.onset:
+ tupdateDStream += [ n.id, end - start ]
+ tupdateOStream += [ n.id, start ]
+ elif end != start + n.cs.duration:
+ tupdateDStream += [ n.id, end - start ]
+ if len(tdeleteStream):
+ deleteStream += [ p, t, len(tdeleteStream) ] + tdeleteStream
+ if len(tupdateOStream):
+ updateStream += [ p, t, PARAMETER.ONSET, len(tupdateOStream)//2 ] + tupdateOStream
+ if len(tupdateDStream):
+ updateStream += [ p, t, PARAMETER.DURATION, len(tupdateDStream)//2 ] + tupdateDStream
+ # paste notes
+ for cs in self.clipboard[pp][trackMap[t]]:
+ newcs = cs.clone()
+ newcs.onset += offset
+ if newcs.onset >= ticks: continue
+ if newcs.onset + newcs.duration > ticks:
+ newcs.duration = ticks - newcs.onset
+ newcs.pageId = p
+ newcs.trackId = t
+ # TODO update the cs.instrument or any other parameters?
+ taddStream.append( newcs )
+ if len(taddStream):
+ addStream += [ p, t, len(taddStream) ] + taddStream
+
+ pp += 1
+ if pp == ppMax: pp -= ppMax
+
+ if len(deleteStream):
+ self.deleteNotes( deleteStream + [-1] )
+ if len(updateStream):
+ self.updateNotes( updateStream + [-1] )
+ if len(addStream):
+ return self.addNotes( addStream + [-1] )
+
+ return None
+
+ def getClipboardArea( self, ind ):
+ N = len(self.clipboardArea)
+ while ind >= N: ind -= N
+ return self.clipboardArea[ind]
+
+ #=======================================================
# Listener Functions
def addListener( self, listener, parasite = None, page = False, note = False ):
@@ -438,6 +579,11 @@ class NoteDB:
def getPageIndex( self, page ):
return self.tune.index(page)
+ def getNote( self, page, track, id, listener = None ):
+ if listener:
+ return self.parasiteD[page][track][listener][id]
+ return self.noteD[page][track][id]
+
def getNotesByPage( self, page, listener = None ):
notes = []
if listener:
diff --git a/scripts/clean_trailing_whitespace.py b/scripts/clean_trailing_whitespace.py
index 13532b2..57e793c 100644
--- a/scripts/clean_trailing_whitespace.py
+++ b/scripts/clean_trailing_whitespace.py
@@ -15,7 +15,7 @@ r = open(bakName)
w = open(fName, "w" )
for line in r:
line = line[:-1]
- while len(line) and (line[-1] == " " or line[-1] == " "):
+ while len(line) and (line[-1] == " " or line[-1] == " "):
line = line[:-1]
w.write(line+"\n")