Browse Source

Send midi feedback from auditioner

master
Nils 2 years ago
parent
commit
546e721d8f
  1. 3
      engine/api.py
  2. 26
      engine/auditioner.py
  3. 6
      qtgui/horizontalpiano.py
  4. 7
      qtgui/instrument.py
  5. 10
      qtgui/verticalpiano.py

3
engine/api.py

@ -127,6 +127,7 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks
func(export)
def _instrumentMidiNoteOnActivity(self, idKey, pitch, velocity):
"""This happens for real instruments as well as the auditioner"""
for func in self.instrumentMidiNoteOnActivity:
func(idKey, pitch, velocity)
@ -308,7 +309,7 @@ def auditionerInstrument(idKey:tuple):
else:
var = originalInstrument.defaultVariant
session.data.auditioner.loadInstrument(originalInstrument.tarFilePath, originalInstrument.rootPrefixPath, var, originalInstrument.currentKeySwitch)
session.data.auditioner.loadInstrument(idKey, originalInstrument.tarFilePath, originalInstrument.rootPrefixPath, var, originalInstrument.currentKeySwitch)
callbacks._auditionerInstrumentChanged(libraryId, instrumentId)
def getAvailableAuditionerPorts()->dict:

26
engine/auditioner.py

@ -28,6 +28,7 @@ import logging; logger = logging.getLogger(__name__); logger.info("import")
from calfbox import cbox
#Template Modules
from template.engine.input_midi import MidiProcessor
class Auditioner(object):
"""
@ -48,6 +49,8 @@ class Auditioner(object):
self.midiInputPortName = "Auditioner"
self.cboxPortname = cbox.JackIO.status().client_name + ":" + self.midiInputPortName
self.idKey = None
#Calfbox. The JACK ports are constructed without samples at first.
self.scene = cbox.Document.get_engine().new_scene()
self.scene.clear()
@ -77,6 +80,13 @@ class Auditioner(object):
cbox.JackIO.set_appsink_for_midi_input(self.cboxMidiPortUid, True) #This sounds like a program wide sink, but it is needed for every port.
cbox.JackIO.route_midi_input(self.cboxMidiPortUid, self.scene.uuid)
#Always on
self.midiProcessor = MidiProcessor(parentInput = self) #works through self.cboxMidiPortUid
self.midiProcessor.register_NoteOn(self.triggerNoteOnCallback)
self.midiProcessor.register_NoteOff(self.triggerNoteOffCallback)
#self.midiProcessor.notePrinter(True)
self.parentData.parentSession.eventLoop.fastConnect(self.midiProcessor.processEvents)
@property
def volume(self)->float:
return self.outputMergerRouter.status().gain
@ -89,7 +99,7 @@ class Auditioner(object):
value = -21
self.outputMergerRouter.set_gain(value)
def loadInstrument(self, tarFilePath, rootPrefixPath:str, variantSfzFileName:str, keySwitchMidiPitch:int):
def loadInstrument(self, idKey, tarFilePath, rootPrefixPath:str, variantSfzFileName:str, keySwitchMidiPitch:int):
"""load_patch_from_tar is blocking. This function will return when the instrument is ready
to play.
"""
@ -100,6 +110,7 @@ class Auditioner(object):
name = variantSfzFileName
self.program = self.instrumentLayer.engine.load_patch_from_tar(programNumber, tarFilePath, rootPrefixPath+variantSfzFileName, name)
self.currentVariant = variantSfzFileName
self.idKey = idKey
if keySwitchMidiPitch is None:
self.currentKeySwitch = None
@ -116,6 +127,7 @@ class Auditioner(object):
self.scene.status().layers[0].get_instrument().engine.load_patch_from_string(0, "", "", "") #fill with null instruments, hopefully replacing the loaded sfz data.
self.currentVariant = None
self.currentKeySwitch = None
self.idKey = None
def getAvailablePorts(self)->dict:
"""This function queries JACK each time it is called.
@ -149,3 +161,15 @@ class Auditioner(object):
raise RuntimeError(f"Auditioner was instructed to connect to port {externalPort}, which does not exist")
cbox.JackIO.port_connect(externalPort, self.cboxPortname)
def triggerNoteOnCallback(self, timestamp, channel, pitch, velocity):
"""args are: timestamp, channel, note, velocity.
consider to change eventloop.slowConnect to fastConnect. And also disconnect in self.disable()"""
if self.idKey:
self.parentData.instrumentMidiNoteOnActivity(self.idKey, pitch, velocity)
def triggerNoteOffCallback(self, timestamp, channel, pitch, velocity):
"""args are: timestamp, channel, note, velocity.
consider to change eventloop.slowConnect to fastConnect. And also disconnect in self.disable()"""
if self.idKey:
self.parentData.instrumentMidiNoteOffActivity(self.idKey, pitch, velocity)

