From f559adf1811c9ee49216943c0289a201fe9eb65f Mon Sep 17 00:00:00 2001 From: Nils <> Date: Tue, 11 Aug 2020 19:06:21 +0200 Subject: [PATCH] Remove old code that did apparently nothing than crash. Regressions here we come :) --- engine/api.py | 90 ++++++++++++++++++++++--------------------- engine/pattern.py | 83 +++++++++++++++++++-------------------- qtgui/mainwindow.py | 10 ----- qtgui/pattern_grid.py | 11 +++--- 4 files changed, 94 insertions(+), 100 deletions(-) diff --git a/engine/api.py b/engine/api.py index f17c703..b413bef 100644 --- a/engine/api.py +++ b/engine/api.py @@ -54,16 +54,16 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks ppqn = cbox.Transport.status().pos_ppqn * session.data.subdivisions status = playbackStatus() for func in self.setPlaybackTicks: - func(ppqn, status) + func(ppqn, status) def _loopChanged(self, measurenumber, loopStart, loopEnd): export = measurenumber for func in self.loopChanged: - func(export) + func(export) def _timeSignatureChanged(self): nr = session.data.howManyUnits - typ = session.data.whatTypeOfUnit + typ = session.data.whatTypeOfUnit for func in self.timeSignatureChanged: func(nr, typ) @@ -79,26 +79,26 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks You don't need to redraw anything if you don't want to. One recommendation is to draw every n step a little more important (bigger, different color). where n = subdivions - - We also place JACK BBT via tempoMap here because we need it every time the time sig + + We also place JACK BBT via tempoMap here because we need it every time the time sig changes (which calls _subdivisionsChanged) and if subdivisions change. """ - typ = baseDurationToTraditionalNumber[session.data.whatTypeOfUnit] + typ = baseDurationToTraditionalNumber[session.data.whatTypeOfUnit] nr = session.data.howManyUnits - tradNr = int(nr / session.data.subdivisions) + tradNr = int(nr / session.data.subdivisions) #JACK BBT for Timebase Master. No matter if we are master at the moment or not. - if tradNr == nr / session.data.subdivisions: + if tradNr == nr / session.data.subdivisions: #Easier to read than the else case. Not possible with 9 Steps per Pattern in Groups of 2 because that is a 4.5/4 timesig. - session.data.tempoMap.setTimeSignature(tradNr, typ) + session.data.tempoMap.setTimeSignature(tradNr, typ) else: - #Always possible, compared to first if case. + #Always possible, compared to first if case. session.data.tempoMap.setTimeSignature(nr, typ*session.data.subdivisions) - + export = session.data.subdivisions for func in self.subdivisionsChanged: func(export) callbacks._dataChanged() - + def _scoreChanged(self): """This includes the time signature as well, but is not send on a timesig change. @@ -120,7 +120,7 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks and stepChanged.""" export = track.export() for func in self.exportCacheChanged: - func(export) + func(export) def _patternChanged(self, track): """each track has only one pattern. We can identify the pattern by track and vice versa. @@ -163,7 +163,7 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks def _trackMetaDataChanged(self, track): """a low cost function that should not trigger anything costly to redraw but some text and simple widgets.""" - export = track.export() + export = track.export() for func in self.trackMetaDataChanged: func(export) callbacks._dataChanged() @@ -182,7 +182,7 @@ from template.engine.api import callbacks _templateStartEngine = startEngine def updatePlayback(): - #TODO: use template.sequencer.py internal updates instead + #TODO: use template.sequencer.py internal updates instead cbox.Document.get_song().update_playback() def startEngine(nsmClient): @@ -193,6 +193,7 @@ def startEngine(nsmClient): #Send initial Callbacks to create the first GUI state. #The order of initial callbacks must not change to avoid GUI problems. #For example it is important that the tracks get created first and only then the number of measures + logger.info("Sending initial callbacks to GUI") callbacks._numberOfTracksChanged() callbacks._timeSignatureChanged() callbacks._numberOfMeasuresChanged() @@ -205,13 +206,14 @@ def startEngine(nsmClient): session.data.buildAllTracks(buildSongDuration=True) #will set to max track length, we always have a song duration. updatePlayback() + logger.info("Patroneo api startEngine complete") def _loopOff(): session.data.buildSongDuration() #no parameter removes the loop updatePlayback() session.inLoopMode = None - callbacks._loopChanged(None, None, None) + callbacks._loopChanged(None, None, None) def _loopNow(): now = cbox.Transport.status().pos_ppqn @@ -221,7 +223,7 @@ def _setLoop(loopMeasureAroundPpqn): if loopMeasureAroundPpqn < 0: _loopOff() return - + loopStart, loopEnd = session.data.buildSongDuration(loopMeasureAroundPpqn) updatePlayback() @@ -267,19 +269,19 @@ def set_quarterNotesPerMinute(value): elif value == "on": assert not session.data.tempoMap.isTransportMaster #keep old bpm value. 120 bpm is default. - session.data.tempoMap.isTransportMaster = True #triggers rebuild + session.data.tempoMap.isTransportMaster = True #triggers rebuild else: assert value > 0 session.data.tempoMap.setQuarterNotesPerMinute(value) - session.data.tempoMap.isTransportMaster = True #triggers rebuild + session.data.tempoMap.isTransportMaster = True #triggers rebuild #Does not need track rebuilding updatePlayback() - callbacks._quarterNotesPerMinuteChanged() - + callbacks._quarterNotesPerMinuteChanged() + def set_whatTypeOfUnit(ticks): """Denominator of Time Signature""" - if session.data.whatTypeOfUnit == ticks: return + if session.data.whatTypeOfUnit == ticks: return session.data.whatTypeOfUnit = ticks session.data.buildAllTracks() if session.inLoopMode: @@ -289,14 +291,14 @@ def set_whatTypeOfUnit(ticks): def set_howManyUnits(value): """Numerator of Time Signature""" - if session.data.howManyUnits == value: return + if session.data.howManyUnits == value: return session.data.howManyUnits = value session.data.buildAllTracks() if session.inLoopMode: _loopNow() updatePlayback() callbacks._timeSignatureChanged() - + def set_subdivisions(value): if session.data.subdivisions == value: return @@ -456,18 +458,18 @@ def trackMergeCopyFrom(sourceTrackId, targetTrackId): targetTrack.buildTrack() updatePlayback() callbacks._trackStructureChanged(targetTrack) - + def trackPatternReplaceFrom(sourceTrackId, targetTrackId): if not sourceTrackId == targetTrackId: sourceTrack = session.data.trackById(sourceTrackId) targetTrack = session.data.trackById(targetTrackId) - + copyPattern = sourceTrack.pattern.copy(newParentTrack = targetTrack) targetTrack.pattern = copyPattern - + targetTrack.buildTrack() updatePlayback() - callbacks._patternChanged(targetTrack) + callbacks._patternChanged(targetTrack) def setSwitchScaleTranspose(trackId, position, transpose): """Scale transposition is flipped. lower value means higher pitch""" @@ -498,32 +500,32 @@ def insertSilence(howMany, beforeMeasureNumber): updatePlayback() def duplicateSwitchGroup(startMeasureForGroup:int, endMeasureExclusive:int): - groupSize = endMeasureExclusive-startMeasureForGroup - insertSilence(groupSize, endMeasureExclusive) - + groupSize = endMeasureExclusive-startMeasureForGroup + insertSilence(groupSize, endMeasureExclusive) + for track in session.data.tracks: for switch in range(startMeasureForGroup+groupSize, endMeasureExclusive+groupSize): #One group after the given one. if switch-groupSize in track.structure: - track.structure.add(switch) + track.structure.add(switch) if switch-groupSize in track.whichPatternsAreScaleTransposed: track.whichPatternsAreScaleTransposed[switch] = track.whichPatternsAreScaleTransposed[switch-groupSize] if switch-groupSize in track.whichPatternsAreHalftoneTransposed: - track.whichPatternsAreHalftoneTransposed[switch] = track.whichPatternsAreHalftoneTransposed[switch-groupSize] + track.whichPatternsAreHalftoneTransposed[switch] = track.whichPatternsAreHalftoneTransposed[switch-groupSize] callbacks._trackStructureChanged(track) session.data.buildAllTracks() updatePlayback() -def clearSwitchGroupTranspositions(startMeasureForGroup:int, endMeasureExclusive:int): +def clearSwitchGroupTranspositions(startMeasureForGroup:int, endMeasureExclusive:int): for track in session.data.tracks: for switch in range(startMeasureForGroup, endMeasureExclusive): if switch in track.whichPatternsAreScaleTransposed: del track.whichPatternsAreScaleTransposed[switch] if switch in track.whichPatternsAreHalftoneTransposed: - del track.whichPatternsAreHalftoneTransposed[switch] + del track.whichPatternsAreHalftoneTransposed[switch] callbacks._trackStructureChanged(track) session.data.buildAllTracks() - updatePlayback() - + updatePlayback() + def deleteSwitches(howMany, fromMeasureNumber): for track in session.data.tracks: new_structure = set() @@ -650,35 +652,35 @@ def patternOffAllSteps(trackId): track.buildTrack() updatePlayback() callbacks._patternChanged(track) - + def patternInvertRow(trackId, pitchindex): - """Pitchindex is the row""" + """Pitchindex is the row""" track = session.data.trackById(trackId) track.pattern.invertRow(pitchindex) track.pattern.buildExportCache() track.buildTrack() updatePlayback() - callbacks._patternChanged(track) + callbacks._patternChanged(track) def patternClearRow(trackId, pitchindex): """Pitchindex is the row. - Index is the column""" + Index is the column""" track = session.data.trackById(trackId) track.pattern.clearRow(pitchindex) track.pattern.buildExportCache() track.buildTrack() updatePlayback() - callbacks._patternChanged(track) + callbacks._patternChanged(track) def patternRowRepeatFromStep(trackId, pitchindex, index): """Pitchindex is the row. - Index is the column""" + Index is the column""" track = session.data.trackById(trackId) track.pattern.repeatFromStep(pitchindex, index) track.pattern.buildExportCache() track.buildTrack() updatePlayback() - callbacks._patternChanged(track) + callbacks._patternChanged(track) def patternRowChangeVelocity(trackId, pitchindex, delta): track = session.data.trackById(trackId) diff --git a/engine/pattern.py b/engine/pattern.py index 34ce8dd..742f684 100644 --- a/engine/pattern.py +++ b/engine/pattern.py @@ -36,8 +36,8 @@ class Pattern(object): """A pattern can be in only one track. In fact having it as its own object is only for code readability - A pattern is an unordered list of dicts. - Each dict is an step, or a note. + A pattern is an unordered list of dicts. + Each dict is an step, or a note. {"index": from 0 to parentTrack.parentData.howManyUnits, "factor": float, "pitch": int 0-7, @@ -61,6 +61,7 @@ class Pattern(object): self.scale = scale if scale else (72, 71, 69, 67, 65, 64, 62, 60) #Scale needs to be set first because on init/load data already depends on it, at least the default scale. The scale is part of the track meta callback. self.data = data if data else list() #For content see docstring. this cannot be the default parameter because we would set the same list for all instances. self.simpleNoteNames = simpleNoteNames if simpleNoteNames else self.parentTrack.parentData.lastUsedNotenames[:] #This is mostly for the GUI or other kinds of representation instead midi notes + assert self.simpleNoteNames self._processAfterInit() def _prepareBeforeInit(self): @@ -77,11 +78,11 @@ class Pattern(object): self._builtPatternCache = {} #holds a ready cbox pattern for a clip as value. Key is a tuple of hashable parameters. see self.buildPattern def copy(self, newParentTrack): - """Return an independent copy of this pattern""" + """Return an independent copy of this pattern""" data = [note.copy() for note in self.data] #list of mutable dicts. Dicts have only primitve data types inside - scale = self.scale #it is immutable so there is no risk of changing it in place for both patterns at once - simpleNoteNames = self.simpleNoteNames[:] #this mutable list always gets replaced completely by setting a new list, but we don't want to take any chances and create a slice copy. - result = Pattern(newParentTrack, data, scale, simpleNoteNames) + scale = self.scale #it is immutable so there is no risk of changing it in place for both patterns at once + simpleNoteNames = self.simpleNoteNames[:] #this mutable list always gets replaced completely by setting a new list, but we don't want to take any chances and create a slice copy. + result = Pattern(newParentTrack, data, scale, simpleNoteNames) return result @property @@ -137,80 +138,80 @@ class Pattern(object): def _putRow(self, pitchindex, rowAsListOfSteps): """Replace a row with the given one""" self.clearRow(pitchindex) - self.data.extend(rowAsListOfSteps) + self.data.extend(rowAsListOfSteps) def clearRow(self, pitchindex): - """pure convenience. This could be done with + """pure convenience. This could be done with repeatFromStep on the first empty step""" - existingSteps = self.getRow(pitchindex) + existingSteps = self.getRow(pitchindex) for step in existingSteps: self.data.remove(step) - + def _rowAsBooleans(self, pitchindex): """Existence or not""" - existingSteps = self.getRow(pitchindex) - existingIndices = set(s["index"] for s in existingSteps) - + existingSteps = self.getRow(pitchindex) + existingIndices = set(s["index"] for s in existingSteps) + result = [False] * self.parentTrack.parentData.howManyUnits for i in existingIndices: - result[i] = True - return result + result[i] = True + return result def _getRowWithNoneFillers(self, pitchindex): - existingSteps = self.getRow(pitchindex) + existingSteps = self.getRow(pitchindex) result = [None] * self.parentTrack.parentData.howManyUnits for st in existingSteps: result[st["index"]] = st - return result + return result def _old_repeatFromStep(self, pitchindex, stepIndex): - """Includes the given step. + """Includes the given step. Uses average velocities """ - vel = self.averageVelocity + vel = self.averageVelocity rowAsBools = self._rowAsBooleans(pitchindex) - toRepeatChunk = rowAsBools[:stepIndex+1] - numberOfRepeats, rest = divmod(self.parentTrack.parentData.howManyUnits, stepIndex+1) + toRepeatChunk = rowAsBools[:stepIndex+1] + numberOfRepeats, rest = divmod(self.parentTrack.parentData.howManyUnits, stepIndex+1) index = 0 - - newRow = [] + + newRow = [] for i in range(numberOfRepeats): for b in toRepeatChunk: if b: - newRow.append({"index":index, "factor": 1, "pitch": pitchindex, "velocity":vel}) - index += 1 - + newRow.append({"index":index, "factor": 1, "pitch": pitchindex, "velocity":vel}) + index += 1 + self._putRow(pitchindex, newRow) - + def repeatFromStep(self, pitchindex, stepIndex): - """Includes the given step. + """Includes the given step. Uses original velocities and scale factors - """ + """ originalRow = self._getRowWithNoneFillers(pitchindex) - toRepeatChunk = originalRow[:stepIndex+1] - numberOfRepeats, rest = divmod(self.parentTrack.parentData.howManyUnits, stepIndex+1) + toRepeatChunk = originalRow[:stepIndex+1] + numberOfRepeats, rest = divmod(self.parentTrack.parentData.howManyUnits, stepIndex+1) newRow = [] for i in range(numberOfRepeats): for st in toRepeatChunk: if st: - s = st.copy() + s = st.copy() s["index"] = len(toRepeatChunk)*i + s["index"] newRow.append(s) - self._putRow(pitchindex, newRow) + self._putRow(pitchindex, newRow) - def invertRow(self, pitchindex): - vel = self.averageVelocity - existingSteps = self.getRow(pitchindex) + def invertRow(self, pitchindex): + vel = self.averageVelocity + existingSteps = self.getRow(pitchindex) existingIndices = set(s["index"] for s in existingSteps) - + for step in existingSteps: - self.data.remove(step) - + self.data.remove(step) + for index in range(self.parentTrack.parentData.howManyUnits): if not index in existingIndices: - self.data.append({"index":index, "factor": 1, "pitch": pitchindex, "velocity":vel}) - + self.data.append({"index":index, "factor": 1, "pitch": pitchindex, "velocity":vel}) + def stepByIndexAndPitch(self, index, pitch): for d in self.data: diff --git a/qtgui/mainwindow.py b/qtgui/mainwindow.py index 3f507b2..fd2843d 100644 --- a/qtgui/mainwindow.py +++ b/qtgui/mainwindow.py @@ -157,9 +157,6 @@ class MainWindow(TemplateMainWindow): #Toolbar, which needs the widgets above already established self._populateToolbar() - #MainWindow Callbacks - api.callbacks.numberOfTracksChanged.append(self.callback_numberOfTracksChanged) - self.currentTrackId = None #this is purely a GUI construct. the engine does not know a current track. On startup there is no active track 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. #There is always a track. Forcing that to be active is better than having to hide all the pattern widgets, or to disable them. @@ -174,13 +171,6 @@ class MainWindow(TemplateMainWindow): self.nsmClient.announceSaveStatus(isClean = True) - def callback_numberOfTracksChanged(self, exportDictList): - """We need to find out of the current track was the deleted one or if a new track got added - automatically.""" - #if self.programStarted and len(exportDictList) == 1: - if len(exportDictList) == 1: - self.chooseCurrentTrack(exportDictList[0]) - def chooseCurrentTrack(self, exportDict): """This is in mainWindow because we need access to different sections of the program. newCurrentTrack is a backend track ID diff --git a/qtgui/pattern_grid.py b/qtgui/pattern_grid.py index 21d8a68..b8ee50c 100644 --- a/qtgui/pattern_grid.py +++ b/qtgui/pattern_grid.py @@ -614,7 +614,7 @@ class Scale(QtWidgets.QGraphicsRectItem): super().__init__(0,0,0,0) self.parentScene = parentScene self.pitchWidgets = [] #sorted from top to bottom in Step Rect and scene coordinates - self.simpleNoteNames = [] #list of 128 notes. use index with note name. Can be changed at runtime. + self.simpleNoteNames = None #list of 128 notes. use index with note name. Can be changed at runtime. Never empty. api.callbacks.trackMetaDataChanged.append(self.callback_trackMetaDataChanged) self.buildScale() #also sets the positions of the buttons above @@ -802,12 +802,13 @@ class PitchWidget(QtWidgets.QGraphicsProxyWidget): def midiToNotename(self, midipitch): - assert self.parentItem.simpleNoteNames, self.parentItem.simpleNoteNames + assert self.parentItem.simpleNoteNames, (self.parentItem, self.parentItem.simpleNoteNames) try: return self.parentItem.simpleNoteNames[midipitch] #includes octave names - except IndexError: - print (midipitch) - print (self.parentItem.simpleNoteNames) + except IndexError as e: + print (e) + print ("Midipitch:", midipitch) + print ("Simple Notename:", self.parentItem.simpleNoteNames) exit() def spinBoxValueChanged(self):