Browse Source

Prepare two new measure modifiers: step-delay and augment/diminish. Engine and save/load done but nothing in the GUI yet

master
Nils 1 year ago
parent
commit
e8fd18bf10
  1. 6
      CHANGELOG
  2. 127
      engine/api.py
  3. 38
      engine/pattern.py
  4. 51
      engine/track.py
  5. 2
      qtgui/songeditor.py

6
CHANGELOG

@ -1,7 +1,13 @@
2021-04-15 Version 2.1.1
Add status bar to explain possible user actions (like "use shift + mousewheel to transpose measure")
Streamline mousewheel behaviour in song editor. It now always scrolls without shift / alt key, no more accidental transpositions.
Add non-destructive step delay for individual measures. Shift an individual measures forwards or backwards in time, within the boundaries of the measure.
Add track-switch to choose step-delay wrap-around behaviour, if delayed notes end up outside the pattern
Add non-destructiv pattern augmentation/scaling. Make an individual measure shorter or longer overall, with the same GUI as transpositions.
Add track-switch to choose repeat-to-fill behaviour if scaling a pattern shorter, which creates empty space.
Fix wrong playback cursor speed.
Fix small GUI drawing issues
2021-02-15 Version 2.1.0
Full Undo/Redo

127
engine/api.py

@ -783,6 +783,8 @@ def trackMergeCopyFrom(sourceTrackId, targetTrackId):
targetTrack.structure = targetTrack.structure.union(sourceTrack.structure)
targetTrack.whichPatternsAreScaleTransposed.update(sourceTrack.whichPatternsAreScaleTransposed)
targetTrack.whichPatternsAreHalftoneTransposed.update(sourceTrack.whichPatternsAreHalftoneTransposed)
targetTrack.whichPatternsAreStepDelayed.update(sourceTrack.whichPatternsAreStepDelayed)
targetTrack.whichPatternsHaveAugmentationFactor.update(sourceTrack.whichPatternsHaveAugmentationFactor)
targetTrack.buildTrack()
updatePlayback()
callbacks._trackStructureChanged(targetTrack)
@ -801,6 +803,7 @@ def trackPatternReplaceFrom(sourceTrackId, targetTrackId):
callbacks._patternChanged(targetTrack)
#Transpositions and Modal Shifts
#StepDelay and AugmentationFactor
def _setSwitchesScaleTranspose(trackId, whichPatternsAreScaleTransposed):
"""For undo. Used by all functions as entry point, then calls itself for undo/redo.
@ -816,8 +819,9 @@ def _setSwitchesScaleTranspose(trackId, whichPatternsAreScaleTransposed):
callbacks._trackStructureChanged(track)
def setSwitchScaleTranspose(trackId, position, transpose):
"""Scale transposition is flipped. lower value means higher pitch"""
def setSwitchScaleTranspose(trackId, position:int, transpose:int):
"""Scale transposition is flipped. lower value means higher pitch.
Default value is 0."""
track = session.data.trackById(trackId)
if not track: return
session.history.register(lambda tr=trackId, v=track.whichPatternsAreScaleTransposed.copy(): _setSwitchesScaleTranspose(trackId, v), descriptionString="Set Modal Shift")
@ -841,8 +845,10 @@ def _setSwitchHalftoneTranspose(trackId, whichPatternsAreHalftoneTransposed):
updatePlayback()
callbacks._trackStructureChanged(track)
def setSwitchHalftoneTranspose(trackId, position, transpose):
"""Halftone transposition is not flipped. Higher value means higher pitch"""
def setSwitchHalftoneTranspose(trackId, position:int, transpose:int):
"""Halftone transposition is not flipped. Higher value means higher pitch
Default value is 0.
"""
track = session.data.trackById(trackId)
if not track: return
session.history.register(lambda tr=trackId, v=track.whichPatternsAreHalftoneTransposed.copy(): _setSwitchHalftoneTranspose(trackId, v), descriptionString="Set Half Tone Shift")
@ -852,8 +858,61 @@ def setSwitchHalftoneTranspose(trackId, position, transpose):
callbacks._trackStructureChanged(track)
return True
def _setSwitchStepDelay(trackId, whichPatternsAreStepDelayed):
"""For undo. Used by all functions as entry point, then calls itself for undo/redo.
whichPatternsAreStepDelayed is a dicts of int:int which we can copy with .copy()"""
track = session.data.trackById(trackId)
if not track: return
session.history.register(lambda tr=trackId, v=track.whichPatternsAreStepDelayed.copy(): _setSwitchStepDelay(trackId, v), descriptionString="Set Step Delay")
track.whichPatternsAreStepDelayed = whichPatternsAreStepDelayed #restore data
track.buildTrack()
updatePlayback()
callbacks._trackStructureChanged(track)
def setSwitchStepDelay(trackId, position:int, delay:int):
"""Public entry function for _setSwitchStepDelay.
Default value is 0"""
track = session.data.trackById(trackId)
if not track: return
session.history.register(lambda tr=trackId, v=track.whichPatternsAreStepDelayed.copy(): _setSwitchStepDelay(trackId, v), descriptionString="Set Step Delay")
track.whichPatternsAreStepDelayed[position] = delay
track.buildTrack()
updatePlayback()
callbacks._trackStructureChanged(track)
return True
def _setSwitchAugmentationsFactor(trackId, whichPatternsAreStepDelayed):
"""For undo. Used by all functions as entry point, then calls itself for undo/redo.
whichPatternsHaveAugmentationFactor is a dicts of int:float which we can copy with .copy()"""
track = session.data.trackById(trackId)
if not track: return
session.history.register(lambda tr=trackId, v=track.whichPatternsHaveAugmentationFactor.copy(): _setSwitchAugmentationsFactor(trackId, v), descriptionString="Set Augmentation Factor")
track.whichPatternsHaveAugmentationFactor = whichPatternsHaveAugmentationFactor #restore data
track.buildTrack()
updatePlayback()
callbacks._trackStructureChanged(track)
def setSwitchAugmentationsFactor(trackId, position:int, factor:float):
"""Public entry function for _setSwitchAugmentationsFactor.
Default value is 1.0"""
track = session.data.trackById(trackId)
if not track: return
session.history.register(lambda tr=trackId, v=track.whichPatternsHaveAugmentationFactor.copy(): _setSwitchAugmentationsFactor(trackId, v), descriptionString="Set Augmentation Factor")
track.whichPatternsHaveAugmentationFactor[position] = factor
track.buildTrack()
updatePlayback()
callbacks._trackStructureChanged(track)
return True
def _registerHistoryWholeTrackSwitches(track):
"""This is used by insertSilence, clearSwitchGroupTranspositions etc.
"""This is used by insertSilence, clearSwitchGroupModifications etc.
It assumes to run inside the context:
with session.history.sequence(""):
"""
@ -862,6 +921,8 @@ def _registerHistoryWholeTrackSwitches(track):
session.history.register(lambda trId=trackId, v=track.structure.copy(): _setTrackStructure(trId, v, "Change Group"), descriptionString="Change Group")
session.history.register(lambda trId=trackId, v=track.whichPatternsAreScaleTransposed.copy(): _setSwitchesScaleTranspose(trId, v), descriptionString="Set Modal Shift")
session.history.register(lambda trId=trackId, v=track.whichPatternsAreHalftoneTransposed.copy(): _setSwitchHalftoneTranspose(trId, v), descriptionString="Set Half Tone Shift")
session.history.register(lambda trId=trackId, v=track.whichPatternsAreStepDelayed.copy(): _setSwitchStepDelay(trId, v), descriptionString="Set Step Delay")
session.history.register(lambda trId=trackId, v=track.whichPatternsHaveAugmentationFactor.copy(): _setSwitchAugmentationsFactor(trId, v), descriptionString="Set Augmentation Factor")
def insertSilence(howMany:int, beforeMeasureNumber:int):
@ -880,6 +941,8 @@ def insertSilence(howMany:int, beforeMeasureNumber:int):
track.structure = set( (switch + thisTrackHowMany if switch >= thisTrackWhere else switch) for switch in track.structure )
track.whichPatternsAreScaleTransposed = { (k+thisTrackHowMany if k >= thisTrackWhere else k):v for k,v in track.whichPatternsAreScaleTransposed.items() }
track.whichPatternsAreHalftoneTransposed = { (k+thisTrackHowMany if k >= thisTrackWhere else k):v for k,v in track.whichPatternsAreHalftoneTransposed.items() }
track.whichPatternsAreStepDelayed = { (k+thisTrackHowMany if k >= thisTrackWhere else k):v for k,v in track.whichPatternsAreStepDelayed.items() }
track.whichPatternsHaveAugmentationFactor = { (k+thisTrackHowMany if k >= thisTrackWhere else k):v for k,v in track.whichPatternsHaveAugmentationFactor.items() }
callbacks._trackStructureChanged(track)
callbacks._dataChanged() #register undo
@ -906,6 +969,10 @@ def duplicateSwitchGroup(startMeasureForGroup:int, endMeasureExclusive:int):
track.whichPatternsAreScaleTransposed[switch] = track.whichPatternsAreScaleTransposed[switch-thisGroupSize]
if switch-thisGroupSize in track.whichPatternsAreHalftoneTransposed:
track.whichPatternsAreHalftoneTransposed[switch] = track.whichPatternsAreHalftoneTransposed[switch-thisGroupSize]
if switch-thisGroupSize in track.whichPatternsAreStepDelayed:
track.whichPatternsAreStepDelayed[switch] = track.whichPatternsAreStepDelayed[switch-thisGroupSize]
if switch-thisGroupSize in track.whichPatternsHaveAugmentationFactor:
track.whichPatternsHaveAugmentationFactor[switch] = track.whichPatternsHaveAugmentationFactor[switch-thisGroupSize]
callbacks._trackStructureChanged(track)
session.data.buildAllTracks()
@ -931,11 +998,14 @@ def exchangeSwitchGroupWithGroupToTheRight(startMeasureForGroup:int, endMeasureE
tempStructure = set() #integers
tempScaleTransposed = dict() #position:integers
tempHalfToneTransposed = dict() #position:integers
tempStepDelayed = dict() #position:integers
tempAugmentedFactor = dict() #position:floats
#Remember for later testing
lenStructure = len(track.structure)
lenScaleTransposed = len(track.whichPatternsAreScaleTransposed.keys())
lenHalfToneTransposed = len(track.whichPatternsAreHalftoneTransposed.keys())
lenStepDelayed = len(track.whichPatternsAreStepDelayed.keys())
lenAugmentedFactor = len(track.whichPatternsHaveAugmentationFactor.keys())
#First move right group into a temporary buffer to have it out of the way
for switch in range(thisTrackStartMeasure+groupSize, thisTrackEndMeasure+groupSize): #switch is a number
@ -948,6 +1018,12 @@ def exchangeSwitchGroupWithGroupToTheRight(startMeasureForGroup:int, endMeasureE
if switch in track.whichPatternsAreHalftoneTransposed:
tempHalfToneTransposed[switch] = track.whichPatternsAreHalftoneTransposed[switch]
del track.whichPatternsAreHalftoneTransposed[switch]
if switch in track.whichPatternsAreStepDelayed:
tempStepDelayed[switch] = track.whichPatternsAreStepDelayed[switch]
del track.whichPatternsAreStepDelayed[switch]
if switch in track.whichPatternsHaveAugmentationFactor:
tempAugmentedFactor[switch] = track.whichPatternsHaveAugmentationFactor[switch]
del track.whichPatternsHaveAugmentationFactor[switch]
#Now move current group to the right, which is now empty.
for switch in range(thisTrackStartMeasure, thisTrackEndMeasure): #switch is a number
@ -960,6 +1036,14 @@ def exchangeSwitchGroupWithGroupToTheRight(startMeasureForGroup:int, endMeasureE
if switch in track.whichPatternsAreHalftoneTransposed:
track.whichPatternsAreHalftoneTransposed[switch+groupSize] = track.whichPatternsAreHalftoneTransposed[switch]
del track.whichPatternsAreHalftoneTransposed[switch]
if switch in track.whichPatternsAreStepDelayed:
track.whichPatternsAreStepDelayed[switch+groupSize] = track.whichPatternsAreStepDelayed[switch]
del track.whichPatternsAreStepDelayed[switch]
if switch in track.whichPatternsHaveAugmentationFactor:
track.whichPatternsHaveAugmentationFactor[switch+groupSize] = track.whichPatternsHaveAugmentationFactor[switch]
del track.whichPatternsHaveAugmentationFactor[switch]
#Move old right-group into its new place
for sw in tempStructure:
@ -968,6 +1052,10 @@ def exchangeSwitchGroupWithGroupToTheRight(startMeasureForGroup:int, endMeasureE
track.whichPatternsAreScaleTransposed[stPos-groupSize] = stVal
for htPos, htVal in tempScaleTransposed.items():
track.whichPatternsAreHalftoneTransposed[hPos-groupSize] = htVal
for sDPos, sDVal in tempStepDelayed.items():
track.whichPatternsAreStepDelayed[sDPos-groupSize] = sDVal
for aFPos, aFVal in tempAugmentedFactor.items():
track.whichPatternsHaveAugmentationFactor[aFPos-groupSize] = aFVal
callbacks._trackStructureChanged(track)
@ -975,13 +1063,15 @@ def exchangeSwitchGroupWithGroupToTheRight(startMeasureForGroup:int, endMeasureE
assert lenStructure == len(track.structure), (lenStructure, len(track.structure))
assert lenScaleTransposed == len(track.whichPatternsAreScaleTransposed.keys()), (lenScaleTransposed, len(track.whichPatternsAreScaleTransposed.keys()))
assert lenHalfToneTransposed == len(track.whichPatternsAreHalftoneTransposed.keys()), (lenHalfToneTransposed, len(track.whichPatternsAreHalftoneTransposed.keys()))
assert lenStepDelayed == len(track.whichPatternsAreStepDelayed.keys()), (lenStepDelayed, len(track.whichPatternsAreStepDelayed.keys()))
assert lenAugmentedFactor == len(track.whichPatternsHaveAugmentationFactor.keys()), (lenAugmentedFactor, len(track.whichPatternsHaveAugmentationFactor.keys()))
callbacks._dataChanged() #register undo
session.data.buildAllTracks()
updatePlayback()
def clearSwitchGroupTranspositions(startMeasureForGroup:int, endMeasureExclusive:int):
def clearSwitchGroupModifications(startMeasureForGroup:int, endMeasureExclusive:int):
"""startMeasureForGroup and endMeasureExclusive are in the global, un-multiplied measure counting
format."""
with session.history.sequence("Clear all Group Transpositions"):
@ -994,6 +1084,10 @@ def clearSwitchGroupTranspositions(startMeasureForGroup:int, endMeasureExclusive
del track.whichPatternsAreScaleTransposed[switch]
if switch in track.whichPatternsAreHalftoneTransposed:
del track.whichPatternsAreHalftoneTransposed[switch]
if switch in track.whichPatternsAreStepDelayed:
del track.whichPatternsAreStepDelayed[switch]
if switch in track.whichPatternsHaveAugmentationFactor:
del track.whichPatternsHaveAugmentationFactor[switch]
callbacks._trackStructureChanged(track)
callbacks._dataChanged() #register undo
session.data.buildAllTracks()
@ -1037,6 +1131,25 @@ def deleteSwitches(howMany, fromMeasureNumber):
#else: #discard all in range to delete
track.whichPatternsAreHalftoneTransposed = new_halftoneTransposed
new_stepDelayed = dict()
for k,v in track.whichPatternsAreStepDelayed.items():
if k < thisTrackWhere:
new_stepDelayed[k] = v
elif k >= thisTrackWhere+thisTrackHowMany: #like a text editor let gravitate left into the hole left by the deleted range
new_stepDelayed[k-thisTrackHowMany] = v
#else: #discard all in range to delete
track.whichPatternsAreStepDelayed = new_stepDelayed
new_augmentFactor = dict()
for k,v in track.whichPatternsHaveAugmentationFactor.items():
if k < thisTrackWhere:
new_augmentFactor[k] = v
elif k >= thisTrackWhere+thisTrackHowMany: #like a text editor let gravitate left into the hole left by the deleted range
new_augmentFactor[k-thisTrackHowMany] = v
#else: #discard all in range to delete
track.whichPatternsHaveAugmentationFactor = new_augmentFactor
callbacks._trackStructureChanged(track)
callbacks._dataChanged() #register undo

38
engine/pattern.py

@ -270,15 +270,18 @@ class Pattern(object):
self.averageVelocity = int(median(n["velocity"] for n in self.data)) if self.data else DEFAULT_VELOCITY
self._exportCacheVersion += 1 #used by build pattern for its cache hash
def buildPattern(self, scaleTransposition, halftoneTransposition, howManyUnits, whatTypeOfUnit, subdivisions, patternLengthMultiplicator):
def buildPattern(self, scaleTransposition, halftoneTransposition, howManyUnits, whatTypeOfUnit, subdivisions, patternLengthMultiplicator, stepDelay, augmentationFactor):
"""return a cbox pattern ready to insert into a cbox clip.
This is the function to communicate with the outside, e.g. the track.
All ticks are relativ to the pattern start
This is called for every measure in a track. If you change the pattern it is called
for each existing modification once (transposition, stepDelay, AugmentFactor)
This is the function to communicate with the outside, e.g. the track.
We cache internally, but for the outside it looks like we generate a new pattern each time
on each little note update or transposition change.
All ticks are relative to the pattern start
The cache restores the cbox-pattern, not the note configuration in our python data. It is
used for placing the same pattern into multiple measures. But _not_ to return to a note
configuration we already had once. If you change the pattern itself the cache is cleared.
@ -295,6 +298,8 @@ class Pattern(object):
scale (as tuple so it is hashable)
self._exportCacheVersion
patternLengthMultiplicator
stepDelay
augmentationFactor
_cachedTransposedScale is updated with self.scale changes and therefore already covered.
@ -321,12 +326,14 @@ class Pattern(object):
api.swingPercentToAbsolute
"""
cacheHash = (scaleTransposition, halftoneTransposition, howManyUnits, whatTypeOfUnit, subdivisions, tuple(self.scale), self._exportCacheVersion, patternLengthMultiplicator)
cacheHash = (scaleTransposition, halftoneTransposition, howManyUnits, whatTypeOfUnit, subdivisions, tuple(self.scale), self._exportCacheVersion, patternLengthMultiplicator, stepDelay, augmentationFactor)
try:
return self._builtPatternCache[cacheHash]
except KeyError:
pass
#If uncertain if the cache works print cacheHash to see what is really different. This function is called more than once per pattern sometimes, which is correct.
oneMeasureInTicks = howManyUnits * whatTypeOfUnit
oneMeasureInTicks /= subdivisions #subdivisions is 1 by default. bigger values mean shorter durations, which is compensated by the user setting bigger howManyUnits manually.
oneMeasureInTicks = int(oneMeasureInTicks) * patternLengthMultiplicator
@ -336,7 +343,14 @@ class Pattern(object):
shuffle = int(self.parentTrack.parentData.swing * whatTypeOfUnit)
for noteDict in self.exportCache:
index = noteDict["index"]
if self.parentTrack.stepDelayWrapAround:
index = (noteDict["index"] + stepDelay) % howManyUnits
assert index >= 0, index
else:
index = noteDict["index"] + stepDelay
if index < 0 or index >= howManyUnits:
continue #skip lost step
startTick = index * whatTypeOfUnit
endTick = startTick + noteDict["factor"] * whatTypeOfUnit
startTick /= subdivisions
@ -349,12 +363,20 @@ class Pattern(object):
else: #off beats
startTick += shuffle
startTick = int(startTick)
endTick = int(endTick)
startTick = int(startTick * augmentationFactor)
endTick = int(endTick * augmentationFactor)
#Prevent augmented notes to start and hang when exceeding the pattern-length
if startTick >= oneMeasureInTicks-1: #-1 is important!!! Without it we will get hanging notes with factor 1.333
assert augmentationFactor > 1.0, augmentationFactor #can only happen for augmented patterns
continue #do not create a note, at all
if endTick > oneMeasureInTicks:
endTick = oneMeasureInTicks #all note off must end at the end of the pattern
noteDict["exceedsPlayback"] = True
if endTick / augmentationFactor > oneMeasureInTicks: #this is only for the visuals, the GUI. For them it did not exceed, it displays the original step.
noteDict["exceedsPlayback"] = True
else:
noteDict["exceedsPlayback"] = False
else:
noteDict["exceedsPlayback"] = False

51
engine/track.py

@ -45,7 +45,12 @@ class Track(object): #injection at the bottom of this file!
color:str=None,
whichPatternsAreScaleTransposed:Dict[int,int]=None,
whichPatternsAreHalftoneTransposed:Dict[int,int]=None,
simpleNoteNames:List[str]=None):
simpleNoteNames:List[str]=None,
whichPatternsAreStepDelayed:Dict[int,int]=None,
whichPatternsHaveAugmentationFactor:Dict[int,float]=None,
stepDelayWrapAround:bool=False,
repeatDiminishedPatternInItself:bool=False,
):
self.parentData = parentData
self.sequencerInterface = template.engine.sequencer.SequencerInterface(parentTrack=self, name=name) #needs parentData
@ -61,11 +66,15 @@ class Track(object): #injection at the bottom of this file!
#2.1
self.group = "" # "" is a standalone track, the normal one which existed since version 1.0. Using a name here will group these tracks together. A GUI can use this information. Also all tracks in a group share a single jack out port.
self.visible = True #only used together with groups. the api and our Datas setGroup function take care that standalone tracks are never hidden.
self.stepDelayWrapAround = False #every note that falls down on the "right side" of the pattern will wrap around to the beginning.
self.repeatDiminishedPatternInItself = False # if augmentationFactor < 1: repeat the pattern multiple times, as many as fit into the measure.
self.pattern = Pattern(parentTrack=self, scale=scale, simpleNoteNames=simpleNoteNames)
self.structure = structure if structure else set() #see buildTrack(). This is the main track data structure besides the pattern. Just integers (starts at 0) as switches which are positions where to play the patterns. In between are automatic rests.
self.whichPatternsAreScaleTransposed = whichPatternsAreScaleTransposed if whichPatternsAreScaleTransposed else {} #position:integers between -7 and 7. Reversed pitch, row based: -7 is higher than 7!!
self.whichPatternsAreHalftoneTransposed = whichPatternsAreHalftoneTransposed if whichPatternsAreHalftoneTransposed else {} #position:integers between -7 and 7. Reversed pitch, row based: -7 is higher than 7!!
self.whichPatternsAreStepDelayed = whichPatternsAreStepDelayed if whichPatternsAreStepDelayed else {} #position:signed integer
self.whichPatternsHaveAugmentationFactor = whichPatternsHaveAugmentationFactor if whichPatternsHaveAugmentationFactor else {} #position:float > 0
self._processAfterInit()
@property
@ -101,9 +110,13 @@ class Track(object): #injection at the bottom of this file!
assert ourCalfboxTrack, (ourCalfboxTrack, self, self.group, self.parentData, self.sequencerInterface)
#First clean the transpositions of zeroes
#First clean all modifications from the default value.
#We test for k in self.structure to not have modifications for measures that will be switched on later
#TODO: Can this possibly lead to a race condition where we load modifications first and then load the structure, which results in the mods getting perma-deleted here. e.g. we could not set default value in init, even for testing purposes.
self.whichPatternsAreScaleTransposed = {k:v for k,v in self.whichPatternsAreScaleTransposed.items() if v!=0 and k in self.structure}
self.whichPatternsAreHalftoneTransposed = {k:v for k,v in self.whichPatternsAreHalftoneTransposed.items() if v!=0 and k in self.structure}
self.whichPatternsAreStepDelayed = {k:v for k,v in self.whichPatternsAreStepDelayed.items() if v!=0 and k in self.structure}
self.whichPatternsHaveAugmentationFactor = {k:v for k,v in self.whichPatternsHaveAugmentationFactor.items() if v!=1.0 and k in self.structure} #default is 1.0
oneMeasureInTicks = (self.parentData.howManyUnits * self.parentData.whatTypeOfUnit) / self.parentData.subdivisions #subdivisions is 1 by default. bigger values mean shorter values, which is compensated by the user setting bigger howManyUnits manually.
oneMeasureInTicks = int(oneMeasureInTicks) * self.patternLengthMultiplicator
@ -118,7 +131,9 @@ class Track(object): #injection at the bottom of this file!
for index in filteredStructure:
scaleTransposition = self.whichPatternsAreScaleTransposed[index] if index in self.whichPatternsAreScaleTransposed else 0
halftoneTransposition = self.whichPatternsAreHalftoneTransposed[index] if index in self.whichPatternsAreHalftoneTransposed else 0
cboxPattern = self.pattern.buildPattern(scaleTransposition, halftoneTransposition, self.parentData.howManyUnits, self.parentData.whatTypeOfUnit, self.parentData.subdivisions, self.patternLengthMultiplicator)
stepDelay = self.whichPatternsAreStepDelayed[index] if index in self.whichPatternsAreStepDelayed else 0
augmentationFactor = self.whichPatternsHaveAugmentationFactor[index] if index in self.whichPatternsHaveAugmentationFactor else 1
cboxPattern = self.pattern.buildPattern(scaleTransposition, halftoneTransposition, self.parentData.howManyUnits, self.parentData.whatTypeOfUnit, self.parentData.subdivisions, self.patternLengthMultiplicator, stepDelay, augmentationFactor)
r = ourCalfboxTrack.add_clip(globalOffset + index*oneMeasureInTicks, 0, oneMeasureInTicks, cboxPattern) #pos, pattern-internal offset, length, pattern.
######Old optimisations. Keep for later####
@ -140,9 +155,13 @@ class Track(object): #injection at the bottom of this file!
"whichPatternsAreScaleTransposed" : self.whichPatternsAreScaleTransposed,
"whichPatternsAreHalftoneTransposed" : self.whichPatternsAreHalftoneTransposed,
"patternLengthMultiplicator" : self.patternLengthMultiplicator,
"whichPatternsAreStepDelayed" : self.whichPatternsAreStepDelayed,
"whichPatternsHaveAugmentationFactor" : self.whichPatternsHaveAugmentationFactor,
"midiChannel" : self.midiChannel,
"group" : self.group,
"visible" : self.visible,
"stepDelayWrapAround" : self.stepDelayWrapAround,
"repeatDiminishedPatternInItself" : self.repeatDiminishedPatternInItself,
}
@classmethod
@ -172,6 +191,28 @@ class Track(object): #injection at the bottom of this file!
else:
self.visible = True
if "stepDelayWrapAround" in serializedData:
self.stepDelayWrapAround = bool(serializedData["stepDelayWrapAround"])
else:
self.stepDelayWrapAround = False
if "repeatDiminishedPatternInItself" in serializedData:
self.repeatDiminishedPatternInItself = bool(serializedData["repeatDiminishedPatternInItself"])
else:
self.repeatDiminishedPatternInItself = False
if "whichPatternsAreStepDelayed" in serializedData:
self.whichPatternsAreStepDelayed = {int(k):int(v) for k,v in serializedData["whichPatternsAreStepDelayed"].items()} #json saves dict keys as strings
else:
self.whichPatternsAreStepDelayed = {}
if "whichPatternsHaveAugmentationFactor" in serializedData:
self.whichPatternsHaveAugmentationFactor = {int(k):float(v) for k,v in serializedData["whichPatternsHaveAugmentationFactor"].items()} #json saves dict keys as strings
else:
self.whichPatternsHaveAugmentationFactor = {}
self.color = serializedData["color"]
self.structure = set(serializedData["structure"])
self.whichPatternsAreHalftoneTransposed = {int(k):int(v) for k,v in serializedData["whichPatternsAreHalftoneTransposed"].items()} #json saves dict keys as strings
@ -195,9 +236,13 @@ class Track(object): #injection at the bottom of this file!
"numberOfMeasures": self.parentData.numberOfMeasures,
"whichPatternsAreScaleTransposed": self.whichPatternsAreScaleTransposed,
"whichPatternsAreHalftoneTransposed": self.whichPatternsAreHalftoneTransposed,
"whichPatternsAreStepDelayed": self.whichPatternsAreStepDelayed,
"whichPatternsHaveAugmentationFactor": self.whichPatternsHaveAugmentationFactor,
"midiChannel" : self.midiChannel+1, #1-16
"group" : self.group, #string
"visible" : self.visible, #bool. Always True for standalone tracks
"stepDelayWrapAround" : self.stepDelayWrapAround, #bool.
"repeatDiminishedPatternInItself" : self.repeatDiminishedPatternInItself, #bool
}
#Dependency Injections.

2
qtgui/songeditor.py

@ -434,7 +434,7 @@ class TrackStructure(QtWidgets.QGraphicsRectItem):
(QtCore.QCoreApplication.translate("SongStructure", "Exchange group with right neigbour"), lambda: api.exchangeSwitchGroupWithGroupToTheRight(startMeasureForGroup, endMeasureExclusive)),
(QtCore.QCoreApplication.translate("SongStructure", "Delete whole group"), lambda: api.deleteSwitches(howMany=measuresPerGroup, fromMeasureNumber=startMeasureForGroup)),
(QtCore.QCoreApplication.translate("SongStructure", "Duplicate whole group including measures"), lambda: api.duplicateSwitchGroup(startMeasureForGroup, endMeasureExclusive)),
(QtCore.QCoreApplication.translate("SongStructure", "Clear all group transpositions"), lambda: api.clearSwitchGroupTranspositions(startMeasureForGroup, endMeasureExclusive)),
(QtCore.QCoreApplication.translate("SongStructure", "Clear all group transpositions"), lambda: api.clearSwitchGroupModifications(startMeasureForGroup, endMeasureExclusive)),
]
for text, function in listOfLabelsAndFunctions:

Loading…
Cancel
Save