diff --git a/engine/api.py b/engine/api.py
index 305afa0..409dff1 100644
--- a/engine/api.py
+++ b/engine/api.py
@@ -58,7 +58,6 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks
#self.playbackStart = []
#self.playbackStop = []
- self.playbackSpeedChanged = []
self._cachedTickIndex = -1
self.updateGraphTrackCC = []
@@ -68,9 +67,9 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks
self.updateTempoTrack = []
self.updateTempoTrackBlocks = []
self.updateTempoTrackMeta = []
+ self.tempoScalingChanged = []
- self.prevailingBaseDurationChanged = []
- self.metronomeChanged = []
+ self.prevailingBaseDurationChanged = []
#self.recordingStreamNoteOn = []
#self.recordingStreamNoteOff = []
@@ -115,6 +114,8 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks
changedTracks, currentItem = session.data.currentContentLinkAndItsBlocksInAllTracks()
for track in changedTracks:
self._updateTrack(id(track))
+ if session.data.currentMetronomeTrack is track:
+ setMetronome(track.asMetronomeData, label=track.name) #template api
def _updateTrack(self, trId):
"""The most important function. Create a static representation of the music data which
@@ -178,7 +179,8 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks
def _tracksChanged(self):
"""Track deleted, added or moved. Or toggled double/non-double.
This callback is relatively cheap because it does not generate
- any item data."""
+ any item data."""
+ #TODO: does NOT call template.api._numberOfTracksChanged
session.data.updateJackMetadataSorting()
if self.tracksChanged:
@@ -226,6 +228,10 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks
#TODO: but this also leads to centerOn cursor in the gui after every mouse click in the conductor.
#self._setCursor(destroySelection = False)
+ def _tempoScalingChanged(self, newValue):
+ for func in self.tempoScalingChanged:
+ func(newValue)
+
def _playbackStart(self):
for func in self.playbackStart:
func()
@@ -234,9 +240,6 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks
for func in self.playbackStop:
func()
- def _playbackSpeedChanged(self, newValue):
- for func in self.playbackSpeedChanged:
- func(newValue)
def _prevailingBaseDurationChanged(self, newPrevailingBaseDuration):
for func in self.prevailingBaseDurationChanged:
@@ -252,18 +255,6 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks
for func in self.historyChanged:
func(undoHistory, redoHistory)
- def _metronomeChanged(self):
- """returns a dictionary with meta data such as the mute-state and the track name"""
- #Check if the actual track exists. This handles track delete of any kind.
- #session.data.metronome.currentMetronomeTrack is not a calf box track but our Laborejo track that HOLDS the current cbox metronome track
- return #TODO
- if not session.data.metronome.currentMetronomeTrack or not (session.data.metronome.currentMetronomeTrack in session.data.tracks or session.data.metronome.currentMetronomeTrack in session.data.hiddenTracks):
- session.data.metronome.currentMetronomeTrack = None
- session.data.metronome.useTrack(session.data.currentTrack())
-
- for func in self.metronomeChanged:
- func(session.data.metronome.staticRepresentation())
-
def _recordingStreamNoteOn(self, liveChord):
"""One dict at a time"""
trId = id(session.data.currentTrack())
@@ -290,7 +281,7 @@ def startEngine(nsmClient):
_templateStartEngine(nsmClient)
#Send initial Data etc.
- session.data.tempoMap.isTransportMaster = True #always true for Laborejo.
+ session.data.tempoMap.isTransportMaster = True #always true for Laborejo.
callbacks._tracksChanged() # This creates the frontend/GUI tracks with access through track ids. From now on we can send the GUI a trId and it knows which track needs change.
for trId in session.data.listOfTrackIds():
callbacks._updateTrack(trId) #create content: music items
@@ -302,6 +293,8 @@ def startEngine(nsmClient):
for track in session.data.hiddenTracks: #After loading a file some tracks could be hidden. We want midi for them as well.
track.staticRepresentation() #that generates the calfbox data as a side effect. discard the other data.
+ useCurrentTrackAsMetronome()
+
callbacks._setCursor()
global laborejoEngineStarted #makes for a convenient check. stepMidiInput uses it, which needs to know that the gui already started the api.
@@ -414,21 +407,6 @@ def _updateCallbackAllTracks():
updateCallbackAllTracks = _updateCallbackAllTracks
-def useCurrentTrackAsMetronome():
- track = session.data.currentTrack()
- session.data.metronome.useTrack(track)
- callbacks._metronomeChanged() #updates playback as well
-
-def enableMetronome(value):
- session.data.metronome.enabled = value #has side effects
- callbacks._metronomeChanged() #updates playback as well
-
-def isMetronomeEnabled():
- return session.data.metronome.enabled
-
-def toggleMetronome():
- enableMetronome(not session.data.metronome.enabled) #handles callback etc.
-
def _changeBlockAndItemOrder(dictWithTrackIDsAndDataLists):
"""A helper function for deleteSelection, paste and other...
This makes it possible to undo/redo properly. It registers
@@ -472,10 +450,10 @@ def copyObjects(): #ctrl+c
#The score doesn't change at all. No callback.
#no undo.
-def pasteObjects(customBuffer = None, keepCursorState = False, overwriteSelection = True): #ctrl+v
+def pasteObjects(customBuffer = None, updateCursor = True, overwriteSelection = True): #ctrl+v
"""api.duplicate overrides default paste behaviour by providing its own copyBuffer
and not destroying the selection/keep the cursor at its origin position
- """
+ """
dataBefore = session.data.getBlockAndItemOrder()
moveFunction = _createLambdaMoveToForCurrentPosition()
listOfChangedTrackIDs = session.data.pasteObjects(customBuffer, overwriteSelection)
@@ -488,31 +466,31 @@ def pasteObjects(customBuffer = None, keepCursorState = False, overwriteSelectio
#Make changes visible in the GUI
for trackId in listOfChangedTrackIDs:
callbacks._updateTrack(trackId)
-
- if keepCursorState:
- goTo()
- callbacks._setCursor(destroySelection = False)
- else:
+
+ if updateCursor:
callbacks._setCursor()
def duplicate(): #ctrl+d
- """Duplicate a single object and put it right of the original. Duplicate the entire selection
- and put the copy right of the last selected note.
- Basically a special case of copy and paste that does not touch the clipboard."""
+ """Duplicate a single object and put it right of the original. The cursor moves with it
+ to enable follow up insertion.
+
+ Duplicate the entire selection and put the copy right of the last selected note.
+ The cursor moves to selection start. deal with it.
+
+ Basically a special case of copy and paste that does not touch the clipboard."""
if session.data.cursorWhenSelectionStarted:
- #startPos = session.data.where() #we cannot use where() since we don't know if a content linked block before our current position gets new items in the duplication process
- #TODO: when duplicating with content links or at least inside a content link the selection gets lost completely. Maybe this cannot be avoided, but review that in the future.
-
customBuffer = session.data.copyObjects(writeInSessionBuffer = False)
- if customBuffer:
- session.data.goToSelectionStart()
- pasteObjects(customBuffer = customBuffer, keepCursorState = True, overwriteSelection = False)
- else:
+ if customBuffer: #success
+ session.data.goToSelectionStart()
+ pos = session.data.where() #where even keeps the local position if a content linked block inserts items before our position
+ pasteObjects(customBuffer = customBuffer, updateCursor = False, overwriteSelection = False)
+ session.data.goTo(*pos)
+ callbacks._setCursor(destroySelection=False)
+ else:
item = session.data.currentItem()
if item:
- insertItem(item.copy())
-
-
+ insertItem(item.copy())
+ callbacks._setCursor()
#Score
def transposeScore(rootPitch, targetPitch):
"""Based on automatic transpose. The interval is caculated from two pitches.
@@ -521,6 +499,11 @@ def transposeScore(rootPitch, targetPitch):
session.history.register(lambda r=rootPitch,t=targetPitch: transposeScore(t,r), descriptionString="transpose score")
_updateCallbackAllTracks()
+def useCurrentTrackAsMetronome():
+ """This is called once after loading/creating a session in startEngine"""
+ session.data.currentMetronomeTrack = session.data.currentTrack()
+ setMetronome(session.data.currentMetronomeTrack.asMetronomeData, label=session.data.currentMetronomeTrack.name) #template api. has callbacks
+
#Tracks
def insertTrack(atIndex, trackObject):
moveFunction = _createLambdaMoveToForCurrentPosition()
@@ -540,6 +523,7 @@ def newEmptyTrack():
newTrack = Track(session.data)
insertTrack(newIndex, newTrack) #handles callbacks and undo
return (id(newTrack))
+
def deleteTrack(trId):
"""Can not delete hidden tracks because these don't implement undo.
@@ -2135,6 +2119,15 @@ def insertTempoChangeDuringDuration(percentageUnitsPerMinuteAsFloat):
session.history.clear() #TODO: This registers two times undo
+def currentTempoScalingFactor():
+ return session.data.tempoTrack.factor
+
+def changeTempoScaling(factor:float):
+ """The factor is always a factor from x1, not from the previous
+ value"""
+ session.data.tempoTrack.setFactor(float(factor))
+ callbacks._tempoScalingChanged(session.data.tempoTrack.factor)
+
#Toolbox
#High Level commands
diff --git a/engine/block.py b/engine/block.py
index 62a63d6..d2e5033 100644
--- a/engine/block.py
+++ b/engine/block.py
@@ -151,8 +151,9 @@ class Block(object):
for item in self.data:
copyItem = item.copy()
new.data.append(copyItem)
-
- copyItem.parentBlocks.add(new) #parentBlock was empty until now
+
+ assert not copyItem.parentBlocks #parentBlock was empty until now
+ copyItem.parentBlocks.add(new)
if self in copyItem.parentBlocks: #TODO: investigate
copyItem.parentBlocks.remove(self)
@@ -301,7 +302,7 @@ class Block(object):
def insert(self, item):
self.data.insert(self.localCursorIndex, item) #we do not need to check if appending or not. list.insert appends if the index is higher then len()
#self.localCursorIndex += 1 #we don't need to go right here because track.insert() is calling its own right() directly after insert, which triggers block.right()
- item.parentBlocks.add(self)
+ item.parentBlocks.add(self)
def delete(self):
"""The commented out is the immediate garbage collector which
diff --git a/engine/config.py b/engine/config.py
index ac9eba6..6715288 100644
--- a/engine/config.py
+++ b/engine/config.py
@@ -36,7 +36,7 @@ METADATA={
#How many audio outputs do you want? must be pairs. These are just unconnected jack outputs
#that need to be connected internally to instrument outputs like fluidsynth
- "cboxOutputs" : 2 * 0,
+ "cboxOutputs" : 2 * 1, #metronome
#Various strings for the README
#Extra whitespace will be stripped so we don't need to worry about docstring indentation
diff --git a/engine/graphtracks.py b/engine/graphtracks.py
index e906e42..71676b5 100644
--- a/engine/graphtracks.py
+++ b/engine/graphtracks.py
@@ -1313,6 +1313,13 @@ class TempoTrack(GraphTrackCC):
assert self.blocks
+ @property
+ def factor(self):
+ return self.parentData.tempoMap.factor
+
+ def setFactor(self, factor:float):
+ self.parentData.tempoMap.setFactor(factor)
+
def lilypond(self):
"""Based on the static export"""
def _ly(tempoItem, nextTempoItem):
diff --git a/engine/items.py b/engine/items.py
index c5601c1..3246a55 100644
--- a/engine/items.py
+++ b/engine/items.py
@@ -744,8 +744,13 @@ class Item(object):
@property
- def parentTracks(self):
- return [block.parentTrack for block in self.parentBlocks]
+ def parentTracks(self):
+ #return (block.parentTrack for block in self.parentBlocks[0])
+ return (block.parentTrack for block in list(self.parentBlocks)[0].linkedContentBlocksInScore())
+
+ @property
+ def parentTrackIds(self):
+ return (id(tr) for tr in self.parentTracks)
def _copy(self):
"""return an independent copy of self"""
@@ -757,8 +762,8 @@ class Item(object):
return new
def copyParentBlocks(self, oldItem):
- """Move the oldItems parentBlocks to the new item"""
- self.parentBlocks = oldItem.parentBlocks
+ """Move the oldItems parentBlocks to the new item"""
+ #self.parentBlocks = oldItem.parentBlocks #TODO. We took that out when pasting after deleting a track and recreating failed. Why do we to copy the parentBlocks when a parentBlock is added during block.insert anyway? Wild guess: we don't.
def logicalDuration(self):
return 0
diff --git a/engine/main.py b/engine/main.py
index ffe0e0d..6042b14 100644
--- a/engine/main.py
+++ b/engine/main.py
@@ -23,12 +23,12 @@ along with this program. If not, see .
import logging; logging.info("import {}".format(__file__))
#Standar Library
+import sys
#3rd Party
#Template
import template.engine.sequencer
-from template.engine.metronome import Metronome
#Our modules
from .block import Block
@@ -56,11 +56,12 @@ class Data(template.engine.sequencer.Score):
self.cursorWhenSelectionStarted = None #A cursor dict, from self.cursorExport(). Is None when there is no selection. Can be used for "if selection:" questions. Gets changed quite often.
self.copyObjectsBuffer = [] #for copy and paste. obviously empty after load file. Also not saved.
self.cachedTrackDurations = {} #updated after every track export
- self.metronome = Metronome(parentData=self) #Purely dynamic structure. No save/load. No undo/redo
+ self.currentMetronomeTrack = None #A Laborejo Track, indepedent of currentTrack. The metronome is in self.metronome, set by the template Score.
def duration(self):
"""Return the duration of the whole score, in ticks"""
- #TODO: use cached duration? How often is this used?
+ #TODO: use cached duration? How often is this used? Pretty often. 3 Times for a single track note update.
+ #TODO: Measure before trying to improve performance.
result = []
for track in self.tracks:
result.append(track.duration())
@@ -506,7 +507,7 @@ class Data(template.engine.sequencer.Score):
result = []
if validSelection:
for track in selectedTracksAndItems:
- result.append([item.copy() for item, cachedTrackState in track])
+ result.append([item.copy() for item, cachedTrackState in track])
if writeInSessionBuffer:
self.copyObjectsBuffer = result
return result
@@ -580,10 +581,11 @@ class Data(template.engine.sequencer.Score):
if self.cursorWhenSelectionStarted and overwriteSelection:
listOfChangedTrackIdsFromDelete = self.deleteSelection()
+ startPosition = self.where()
+ startItem = self.currentItem()
#TODO: check if the cursor is still in the correct position after delete selection
listOfChangedTrackIds = set()
-
for number, track in enumerate(workBuffer):
curTrack = self.currentTrack()
#Here comes an additional condition. Most of the time pastes are single-track. If so, or we simply are in the starting track, we want to paste at the actual position, not the tickindex. This circumvents problems with block boundaries and zero-duration items.
@@ -597,8 +599,8 @@ class Data(template.engine.sequencer.Score):
for item in track:
newItem = item.copy()
curTrack.insert(newItem) #eventhough copyObjectsBuffer is already a copy of the original notes we don't want every paste to be the same instances.
- for parentBlock in newItem.parentBlocks: #TODO: just be naive and add them all for now. Performance can be improved later.
- listOfChangedTrackIds.add(id(parentBlock.parentTrack))
+ #newItem has now a parentBlock and a parentTrack. We add this parent track to the list of changed Tracks
+ listOfChangedTrackIds.update(newItem.parentTrackIds) #TODO: profiling. All items in a block have the same parentTrack. This is highly redundant BUT can be different for items which originated from block boundaries
if not number+1 == len(workBuffer): #enumerate from 0, len from 1.
#we have to prevent the track going down one too far in the last step and messing with the tick index though.
self.trackDown()
@@ -609,10 +611,13 @@ class Data(template.engine.sequencer.Score):
self.trackUp()
assert self.trackIndex == trackIndexStart == startPosition[0]
assert self.currentTrack().state.tickindex == finalTickIndex #this is not enough to pinpoint the real location.
- self.goTo(trackIndex=startPosition[0], blockindex=startPosition[1], localCursorIndexInBlock=0)
+
+ #Return to the item where pasting starting. Pasting counts as insert, and insert sticks to the item right of cursor.
+ #Therefore we go to the starting track and block, but not to the starting localCursorIndexInBlock. Instead we search for the specific item in a second step
+ self.goTo(trackIndex=startPosition[0], blockindex=startPosition[1], localCursorIndexInBlock=0)
self.goToItemInCurrentBlock(startItem) #we actually want to be at the same item where we started, not just the tick index which gets confused with block boundaries and zero duration items
- try: #overwrite?
+ try: #overwrite? #TODO: What is that? assert with AssertionError pass?
assert listOfChangedTrackIds == listOfChangedTrackIdsFromDelete
except AssertionError:
pass
@@ -811,12 +816,14 @@ class Data(template.engine.sequencer.Score):
raise ValueError("graphItem with this id not in any track")
def goTo(self, trackIndex, blockindex, localCursorIndexInBlock):
+ """Handles even shifting positions in a content link situation"""
self.trackIndex = trackIndex
self.currentTrack().toBlockAndLocalCursorIndex(blockindex, localCursorIndexInBlock)
assert self.where() == (trackIndex, blockindex, localCursorIndexInBlock)
def where(self):
- """return the data needed by self.goto"""
+ """return the data needed by self.goto
+ where even keeps the local position if a content linked block inserts items before our position """
return self.trackIndex, self.currentTrack().state.blockindex, self.currentTrack().currentBlock().localCursorIndex
def goToItemInCurrentBlock(self, itemInstance):
diff --git a/engine/resources/metronome.sfz b/engine/resources/metronome.sfz
new file mode 100644
index 0000000..4a854a6
--- /dev/null
+++ b/engine/resources/metronome.sfz
@@ -0,0 +1,2 @@
+ key=77 sample=samples/normal.wav count=1 //normal metronome tick
+ key=76 sample=samples/stressed.wav count=1 //stressed metronome tick
diff --git a/engine/resources/normal.wav b/engine/resources/samples/normal.wav
similarity index 100%
rename from engine/resources/normal.wav
rename to engine/resources/samples/normal.wav
diff --git a/engine/resources/stressed.wav b/engine/resources/samples/stressed.wav
similarity index 100%
rename from engine/resources/stressed.wav
rename to engine/resources/samples/stressed.wav
diff --git a/engine/track.py b/engine/track.py
index 7bc62e6..cbb0e40 100644
--- a/engine/track.py
+++ b/engine/track.py
@@ -369,8 +369,8 @@ class TrackState(object):
class Track(object):
allTracks = WeakValueDictionary() #key is the trackId, value is the weak reference to the Track. Deleted tracks (from the current session) and hidden tracks are in here as well.
- def __repr__(self) -> str:
- return f"Laborejo Track: {self.sequencerInterface.name}"
+ #def __repr__(self) -> str:
+ # return f"Laborejo Track: {self.sequencerInterface.name}"
def __init__(self, parentData, name=None):
self.parentData = parentData
@@ -395,6 +395,8 @@ class Track(object):
#The instrument names are also handled by the trackState so that the cursor knows about instrument changes
self.initialInstrumentName = "" #different than the track name. e.g. "Violin"
self.initialShortInstrumentName = "" # e.g. "vl"
+
+ self.asMetronomeData = None #This track as metronome version. Is always up to date through export.
self._processAfterInit()
@@ -1060,10 +1062,10 @@ class Track(object):
#########
#Metronome start
- #The metronome cannot be calculated by simply looking at metricalInstructions. We need to look at the barlines. Two instructions in a row at the same tick are wrong, but technically possible. These are not two full measures of metronome
- #TODO: metronome activate
- #if self is self.parentData.currentMetronomeTrack:
- # self.parentData.metronome.generate(((pos, m.isMetrical, m.treeOfInstructions) for pos, m in barlines.items()))
+ #The metronome cannot be calculated by simply looking at metricalInstructions.
+ #We need to look at the barlines. Two instructions in a row at the same tick are wrong,
+ #but technically possible. These are not two full measures of metronome
+ self.asMetronomeData = tuple((pos, m.isMetrical, m.treeOfInstructions) for pos, m in barlines.items())
#Metronome end. Nothing below is connected with the metronome subtrack.
#Calculate the beam positions for the static groups.
@@ -1152,10 +1154,5 @@ class Track(object):
self.toPosition(originalPosition, strict = False) #has head() in it
return result
-
-
-
-
-
#Dependency Injections.
template.engine.sequencer.Score.TrackClass = Track #Score will look for Track in its module.
diff --git a/qtgui/menu.py b/qtgui/menu.py
index c380408..ba26262 100644
--- a/qtgui/menu.py
+++ b/qtgui/menu.py
@@ -539,10 +539,11 @@ class ToolBarMetronome(QtWidgets.QCheckBox):
return False
def updateFromCallback(self, exportDict):
+ """Gets the metronome dict"""
self.blockSignals(True)
self.setStatus()
assert self.isChecked() == exportDict["enabled"], (self.isChecked(), exportDict["enabled"])
- self.setText("Metronome: " + exportDict["trackName"])
+ self.setText("Metronome: " + exportDict["label"])
self.blockSignals(False)
def changed(self, value):
@@ -554,10 +555,10 @@ class ToolBarPlaybackSpeed(QtWidgets.QDoubleSpinBox):
super().__init__()
self.setRange(0.1,3.0)
self.setDecimals(1)
- self.setValue(1.0)
+ self.setValue(api.currentTempoScalingFactor())
self.setSingleStep(0.1)
self.setPrefix("Tempo ×") #yes, this is the unicode multiplication sign × and not an x
- api.callbacks.playbackSpeedChanged.append(self.updateFromCallback)
+ api.callbacks.tempoScalingChanged.append(self.updateFromCallback)
self.valueChanged.connect(self.changed)
def updateFromCallback(self, newValue):
@@ -566,5 +567,4 @@ class ToolBarPlaybackSpeed(QtWidgets.QDoubleSpinBox):
self.blockSignals(False)
def changed(self):
- api.playbackChangeSpeed(self.value())
- constantsAndConfigs.ccViewValue = self.value() #this sends a call back to all CC spinboxes in the whole program
+ api.changeTempoScaling(self.value())
diff --git a/qtgui/scoreview.py b/qtgui/scoreview.py
index 1cc58e3..b2972c7 100644
--- a/qtgui/scoreview.py
+++ b/qtgui/scoreview.py
@@ -23,7 +23,7 @@ along with this program. If not, see .
#Third Party
-from PyQt5 import QtCore, QtGui, QtWidgets
+from PyQt5 import QtCore, QtGui, QtWidgets, QtOpenGL
#from PyQt5 import QtOpenGL
#Template Modules
@@ -39,22 +39,21 @@ class ScoreView(QtWidgets.QGraphicsView):
def __init__(self, mainWindow):
super().__init__()
self.mainWindow = mainWindow
-
-
- #TODO: Measure OpenGL performance impact. Make sure fallback works
- """
- viewport = QtOpenGL.QGLWidget(QtOpenGL.QGLFormat(QtOpenGL.QGL.SampleBuffers))
- viewport.format().setSwapInterval(0) #disable VSync.
- viewport.setAutoFillBackground(False)
-
- viewport = QtWidgets.QOpenGLWidget()
- viewportFormat = QtGui.QSurfaceFormat()
- viewportFormat.setSwapInterval(0) #disable VSync
- viewportFormat.setSamples(2**8)
- viewportFormat.setDefaultFormat(viewportFormat)
- viewport.setFormat(viewportFormat)
- self.setViewport(viewport)
- """
+
+ #OpenGL has a huge positive impact on performance
+ if True: #for testing
+ viewport = QtOpenGL.QGLWidget(QtOpenGL.QGLFormat(QtOpenGL.QGL.SampleBuffers))
+ viewport.format().setSwapInterval(0) #disable VSync.
+ viewport.setAutoFillBackground(False)
+
+ viewport = QtWidgets.QOpenGLWidget()
+ viewportFormat = QtGui.QSurfaceFormat()
+ viewportFormat.setSwapInterval(0) #disable VSync
+ #viewportFormat.setSamples(2**8) #By default, the highest number of samples available is used.
+ viewportFormat.setDefaultFormat(viewportFormat)
+ viewport.setFormat(viewportFormat)
+ self.setViewport(viewport)
+
self.setAlignment(QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop)
#self.setDragMode(QtWidgets.QGraphicsView.RubberBandDrag)
diff --git a/qtgui/structures.py b/qtgui/structures.py
index 269f6b0..9a47bd7 100644
--- a/qtgui/structures.py
+++ b/qtgui/structures.py
@@ -22,7 +22,7 @@ along with this program. If not, see .
from math import log
from PyQt5 import QtCore, QtGui, QtWidgets
-from .items import staticItem2Item, GuiTieCurveGraphicsItem, GuiLiveNote
+from .items import staticItem2Item, GuiTieCurveGraphicsItem
from .constantsAndConfigs import constantsAndConfigs
from .cursor import Cursor, Playhead, Selection
from .conductor import Conductor, ConductorTransparentBlock
@@ -517,16 +517,6 @@ class GuiTrack(QtWidgets.QGraphicsItem):
backgroundColor.setOpacity(1)
for tbh in self.transparentBlockHandles:
tbh.blockMode()
- def recordingStreamNoteOn(self, liveNoteData):
- guiLiveNote = GuiLiveNote(parent=self, liveNoteData=liveNoteData)
- guiLiveNote.setX(liveNoteData["tickindex"] / constantsAndConfigs.ticksToPixelRatio)
- self.parentScore.liveMidiWaitingForNoteOff[liveNoteData["midipitch"]] = guiLiveNote
-
- def recordingStreamNoteOff(self, liveNoteData):
- guiLiveNote = self.parentScore.liveMidiWaitingForNoteOff[liveNoteData["midipitch"]]
- guiLiveNote.update(liveNoteData["duration"])
- del self.parentScore.liveMidiWaitingForNoteOff[liveNoteData["midipitch"]]
-
class GuiScore(QtWidgets.QGraphicsScene):
def __init__(self, parentView):
@@ -541,8 +531,6 @@ class GuiScore(QtWidgets.QGraphicsScene):
self._deleteOnIdleLoop.start(0) #0 means "if there is time"
self._deleteOnIdleLoop.timeout.connect(self._deleteOnIdle) #processes deleteOnIdleStack
- self.liveMidiWaitingForNoteOff = {} #all unfinished midi notes (key is currently held down). We use one for all tracks because we have a callback in the cursor update function that iterates over all live notes. We don't want that to iterate over all tracks just to find the current live notes.
-
self.duringTrackDragAndDrop = None #switched to a QGraphicsItem (e.g. GuiTrack) while a track is moved around by the mouse
self.duringBlockDragAndDrop = None #switched to a QGraphicsItem (e.g. GuiTrack) while a block is moved around by the mouse
@@ -584,11 +572,6 @@ class GuiScore(QtWidgets.QGraphicsScene):
api.callbacks.updateGraphBlockTrack.append(self.updateGraphBlockTrack)
api.callbacks.graphCCTracksChanged.append(self.syncCCsToBackend)
- #api.callbacks.recordingStreamNoteOn.append(self.recordingStreamNoteOn)
- #api.callbacks.recordingStreamNoteOff.append(self.recordingStreamNoteOff)
- #api.callbacks.setPlaybackTicks.append(self._drawLiveMidiRecording)
- #api.callbacks.recordingStreamClear.append(self.recordingStreamClear)
-
def updateMode(self, nameAsString):
assert nameAsString in constantsAndConfigs.availableEditModes
@@ -665,25 +648,7 @@ class GuiScore(QtWidgets.QGraphicsScene):
self.removeWhenIdle(self.tracks[trackId].ccPaths[cc])
del self.tracks[trackId].ccPaths[cc]
- def recordingStreamNoteOn(self, trackId, liveChord):
- if trackId in self.tracks:
- self.tracks[trackId].recordingStreamNoteOn(liveChord)
- #else:
- #hidden track.
- #self.parentView.updateMode()
- #self.updateSceneRect()
-
- def recordingStreamNoteOff(self, trackId, liveChord):
- if trackId in self.tracks:
- self.tracks[trackId].recordingStreamNoteOff(liveChord)
- #else:
- #hidden track.
- #self.parentView.updateMode()
- #self.updateSceneRect()
-
- def recordingStreamClear(self):
- removeInstancesFromScene(GuiLiveNote)
-
+
def redraw(self, listOfStaticTrackRepresentations):
"""The order of guiTracks depends on the backend index.
This way it is a no-brainer, we don't need to maintain our own
@@ -762,10 +727,6 @@ class GuiScore(QtWidgets.QGraphicsScene):
self.removeItem(deleteMe) #This is the only line in the program that should call scene.removeItem
del deleteMe
- def _drawLiveMidiRecording(self, tickPosition):
- for midiPitch, guiLiveNote in self.liveMidiWaitingForNoteOff.items():
- guiLiveNote.update(tickPosition - guiLiveNote.liveNoteData["tickindex"])
-
def trackAt(self, qScenePosition):
"""trackAt always returns the full GuiTrack, even if in ccEdit mode."""