session.data.tempoMap.isTransportMaster=True#always true for Laborejo.
session.data.tempoMap.isTransportMaster=True#always true for Laborejo.
callbacks._tracksChanged()# This creates the frontend/GUI tracks with access through track ids. From now on we can send the GUI a trId and it knows which track needs change.
fortrIdinsession.data.listOfTrackIds():
callbacks._updateTrack(trId)#create content: music items
@ -302,6 +293,8 @@ def startEngine(nsmClient):
fortrackinsession.data.hiddenTracks:#After loading a file some tracks could be hidden. We want midi for them as well.
track.staticRepresentation()#that generates the calfbox data as a side effect. discard the other data.
useCurrentTrackAsMetronome()
callbacks._setCursor()
globallaborejoEngineStarted#makes for a convenient check. stepMidiInput uses it, which needs to know that the gui already started the api.
#startPos = session.data.where() #we cannot use where() since we don't know if a content linked block before our current position gets new items in the duplication process
#TODO: when duplicating with content links or at least inside a content link the selection gets lost completely. Maybe this cannot be avoided, but review that in the future.
copyItem.parentBlocks.add(new)#parentBlock was empty until now
assertnotcopyItem.parentBlocks#parentBlock was empty until now
copyItem.parentBlocks.add(new)
ifselfincopyItem.parentBlocks:#TODO: investigate
copyItem.parentBlocks.remove(self)
@ -301,7 +302,7 @@ class Block(object):
definsert(self,item):
self.data.insert(self.localCursorIndex,item)#we do not need to check if appending or not. list.insert appends if the index is higher then len()
#self.localCursorIndex += 1 #we don't need to go right here because track.insert() is calling its own right() directly after insert, which triggers block.right()
item.parentBlocks.add(self)
item.parentBlocks.add(self)
defdelete(self):
"""The commented out is the immediate garbage collector which
"""Move the oldItems parentBlocks to the new item"""
self.parentBlocks=oldItem.parentBlocks
"""Move the oldItems parentBlocks to the new item"""
#self.parentBlocks = oldItem.parentBlocks #TODO. We took that out when pasting after deleting a track and recreating failed. Why do we to copy the parentBlocks when a parentBlock is added during block.insert anyway? Wild guess: we don't.
@ -56,11 +56,12 @@ class Data(template.engine.sequencer.Score):
self.cursorWhenSelectionStarted=None#A cursor dict, from self.cursorExport(). Is None when there is no selection. Can be used for "if selection:" questions. Gets changed quite often.
self.copyObjectsBuffer=[]#for copy and paste. obviously empty after load file. Also not saved.
self.cachedTrackDurations={}#updated after every track export
self.metronome=Metronome(parentData=self)#Purely dynamic structure. No save/load. No undo/redo
self.currentMetronomeTrack=None#A Laborejo Track, indepedent of currentTrack. The metronome is in self.metronome, set by the template Score.
defduration(self):
"""Return the duration of the whole score, in ticks"""
#TODO: use cached duration? How often is this used?
#TODO: use cached duration? How often is this used? Pretty often. 3 Times for a single track note update.
#TODO: Measure before trying to improve performance.
result=[]
fortrackinself.tracks:
result.append(track.duration())
@ -506,7 +507,7 @@ class Data(template.engine.sequencer.Score):
#TODO: check if the cursor is still in the correct position after delete selection
listOfChangedTrackIds=set()
fornumber,trackinenumerate(workBuffer):
curTrack=self.currentTrack()
#Here comes an additional condition. Most of the time pastes are single-track. If so, or we simply are in the starting track, we want to paste at the actual position, not the tickindex. This circumvents problems with block boundaries and zero-duration items.
@ -597,8 +599,8 @@ class Data(template.engine.sequencer.Score):
foritemintrack:
newItem=item.copy()
curTrack.insert(newItem)#eventhough copyObjectsBuffer is already a copy of the original notes we don't want every paste to be the same instances.
forparentBlockinnewItem.parentBlocks:#TODO: just be naive and add them all for now. Performance can be improved later.
#newItem has now a parentBlock and a parentTrack. We add this parent track to the list of changed Tracks
listOfChangedTrackIds.update(newItem.parentTrackIds)#TODO: profiling. All items in a block have the same parentTrack. This is highly redundant BUT can be different for items which originated from block boundaries
ifnotnumber+1==len(workBuffer):#enumerate from 0, len from 1.
#we have to prevent the track going down one too far in the last step and messing with the tick index though.
self.trackDown()
@ -609,10 +611,13 @@ class Data(template.engine.sequencer.Score):
#Return to the item where pasting starting. Pasting counts as insert, and insert sticks to the item right of cursor.
#Therefore we go to the starting track and block, but not to the starting localCursorIndexInBlock. Instead we search for the specific item in a second step
self.goToItemInCurrentBlock(startItem)#we actually want to be at the same item where we started, not just the tick index which gets confused with block boundaries and zero duration items
try:#overwrite?
try:#overwrite? #TODO: What is that? assert with AssertionError pass?
allTracks=WeakValueDictionary()#key is the trackId, value is the weak reference to the Track. Deleted tracks (from the current session) and hidden tracks are in here as well.
#The instrument names are also handled by the trackState so that the cursor knows about instrument changes
self.initialInstrumentName=""#different than the track name. e.g. "Violin"
self.initialShortInstrumentName=""# e.g. "vl"
self.asMetronomeData=None#This track as metronome version. Is always up to date through export.
self._processAfterInit()
@ -1060,10 +1062,10 @@ class Track(object):
#########
#Metronome start
#The metronome cannot be calculated by simply looking at metricalInstructions. We need to look at the barlines. Two instructions in a row at the same tick are wrong, but technically possible. These are not two full measures of metronome
#TODO: metronome activate
#if self is self.parentData.currentMetronomeTrack:
# self.parentData.metronome.generate(((pos, m.isMetrical, m.treeOfInstructions) for pos, m in barlines.items()))
#The metronome cannot be calculated by simply looking at metricalInstructions.
#We need to look at the barlines. Two instructions in a row at the same tick are wrong,
#but technically possible. These are not two full measures of metronome
self.liveMidiWaitingForNoteOff={}#all unfinished midi notes (key is currently held down). We use one for all tracks because we have a callback in the cursor update function that iterates over all live notes. We don't want that to iterate over all tracks just to find the current live notes.
self.duringTrackDragAndDrop=None#switched to a QGraphicsItem (e.g. GuiTrack) while a track is moved around by the mouse
self.duringBlockDragAndDrop=None#switched to a QGraphicsItem (e.g. GuiTrack) while a block is moved around by the mouse
@ -584,11 +572,6 @@ class GuiScore(QtWidgets.QGraphicsScene):