new=type(self)(self.program,self.msb,self.lsb,self.instrumentName,self.shortInstrumentName)#all init parameters are immutable types and copied implicitly
new=type(self)(self.program,self.msb,self.lsb,self.shortInstrumentName)#all init parameters are immutable types and copied implicitly
returnnew
def_exportObject(self,trackState):
@ -2671,8 +2669,8 @@ class InstrumentChange(Item):
cbox.Pattern.serialize_event(tickPosition,0xB0+trackState.midiChannel(),0,self.msb),#position, status byte+channel, controller number, controller value
cbox.Pattern.serialize_event(tickPosition,0xB0+trackState.midiChannel(),32,self.lsb),#position, status byte+channel, controller number, controller value
],
"instrumentName":self.instrumentName,
],
"shortInstrumentName":self.shortInstrumentName,
"UIstring":"{}[pr{}{}{}]".format(_nameStr,self.program,_msbStr,_lsbStr),#this is for a UI, possibly a text UI, maybe for simple items of a GUI. Make it as short and unambigious as possible.
}
#hier weiter machen. die midiBytes können so exportiert werden, wahrscheinlich auch auf diesem channel.
#Aber die müssen im Track dann am Ende rausgefiltert werden und auf einen eigenen cbox midi track.
#Die Alternative wäre das aufzuteilen. Eins auf CC0, eins auf CC32 und der Program Change auf einen eigenen Kanal?
#Was ist mit sowas wie initial program? Wo wird das geändert? In den Track Properties. Vielleichst ist das selbst ein InstrumentChange Objekt. Könnte eins sein zumindest.
}
def_lilypond(self):
"""called by block.lilypond(), returns a string.
Don't create white-spaces yourself, this is done by the structures.
t=self.selectionExport()#is there a selection at all?
t=self.selectionExport()#is there a selection at all?
ifnott:
return[False,None,None,[],[]]#mimickes the true result. the api can use this to decide to do nothing instead of processing the selection
else:
@ -450,9 +452,10 @@ class Data(template.engine.sequencer.Score):
#listOfChangedTrackIds = set([id(track) for track in tracksWithSelectedItems]) #this is not the same as tracksWithSelectedItems. It gets more ids than the initial ones here. A content-linked note outside a selected track will still be affected. We start with the trackIds we already know.
listOfChangedTrackIds=set()
finalResult=[selectionValid,topLeft,bottomRight,listOfChangedTrackIds]#listOfChangedTrackIds is mutable. It will change in the loop below
firstRound=True
fortrackintracksWithSelectedItems:
asserttrackinself.tracks,track#selecting from hidden or deleted tracks is impossible
originalPosition=track.state.position()
result=[]
@ -467,12 +470,15 @@ class Data(template.engine.sequencer.Score):
ifr==1:
# the item we want is already left of the cursor. So we want the previous item.
iftrack.state.tickindex<=endTick:#yes, it is correct that the state in the parameter is ahead by one position. Why is it that way? Because everything that matters, like new dynamics will only be parsed afterwards. The trackState is always correct except for the tickindex when exporting after parsing. Thats why exportObject sometimes substracts its own duration for finding its starting tick.
listOfChangedTrackIds.add(id(parentBlock.parentTrack))#there are items without parent Tracks. They have None. We must filter them out otherwise our changedTrackIds get polluted with id(None)
else:
continue
elifr==2:#block end. Again, this is already the previous state. right now we are in the next block already.
@ -482,7 +488,10 @@ class Data(template.engine.sequencer.Score):
track.toPosition(originalPosition)#has head() in it
finalResult.append(result)
#finalResult[4:] is only lists(tracks) with items
forchangedTrIdinlistOfChangedTrackIds:
assertself.trackById(changedTrId)inself.tracks,changedTrId#selecting from hidden or deleted tracks is impossible
returnfinalResult
#The next two functions have "Objects" in their name so copy gets not confused with block.copy or item.copy. This is Ctrl+C as in copy and paste.
@ -628,7 +637,10 @@ class Data(template.engine.sequencer.Score):
defdeleteSelection(self):
"""Mortals, Hear The Prophecy: Delete Selection is notoriously tricky. It was hard in Laborejo 1 and it is still hard
#We delete always from the same cursor position. After one deletion the rest items gravitate left to our position. Except block boundaries, those will not be crossed so we have to check for them:
foritem,cached6StateinselectionTrack:
foritem,cachedStateinselectionTrack:
ifitemisbottomRightCursor["item"]:
break#without this the program will delete the first non-selected item, if zero duration, as well
@ -696,6 +708,7 @@ class Data(template.engine.sequencer.Score):
#assert curTrack.state.tickindex == topLeftCursor["tickindex"] We cannot be sure of this. The selection could have started at a content-linked item that got deleted and the tickindex has no item anymore
#return [id(track) for track in self.tracks] #TODO: only for debug reasons. the GUI will draw everything with this! Either this function or the api has to figure out the tracks that really changed. we could do a len check of the blocks data.
returnlistOfChangedTrackIds
defgetBlockAndItemOrder(self):
@ -955,6 +968,11 @@ class Data(template.engine.sequencer.Score):
track=self.currentTrack()
startBlock=track.currentBlock()#this is the only unique thing we can rely on.
#startBlock will get deleted, but we don't remove the blocks as parent because we need them for undo
#They will be finally cleared on save.
#NO. This works for splitting, but not for undo. for item in startBlock.data:
@ -963,7 +981,6 @@ class Data(template.engine.sequencer.Score):
protoBlockRight=startBlock.copy(track)
protoBlockRight.data=startBlock.data[startBlock.localCursorIndex:]#everything right of the cursor. The local cursor index of the current block is guaranteed to be correct. The one from other blocks is not.
forblockinstartBlock.linkedContentBlocksInScore():#all linked blocks in all tracks, including currentBlock. All blocks are unique. The content is not.
self.duringLegatoSlur=False#there are no nested legato slurs
self.duringBeamGroup=False#no nested beam groups.
self.midiChannels=[track.initialMidiChannel]#these are just integers, not items. items.ChannelChange parsing changes this and items automatically get the new value. A stack of midi channels allows the cursor to know the current channel for immediate playback/feedback.