Sampled Instrument Player with static and monolithic design. All instruments are built-in.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

166 lines
6.2 KiB

#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2022, 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; logger = logging.getLogger(__name__); logger.info("import")
#Standard Library
#Third Party
from PyQt5 import QtCore, QtGui, QtWidgets
#Our Qt
#Engine
import engine.api as api
class SelectedInstrumentController(object):
"""Not a qt class. We externally control a collection of widgets.
There is only one set of widgets. We change their contents dynamically.
"""
def __init__(self, parentMainWindow):
self.parentMainWindow = parentMainWindow
self.currentIdKey = None
self.engineData = {} # id-key tuple : engine library metadata dict.
self.statusUpdates = {} # same as engineData, but with incremental status updates. One is guranteed to exist at startup
#Our Widgets
self.ui = parentMainWindow.ui
self.ui.details_groupBox.setTitle("")
self.ui.details_scrollArea.hide() #until the first instrument was selected
self.ui.variants_comboBox.activated.connect(self._newVariantChosen)
#Callbacks
api.callbacks.instrumentListMetadata.append(self.react_initialInstrumentList)
api.callbacks.instrumentStatusChanged.append(self.react_instrumentStatusChanged)
def directLibrary(self, idkey:tuple):
"""User clicked on a library treeItem"""
libraryId, instrumentId = idkey
self.currentIdKey = None
self.ui.details_scrollArea.show()
self.ui.variants_comboBox.hide()
self.ui.variant_label.hide()
metadata = self.engineData[libraryId]["library"]
self.ui.details_groupBox.setTitle(metadata["name"])
self.ui.info_label.setText(self._metadataToDescriptionLabel(metadata))
def _metadataToDescriptionLabel(self, metadata:dict)->str:
"""Can work with instruments and libraries alike"""
if "variants" in metadata: #this is an instrument
fullText = metadata["description"] + "\n\nVendor: " + metadata["vendor"] + "\n\nLicense: " + metadata["license"]
else:
fullText = metadata["description"] + "\n\nVendor: " + metadata["vendor"]
return fullText
def _newVariantChosen(self, index:int):
"""User chose a new variant through the combo box"""
assert self.ui.variants_comboBox.currentIndex() == index
assert self.currentIdKey
api.chooseVariantByIndex(self.currentIdKey, index)
def instrumentChanged(self, idkey:tuple):
"""This is a GUI-internal function. The user selected a different instrument from
the list. Single click, arrow keys etc.
We combine static metadata, which we saved ourselves, with the current instrument status
(e.g. which variant was chosen).
"""
libraryId, instrumentId = idkey
self.currentIdKey = idkey
self.ui.details_scrollArea.show()
#Static
instrumentData = self.engineData[libraryId][instrumentId]
self.ui.details_groupBox.setTitle(instrumentData["name"])
self.ui.variant_label.show()
self.ui.variants_comboBox.show()
self.ui.variants_comboBox.clear()
self.ui.variants_comboBox.addItems(instrumentData["variantsWithoutSfzExtension"])
self.ui.info_label.setText(self._metadataToDescriptionLabel(instrumentData))
#Dynamic
self.react_instrumentStatusChanged(self.statusUpdates[libraryId][instrumentId])
def react_instrumentStatusChanged(self, instrumentStatus:dict):
"""Callback from the api. Has nothing to do with any GUI state or selection.
Data:
#Static ids
result["id"] = self.metadata["id"]
result["id-key"] = self.idKey #redundancy for convenience.
#Dynamic data
result["currentVariant"] = self.currentVariant # str
result["state"] = self.enabled #bool
"""
idkey = instrumentStatus["id-key"]
libraryId, instrumentId = idkey
if not libraryId in self.statusUpdates:
self.statusUpdates[libraryId] = {} #empty library. status dict
self.statusUpdates[libraryId][instrumentId] = instrumentStatus #create or overwrite / keep up to date
loadState = instrumentStatus["state"]
instrumentData = self.engineData[libraryId][instrumentId]
instrumentStatus = self.statusUpdates[libraryId][instrumentId]
if loadState: #None if not loaded
self.ui.variants_comboBox.setEnabled(True)
currentVariantIndex = instrumentData["variantsWithoutSfzExtension"].index(instrumentStatus["currentVariantWithoutSfzExtension"])
self.ui.variants_comboBox.setCurrentIndex(currentVariantIndex)
else:
self.ui.variants_comboBox.setEnabled(False)
defaultVariantIndex = instrumentData["variantsWithoutSfzExtension"].index(instrumentData["defaultVariantWithoutSfzExtension"])
self.ui.variants_comboBox.setCurrentIndex(defaultVariantIndex)
def react_initialInstrumentList(self, data:dict):
"""For data form see docstring of instrument.py buildTree()
Summary:
libraryid : dict ->
instrumentid : metadatadict
Dict-Keys are always the same. Some always have data, some can be empty.
We receive this once at program start and build our permanent GUI widgets from it.
The additional status update callback for dynamic data is handled in
self.react_instrumentStatusChanged
"""
self.engineData = data