6
qtgui/horizontalpiano.py

@ -209,10 +209,12 @@ class _HorizontalPianoScene(QtWidgets.QGraphicsScene):
self.instrumentStatusChanged(instrumentStatus)
def highlightNoteOn(self, idKey:tuple, pitch:int, velocity:int):
self.allKeys[pitch].highlightOn()
if self._selectedInstrument and self._selectedInstrument[0]["idKey"] == idKey:
self.allKeys[pitch].highlightOn()
def highlightNoteOff(self, idKey:tuple, pitch:int, velocity:int):
self.allKeys[pitch].highlightOff()
if self._selectedInstrument and self._selectedInstrument[0]["idKey"] == idKey:
self.allKeys[pitch].highlightOff()
def allHighlightsOff(self):
for keyPitch, keyObject in self.allKeys.items():

7
qtgui/instrument.py

@ -420,7 +420,7 @@ class GuiInstrument(QtWidgets.QTreeWidgetItem):
self.setIcon(COLUMNS.index("name"), self.offIcon)
self.midiActiveFlag = False #midi indicator
api.session.eventLoop.verySlowConnect(self._activityOff) #this is always on, even if no samples loaded. The auditioner sends activeOn, even if state==False
def instrumentSwitchOnViaGui(self, state):
"""Only GUI clicks. Does not react to the engine callback that switches on instruments. For
@ -456,10 +456,11 @@ class GuiInstrument(QtWidgets.QTreeWidgetItem):
self.mixSendDial.setStyleSheet(self.mutedMixSendStylesheet)
self.parentTreeController.parentMainWindow.statusBar().showMessage((QtCore.QCoreApplication.translate("Instrument", "{} send to mix volume: {} {}".format(self.instrumentDict["name"], instrumentStatus["mixerLevel"], muteText))))
api.session.eventLoop.verySlowConnect(self._activityOff)
#api.session.eventLoop.verySlowConnect(self._activityOff) #this is always on, even if not loaded. The auditioner sends activeOn, even if state==False
elif not firstLoad: #and not self.state
#the instrument was once loaded and is currently connected
api.session.eventLoop.verySlowDisconnect(self._activityOff)
pass
#api.session.eventLoop.verySlowDisconnect(self._activityOff)
if not self.state: #in any not-case

10
qtgui/verticalpiano.py

@ -214,12 +214,14 @@ class _VerticalPianoScene(QtWidgets.QGraphicsScene):
self.instrumentStatusChanged(instrumentStatus)
def highlightNoteOn(self, idKey:tuple, pitch:int, velocity:int):
highlight = self.highlights[pitch]
highlight.show()
if self._selectedInstrument and self._selectedInstrument[0]["idKey"] == idKey:
highlight = self.highlights[pitch]
highlight.show()
def highlightNoteOff(self, idKey:tuple, pitch:int, velocity:int):
highlight = self.highlights[pitch]
highlight.hide()
if self._selectedInstrument and self._selectedInstrument[0]["idKey"] == idKey:
highlight = self.highlights[pitch]
highlight.hide()
def allHighlightsOff(self):
for pitch, highlight in self.highlights.items():

Loading…
Cancel
Save