Browse Source

Remove old code that did apparently nothing than crash. Regressions here we come :)

tags/v1.7
Nils 1 month ago
parent
commit
f559adf181
4 changed files with 94 additions and 100 deletions
  1. +46
    -44
      engine/api.py
  2. +42
    -41
      engine/pattern.py
  3. +0
    -10
      qtgui/mainwindow.py
  4. +6
    -5
      qtgui/pattern_grid.py

+ 46
- 44
engine/api.py View File

@@ -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)

+ 42
- 41
engine/pattern.py View File

@@ -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:

+ 0
- 10
qtgui/mainwindow.py View File

@@ -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

+ 6
- 5
qtgui/pattern_grid.py View File

@@ -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):

Loading…
Cancel
Save