Browse Source

Add new track options to control step delay and augment behaviour to track context menu

master
Nils 3 years ago
parent
commit
0e0c6361c6
  1. 1
      CHANGELOG
  2. 52
      engine/api.py
  3. 1
      engine/pattern.py
  4. 10
      engine/track.py
  5. 1
      qtgui/mainwindow.py
  6. 11
      qtgui/songeditor.py

1
CHANGELOG

@ -6,6 +6,7 @@ Add track-switch to choose step-delay wrap-around behaviour, if delayed notes en
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.
Change user input to choose above measure modifications. Everything is now accesible by shortcuts, removing the functionality from the mousewheel.
Fix missing undo for measure modifications after delete.
Fix wrong playback cursor speed.
Fix small GUI drawing issues

52
engine/api.py

@ -523,6 +523,31 @@ def changeTrackMidiChannel(trackId, newChannel:int):
track.buildTrack()
updatePlayback()
callbacks._trackMetaDataChanged(track)
#But not the actual callback because nothing in a gui needs redrawing. Everything looks the same.
def changeTrackStepDelayWrapAround(trackId, newState:bool):
track = session.data.trackById(trackId)
if not track: return
session.history.register(lambda trId=trackId, v=track.stepDelayWrapAround: changeTrackStepDelayWrapAround(trId,v), descriptionString="Track Step Delay Wrap-Around")
track.stepDelayWrapAround = newState
track.pattern.buildExportCache()
track.buildTrack()
updatePlayback()
callbacks._trackMetaDataChanged(track)
#But not the actual callback because nothing in a gui needs redrawing. Everything looks the same.
def changeTrackRepeatDiminishedPatternInItself(trackId, newState:bool):
track = session.data.trackById(trackId)
if not track: return
session.history.register(lambda trId=trackId, v=track.repeatDiminishedPatternInItself: changeTrackRepeatDiminishedPatternInItself(trId,v), descriptionString="Track Repeat Diminished Pattern in itself")
track.repeatDiminishedPatternInItself = newState
track.pattern.buildExportCache()
track.buildTrack()
updatePlayback()
callbacks._trackMetaDataChanged(track)
#But not the actual callback because nothing in a gui needs redrawing. Everything looks the same.
def addTrack(scale=None):
if scale:
@ -716,7 +741,9 @@ def setSwitches(trackId, setOfPositions, newBool):
"""Used in the GUI to select multiple switches in a row by dragging the mouse"""
track = session.data.trackById(trackId)
if not track: return
session.history.register(lambda tr=trackId, v=track.structure.copy(): _setTrackStructure(trackId, v, "Set Measures"), descriptionString="Set Measures")
if newBool:
track.structure = track.structure.union(setOfPositions) #merge: add setOfPositions to the existing one
else:
@ -728,7 +755,9 @@ def setSwitches(trackId, setOfPositions, newBool):
def setSwitch(trackId, position, newBool):
track = session.data.trackById(trackId)
if not track: return
session.history.register(lambda trId=trackId, v=track.structure.copy(): _setTrackStructure(trId, v, "Set Measures"), descriptionString="Set Measures")
if newBool:
if position in track.structure: return
track.structure.add(position)
@ -841,10 +870,12 @@ def _setSwitchHalftoneTranspose(trackId, whichPatternsAreHalftoneTransposed):
session.history.register(lambda tr=trackId, v=track.whichPatternsAreHalftoneTransposed.copy(): _setSwitchHalftoneTranspose(trackId, v), descriptionString="Set Half Tone Shift")
track.whichPatternsAreHalftoneTransposed = whichPatternsAreHalftoneTransposed #restore data
track.buildTrack()
updatePlayback()
callbacks._trackStructureChanged(track)
def setSwitchHalftoneTranspose(trackId, position:int, transpose:int):
"""Halftone transposition is not flipped. Higher value means higher pitch
Default value is 0.
@ -884,7 +915,7 @@ def setSwitchStepDelay(trackId, position:int, delay:int):
return True
def _setSwitchAugmentationsFactor(trackId, whichPatternsAreStepDelayed):
def _setSwitchAugmentationsFactor(trackId, whichPatternsHaveAugmentationFactor):
"""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)
@ -910,12 +941,14 @@ def setSwitchAugmentationsFactor(trackId, position:int, factor:float):
return True
def _registerHistoryWholeTrackSwitches(track):
"""This is used by insertSilence, clearSwitchGroupModifications etc.
It assumes to run inside the context:
with session.history.sequence(""):
"""This is used by insertSilence, clearSwitchGroupModifications,
exchangeSwitchGroupWithGroupToTheRight etc.
It assumes that it runs inside this context:
with session.history.sequence("asdasd"):
"""
print ("reg all", track)
trackId = id(track)
session.history.register(lambda trId=trackId, v=track.patternLengthMultiplicator: setTrackPatternLengthMultiplicator(trId, v), descriptionString="Pattern Multiplier")
session.history.register(lambda trId=trackId, v=track.structure.copy(): _setTrackStructure(trId, v, "Change Group"), descriptionString="Change Group")
@ -923,7 +956,7 @@ def _registerHistoryWholeTrackSwitches(track):
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")
print ("reg done")
def insertSilence(howMany:int, beforeMeasureNumber:int):
"""Insert empty measures into all tracks.
@ -943,6 +976,7 @@ def insertSilence(howMany:int, beforeMeasureNumber:int):
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
@ -1003,6 +1037,7 @@ def exchangeSwitchGroupWithGroupToTheRight(startMeasureForGroup:int, endMeasureE
#Remember for later testing
lenStructure = len(track.structure)
lenHalfToneTransposed = len(track.whichPatternsAreHalftoneTransposed.keys())
lenScaleTransposed = len(track.whichPatternsAreScaleTransposed.keys())
lenStepDelayed = len(track.whichPatternsAreStepDelayed.keys())
lenAugmentedFactor = len(track.whichPatternsHaveAugmentationFactor.keys())
@ -1050,8 +1085,8 @@ def exchangeSwitchGroupWithGroupToTheRight(startMeasureForGroup:int, endMeasureE
track.structure.add(sw-groupSize)
for stPos, stVal in tempScaleTransposed.items():
track.whichPatternsAreScaleTransposed[stPos-groupSize] = stVal
for htPos, htVal in tempScaleTransposed.items():
track.whichPatternsAreHalftoneTransposed[hPos-groupSize] = htVal
for htPos, htVal in tempHalfToneTransposed.items():
track.whichPatternsAreHalftoneTransposed[htPos-groupSize] = htVal
for sDPos, sDVal in tempStepDelayed.items():
track.whichPatternsAreStepDelayed[sDPos-groupSize] = sDVal
for aFPos, aFVal in tempAugmentedFactor.items():
@ -1070,7 +1105,6 @@ def exchangeSwitchGroupWithGroupToTheRight(startMeasureForGroup:int, endMeasureE
session.data.buildAllTracks()
updatePlayback()
def clearSwitchGroupModifications(startMeasureForGroup:int, endMeasureExclusive:int):
"""startMeasureForGroup and endMeasureExclusive are in the global, un-multiplied measure counting
format."""

1
engine/pattern.py

@ -343,7 +343,6 @@ class Pattern(object):
shuffle = int(self.parentTrack.parentData.swing * whatTypeOfUnit)
#If we diminished and want to repeat the sub-pattern, create virtual extra notes.
self.parentTrack.repeatDiminishedPatternInItself = True
virtualNotes = []
inverseAugmentFactor = round(0.5 + 1 / augmentationFactor)
if self.parentTrack.repeatDiminishedPatternInItself and augmentationFactor < 1:

10
engine/track.py

@ -94,6 +94,7 @@ class Track(object): #injection at the bottom of this file!
This is called after every small change.
"""
if self.group:
#We still have an inactive sequencerinterface but instead we use the group ones as subtrack.
ourCalfboxTrack = self.parentData.groups[self.group]._subtracks[id(self)].calfboxSubTrack
@ -113,10 +114,11 @@ class Track(object): #injection at the bottom of this file!
#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
#TODO: Yes, this happened with undo. every {comprehension} had a "and if k in self.structure" at the end. We took that out without remembering what that was for.
self.whichPatternsAreScaleTransposed = {k:v for k,v in self.whichPatternsAreScaleTransposed.items() if v!=0}
self.whichPatternsAreHalftoneTransposed = {k:v for k,v in self.whichPatternsAreHalftoneTransposed.items() if v!=0}
self.whichPatternsAreStepDelayed = {k:v for k,v in self.whichPatternsAreStepDelayed.items() if v!=0}
self.whichPatternsHaveAugmentationFactor = {k:v for k,v in self.whichPatternsHaveAugmentationFactor.items() if v!=1.0} #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

1
qtgui/mainwindow.py

@ -214,7 +214,6 @@ class MainWindow(TemplateMainWindow):
#self.statusBar().showMessage(QtCore.QCoreApplication.translate("Statusbar", ""))
self.statusBar().showMessage("")
api.session.data.setLanguageForEmptyFile(language = QtCore.QLocale().languageToString(QtCore.QLocale().language())) #TODO: this is a hack because we access the session directly. But this is also a function tied to Qts language string. Two wrongs...
self.start() #This shows the GUI, or not, depends on the NSM gui save setting. We need to call that after the menu, otherwise the about dialog will block and then we get new menu entries, which looks strange.

11
qtgui/songeditor.py

@ -985,11 +985,22 @@ class TrackLabelEditor(QtWidgets.QGraphicsScene):
del item #yes, manual memory management in Python. We need to get rid of anything we want to delete later or Qt will crash because we have a python wrapper without a qt object
#Preare both on and off variants so we have a static string translation
stepDelayOnEntry = (QtCore.QCoreApplication.translate("TrackLabelContext", "Step Delay Wrap-Around: turn on"), lambda: api.changeTrackStepDelayWrapAround(exportDict["id"], True))
stepDelayOffEntry = (QtCore.QCoreApplication.translate("TrackLabelContext", "Step Delay Wrap-Around: turn off"), lambda: api.changeTrackStepDelayWrapAround(exportDict["id"], False))
stepDelayUse = stepDelayOffEntry if exportDict["stepDelayWrapAround"] else stepDelayOnEntry
repeatDiminishedEntryOn = (QtCore.QCoreApplication.translate("TrackLabelContext", "Repeat Diminished Pattern in itself: turn on"), lambda: api.changeTrackRepeatDiminishedPatternInItself(exportDict["id"], True))
repeatDiminishedEntryOff = (QtCore.QCoreApplication.translate("TrackLabelContext", "Repeat Diminished Pattern in itself: turn off"), lambda: api.changeTrackRepeatDiminishedPatternInItself(exportDict["id"], False))
repeatDiminishedEntryUse = repeatDiminishedEntryOff if exportDict["repeatDiminishedPatternInItself"] else repeatDiminishedEntryOn
listOfLabelsAndFunctions = [
(exportDict["sequencerInterface"]["name"], None),
(QtCore.QCoreApplication.translate("TrackLabelContext", "Invert Measures"), lambda: api.trackInvertSwitches(exportDict["id"])),
(QtCore.QCoreApplication.translate("TrackLabelContext", "All Measures On"), lambda: api.trackOnAllSwitches(exportDict["id"])),
(QtCore.QCoreApplication.translate("TrackLabelContext", "All Measures Off"), lambda: api.trackOffAllSwitches(exportDict["id"])),
stepDelayUse,
repeatDiminishedEntryUse,
(QtCore.QCoreApplication.translate("TrackLabelContext", "Clone this Track"), lambda: api.createSiblingTrack(exportDict["id"])),
(QtCore.QCoreApplication.translate("TrackLabelContext", "Delete Track"), lambda: api.deleteTrack(exportDict["id"])),
]

Loading…
Cancel
Save