Nils
3 years ago
4 changed files with 183 additions and 4 deletions
@ -0,0 +1,146 @@ |
|||
#! /usr/bin/env python3 |
|||
# -*- coding: utf-8 -*- |
|||
""" |
|||
Copyright 2021, Nils Hilbricht, Germany ( https://www.hilbricht.net ) |
|||
|
|||
This file is part of the Laborejo Software Suite ( https://www.laborejo.org ), |
|||
|
|||
This application is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
This program is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
""" |
|||
import logging; logging.info("import {}".format(__file__)) |
|||
|
|||
#Standard Library Modules |
|||
|
|||
#Third Party Modules |
|||
from PyQt5 import QtWidgets, QtCore, QtGui |
|||
from calfbox import cbox |
|||
|
|||
#Template Modules |
|||
|
|||
#Our modules |
|||
import engine.api as api |
|||
|
|||
|
|||
class QuickMidiInputComboController(QtWidgets.QWidget): |
|||
"""This widget breaks a bit with the convention of engine/gui. However, it is a pure convenience |
|||
fire-and-forget function with no callbacks.""" |
|||
|
|||
def __init__(self, parentWidget): |
|||
super().__init__(parentWidget) |
|||
self.parentWidget = parentWidget |
|||
self.layout = QtWidgets.QHBoxLayout() |
|||
self.setLayout(self.layout) |
|||
|
|||
self.comboBox = QtWidgets.QComboBox(self) |
|||
self.layout.addWidget(self.comboBox) |
|||
""" |
|||
self.volumeDial = parentWidget.ui.auditionerVolumeDial |
|||
self.volumeDial.setMaximum(0) |
|||
self.volumeDial.setMinimum(-21) |
|||
self.volumeDial.valueChanged.connect(self._sendVolumeChangeToEngine) |
|||
self.volumeDial.enterEvent = lambda ev: self.parentWidget.statusBar().showMessage((QtCore.QCoreApplication.translate("Auditioner", "Use mousewheel to change the Auditioner Volume"))) |
|||
self.volumeDial.leaveEvent = lambda ev: self.parentWidget.statusBar().showMessage("") |
|||
self.wholePanel = parentWidget.ui.auditionerWidget |
|||
""" |
|||
self.label = QtWidgets.QLabel(self) |
|||
self.label.setText(QtCore.QCoreApplication.translate("QuickMidiInputComboController", "Midi Input. Use JACK to connect multiple inputs.")) |
|||
self.layout.addWidget(self.label) |
|||
|
|||
#if not api.isStandaloneMode(): |
|||
#self.wholePanel.hide() |
|||
#return |
|||
|
|||
##self.wholePanel.show() #explicit is better than implicit |
|||
self.originalShowPopup = self.comboBox.showPopup |
|||
self.comboBox.showPopup = self.showPopup |
|||
self.comboBox.activated.connect(self._newPortChosen) |
|||
|
|||
self.cboxPortname, self.cboxMidiPortUid = api.getMidiInputNameAndUuid() #these don't change during runtime. |
|||
|
|||
self.layout.addStretch() |
|||
#api.callbacks.startLoadingAuditionerInstrument.append(self.callback_startLoadingAuditionerInstrument) |
|||
#api.callbacks.auditionerInstrumentChanged.append(self.callback_auditionerInstrumentChanged) |
|||
#api.callbacks.auditionerVolumeChanged.append(self.callback__auditionerVolumeChanged) |
|||
|
|||
def callback_startLoadingAuditionerInstrument(self, idkey): |
|||
self.parentWidget.qtApp.setOverrideCursor(QtCore.Qt.WaitCursor) #reset in self.callback_auditionerInstrumentChanged |
|||
self.label.setText(QtCore.QCoreApplication.translate("QuickMidiInputComboController", "…loading…")) |
|||
self.parentWidget.qtApp.processEvents() #actually show the label and cursor |
|||
|
|||
def callback_auditionerInstrumentChanged(self, exportMetadata:dict): |
|||
self.parentWidget.qtApp.restoreOverrideCursor() #We assume the cursor was set to a loading animation |
|||
key = exportMetadata["id-key"] |
|||
t = f"➜ [{key[0]}-{key[1]}] {exportMetadata['name']}" |
|||
self.label.setText(t) |
|||
|
|||
def callback__auditionerVolumeChanged(self, value:float): |
|||
self.volumeDial.setValue(value) |
|||
self.parentWidget.statusBar().showMessage(QtCore.QCoreApplication.translate("QuickMidiInputComboController", "Volume: {}").format(value)) |
|||
|
|||
def _sendVolumeChangeToEngine(self, newValue): |
|||
self.volumeDial.blockSignals(True) |
|||
api.setAuditionerVolume(newValue) |
|||
self.volumeDial.blockSignals(False) |
|||
|
|||
def _newPortChosen(self, index:int): |
|||
assert self.comboBox.currentIndex() == index |
|||
self.connectMidiInputPort(self.comboBox.currentText()) |
|||
|
|||
def showPopup(self): |
|||
"""When the combobox is opened quickly update the port list before showing it""" |
|||
self._fill() |
|||
self.originalShowPopup() |
|||
|
|||
def _fill(self): |
|||
self.comboBox.clear() |
|||
self.comboBox.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents) |
|||
availablePorts = self.getAvailablePorts() |
|||
self.comboBox.addItem("") # Not only a more visible seaparator than the Qt one, but also doubles as "disconnect" |
|||
self.comboBox.addItems(availablePorts["hardware"]) |
|||
#self.comboBox.insertSeparator(len(availablePorts["hardware"])+1) |
|||
self.comboBox.addItem("") # Not only a more visible seaparator than the Qt one, but also doubles as "disconnect" |
|||
self.comboBox.addItems(availablePorts["software"]) |
|||
|
|||
def getAvailablePorts(self)->dict: |
|||
"""This function queries JACK each time it is called. |
|||
It returns a dict with two lists. |
|||
Keys "hardware" and "software" for the type of port. |
|||
""" |
|||
result = {} |
|||
hardware = set(cbox.JackIO.get_ports(".*", cbox.JackIO.MIDI_TYPE, cbox.JackIO.PORT_IS_SOURCE | cbox.JackIO.PORT_IS_PHYSICAL)) |
|||
allPorts = set(cbox.JackIO.get_ports(".*", cbox.JackIO.MIDI_TYPE, cbox.JackIO.PORT_IS_SOURCE)) |
|||
software = allPorts.difference(hardware) |
|||
result["hardware"] = sorted(list(hardware)) |
|||
result["software"] = sorted(list(software)) |
|||
return result |
|||
|
|||
|
|||
def connectMidiInputPort(self, externalPort:str): |
|||
"""externalPort is in the Client:Port JACK format |
|||
If "" False or None disconnect all ports.""" |
|||
|
|||
try: |
|||
currentConnectedList = cbox.JackIO.get_connected_ports(self.cboxMidiPortUid) |
|||
except: #port not found. |
|||
currentConnectedList = [] |
|||
|
|||
for port in currentConnectedList: |
|||
cbox.JackIO.port_disconnect(port, self.cboxPortname) |
|||
|
|||
if externalPort: |
|||
availablePorts = self.getAvailablePorts() |
|||
if not (externalPort in availablePorts["hardware"] or externalPort in availablePorts["software"]): |
|||
raise RuntimeError(f"QuickMidiInput was instructed to connect to port {externalPort}, which does not exist") |
|||
|
|||
cbox.JackIO.port_connect(externalPort, self.cboxPortname) |
Loading…
Reference in new issue