Browse Source

Finalize work on sample dir change for now

master
Nils 2 years ago
parent
commit
ec5a02f0f6
  1. 29
      engine/instrument.py
  2. 38
      engine/main.py
  3. 2
      qtgui/auditioner.py
  4. 44
      qtgui/instrument.py
  5. 6
      qtgui/selectedinstrumentcontroller.py

29
engine/instrument.py

@ -127,7 +127,7 @@ class Instrument(object):
#Static ids #Static ids
result["id"] = int(self.metadata["id"]) result["id"] = int(self.metadata["id"])
result["id-key"] = self.idKey #redundancy for convenience. result["idKey"] = self.idKey #redundancy for convenience.
#Dynamic data #Dynamic data
result["currentVariant"] = self.currentVariant # str result["currentVariant"] = self.currentVariant # str
@ -139,31 +139,6 @@ class Instrument(object):
result["currentKeySwitch"] = self.currentKeySwitch result["currentKeySwitch"] = self.currentKeySwitch
return result return result
def copyStateFrom(self, otherInstrument):
"""Use another instrument instance to copy the soft values.
Not really sampler internal CC but everything we control on our own"""
assert otherInstrument.idKey == self.idKey, (otherInstrument.idKey, self.idKey)
if otherInstrument.enabled:
print (self.idKey, otherInstrument.idKey)
self.enable()
self.chooseVariant(otherInstrument.currentVariant)
if self.currentKeySwitch:
self.setKeySwitch(self.currentKeySwitch)
self.mixerLevel = otherInstrument.mixerLevel
else:
self.disable()
#jack conections
#Ist die alte Verbindung noch hier? Dann können wir nicht jack connections machen. Das teil kriegt sogar den falschen namen!
#Ich könnte die funktion hier doch wieder in lib packen und dann die jack connections parsen,
#die alte lib deaktivieren und dann die neuen instrumente erst aktivieren. schwierig. Das braucht einen Test ob
#die jack connections einen falschen namen kriegen (pretty names ausmachen)
def exportMetadata(self)->dict: def exportMetadata(self)->dict:
"""This gets called before the samples are loaded. """This gets called before the samples are loaded.
Only static data, that does not get changed during runtime, is included here. Only static data, that does not get changed during runtime, is included here.
@ -175,7 +150,7 @@ class Instrument(object):
result = {} result = {}
result["id"] = int(self.metadata["id"]) #int result["id"] = int(self.metadata["id"]) #int
result["id-key"] = self.idKey # tuple (int, int) redundancy for convenience. result["idKey"] = self.idKey # tuple (int, int) redundancy for convenience.
result["name"] = self.metadata["name"] #str result["name"] = self.metadata["name"] #str
result["description"] = self.metadata["description"] #str result["description"] = self.metadata["description"] #str
result["variants"] = self.variants #list of str result["variants"] = self.variants #list of str

38
engine/main.py

@ -94,7 +94,7 @@ class Data(TemplateData):
s = pathlib.Path(PATHS["share"]) s = pathlib.Path(PATHS["share"])
defaultLibraryPath = s.joinpath("000 - Default.tar") defaultLibraryPath = s.joinpath("000 - Default.tar")
logger.info(f"Loading Default Instrument Library from {defaultLibraryPath}. This message must only appear once in the log.") logger.info(f"Loading Default Instrument Library from {defaultLibraryPath}. This message must only appear once in the log.")
defaultLib = Library(parentData=self, tarFilePath=defaultLibraryPath) defaultLib = Library(parentData=self, tarFilePath=defaultLibraryPath) #If this fails we let the program crash. The default samples must exist and be accessible.
self.libraries[defaultLib.id] = defaultLib self.libraries[defaultLib.id] = defaultLib
assert defaultLib.id == 0, defaultLib.id assert defaultLib.id == 0, defaultLib.id
@ -107,7 +107,12 @@ class Data(TemplateData):
for f in basePath.glob('*.tar'): for f in basePath.glob('*.tar'):
if f.is_file() and f.suffix == ".tar": if f.is_file() and f.suffix == ".tar":
#First load the library (this is .ini parsing, not sample loading, so it is cheap) and create a library object #First load the library (this is .ini parsing, not sample loading, so it is cheap) and create a library object
lib = Library(parentData=self, tarFilePath=f) #It will not create jack ports
try:
lib = Library(parentData=self, tarFilePath=f)
except PermissionError as e:
logger.error(f"Library {f} could not be loaded. The reason follows: {e}")
continue
#Then compare if this is actually a file we already knew: #Then compare if this is actually a file we already knew:
#we loaded this before and it still exists. We will NOT delete it below. #we loaded this before and it still exists. We will NOT delete it below.
@ -121,15 +126,14 @@ class Data(TemplateData):
else: #we already know this id else: #we already know this id
oldLib = self.libraries[lib.id] oldLib = self.libraries[lib.id]
print (lib.tarFilePath, oldLib.tarFilePath, lib.tarFilePath.samefile(oldLib.tarFilePath))
if lib.tarFilePath == oldLib.tarFilePath or lib.tarFilePath.samefile(oldLib.tarFilePath): if lib.tarFilePath == oldLib.tarFilePath or lib.tarFilePath.samefile(oldLib.tarFilePath):
#Same id, same file path, or (sym)link. We update the old instrument and maybe there are new variants to load. #Same id, same file path, or (sym)link. We update the old instrument and maybe there are new variants to load.
#Loaded state will remain the same. Tembro instruments don't change with updates. #Loaded state will remain the same. Tembro instruments don't change with updates.
self.libraries[lib.id].updateWithNewParse(lib) self.libraries[lib.id].updateWithNewParse(lib)
else: else:
#Same id, different file path. We treat it as a different lib and unload/reload completely. #Same id, different file path. We treat it as a different lib and unload/reload completely.
self._unloadLibrary(lib.id) #remove old lib instance
lib.transferOldState(oldLib) #at least reactivate the already loaded instruments. lib.transferOldState(oldLib) #at least reactivate the already loaded instruments.
self._unloadLibrary(lib.id) #remove old lib instance
self.libraries[lib.id] = lib #this is the new lib instance. self.libraries[lib.id] = lib #this is the new lib instance.
@ -303,6 +307,7 @@ class Library(object):
needTarData = True needTarData = True
if needTarData: if needTarData:
with tarfile.open(name=tarFilePath, mode='r:') as opentarfile: with tarfile.open(name=tarFilePath, mode='r:') as opentarfile:
#PermissionErrors are caught by the constructing line in main/Data above
iniFileObject = TextIOWrapper(opentarfile.extractfile("library.ini")) iniFileObject = TextIOWrapper(opentarfile.extractfile("library.ini"))
self.config = configparser.ConfigParser() self.config = configparser.ConfigParser()
self.config.read_file(iniFileObject) self.config.read_file(iniFileObject)
@ -345,7 +350,6 @@ class Library(object):
discarded. discarded.
""" """
assert self.tarFilePath == newLib.tarFilePath or newLib.tarFilePath.samefile(self.tarFilePath), (self.tarFilePath, newLib.tarFilePath) assert self.tarFilePath == newLib.tarFilePath or newLib.tarFilePath.samefile(self.tarFilePath), (self.tarFilePath, newLib.tarFilePath)
print ("update new parse", (self.tarFilePath, newLib.tarFilePath))
if newLib.config["library"]["version"] > self.config["library"]["version"]: if newLib.config["library"]["version"] > self.config["library"]["version"]:
self.config = newLib.config self.config = newLib.config
for newInstrId, newInstrument in newLib.instruments.items(): for newInstrId, newInstrument in newLib.instruments.items():
@ -385,9 +389,29 @@ class Library(object):
data from the old one, before discarding it. data from the old one, before discarding it.
""" """
print ("transferOldState", self.tarFilePath, oldLib.tarFilePath)
for instrId, oldInstrument in oldLib.instruments.items(): for instrId, oldInstrument in oldLib.instruments.items():
self.instruments[instrId].copyStateFrom(oldInstrument) #Use another instrument instance to copy the soft values.
#Not really sampler internal CC but at least everything we control on our own.
ourNewInstrument = self.instruments[instrId]
assert oldInstrument.idKey == ourNewInstrument.idKey, (oldInstrument.idKey, ourNewInstrument.idKey)
if oldInstrument.enabled:
#We need to deactivate the old lib before activating the new one because we have the
#same ids and names, which results in the same jack ports.
tmpCurVar = oldInstrument.currentVariant
tmpKeySw = oldInstrument.currentKeySwitch
tmpMix = oldInstrument.mixerLevel
oldInstrument.disable() #frees jack port names
ourNewInstrument.enable()
ourNewInstrument.chooseVariant(tmpCurVar)
if tmpKeySw:
ourNewInstrument.setKeySwitch(tmpKeySw)
ourNewInstrument.mixerLevel = tmpMix
elif ourNewInstrument.enabled and not oldInstrument.enabled:
ourNewInstrument.disable()
def exportMetadata(self)->dict: def exportMetadata(self)->dict:
"""Return a dictionary with each key is an instrument id, but also a special key "library" """Return a dictionary with each key is an instrument id, but also a special key "library"

2
qtgui/auditioner.py

@ -70,7 +70,7 @@ class AuditionerMidiInputComboController(object):
self.currentInstrumentLabel.setText(self.defaultText) self.currentInstrumentLabel.setText(self.defaultText)
else: else:
self.parentMainWindow.qtApp.restoreOverrideCursor() #We assume the cursor was set to a loading animation self.parentMainWindow.qtApp.restoreOverrideCursor() #We assume the cursor was set to a loading animation
key = exportMetadata["id-key"] key = exportMetadata["idKey"]
t = f"➜ [{key[0]}-{key[1]}] {exportMetadata['name']}" t = f"➜ [{key[0]}-{key[1]}] {exportMetadata['name']}"
self.currentInstrumentLabel.setText(t) self.currentInstrumentLabel.setText(t)

44
qtgui/instrument.py

@ -33,7 +33,7 @@ from template.qtgui.helper import ToggleSwitch,FancySwitch
import engine.api as api import engine.api as api
COLUMNS = ("state", "id-key", "mixSend", "name", "loaded", "group", "tags" ) #Loaded = Variant COLUMNS = ("state", "idKey", "mixSend", "name", "loaded", "group", "tags" ) #Loaded = Variant
class InstrumentTreeController(object): class InstrumentTreeController(object):
@ -56,8 +56,8 @@ class InstrumentTreeController(object):
#Includes: #Includes:
#self._cachedData = None #self._cachedData = None
#self._cachedLastInstrumentStatus = {} # instrument idKey : status Dict #self._cachedLastInstrumentStatus = {} # instrument idKey : status Dict
#self.guiLibraries = {} # id-key : GuiLibrary #self.guiLibraries = {} # idKey : GuiLibrary
#self.guiInstruments = {} # id-key : GuiInstrument #self.guiInstruments = {} # idKey : GuiInstrument
self.currentlyNested = None #is the view nested in libraries or just all instruments? self.currentlyNested = None #is the view nested in libraries or just all instruments?
@ -102,8 +102,8 @@ class InstrumentTreeController(object):
self._cachedData = None self._cachedData = None
self._cachedLastInstrumentStatus = {} # instrument idKey : status Dict self._cachedLastInstrumentStatus = {} # instrument idKey : status Dict
#The next two will delete all children through the garbage collector. #The next two will delete all children through the garbage collector.
self.guiLibraries = {} # id-key : GuiLibrary self.guiLibraries = {} # idKey : GuiLibrary
self.guiInstruments = {} # id-key : GuiInstrument self.guiInstruments = {} # idKey : GuiInstrument
def itemExpandedOrCollapsed(self, libraryItem:QtWidgets.QTreeWidgetItem): def itemExpandedOrCollapsed(self, libraryItem:QtWidgets.QTreeWidgetItem):
@ -149,7 +149,7 @@ class InstrumentTreeController(object):
{'0': {'0': {'group': 'strings', {'0': {'0': {'group': 'strings',
'id': '0', 'id': '0',
'id-key': ('0', '0'), 'idKey': ('0', '0'),
'license': 'https://unlicense.org/', 'license': 'https://unlicense.org/',
'name': 'Sine Wave', 'name': 'Sine Wave',
'tags': ['sine', 'basic'], 'tags': ['sine', 'basic'],
@ -157,7 +157,7 @@ class InstrumentTreeController(object):
'vendor': 'Test entry to provide more vendor information'}, 'vendor': 'Test entry to provide more vendor information'},
'1': {'group': 'strings', '1': {'group': 'strings',
'id': '1', 'id': '1',
'id-key': ('0', '1'), 'idKey': ('0', '1'),
'license': '', 'license': '',
'name': 'Square Wave', 'name': 'Square Wave',
'tags': ['square', 'basic'], 'tags': ['square', 'basic'],
@ -165,7 +165,7 @@ class InstrumentTreeController(object):
'vendor': ''}, 'vendor': ''},
'2': {'group': 'brass', '2': {'group': 'brass',
'id': '2', 'id': '2',
'id-key': ('0', '2'), 'idKey': ('0', '2'),
'license': '', 'license': '',
'name': 'Saw Wave', 'name': 'Saw Wave',
'tags': ['saw', 'basic'], 'tags': ['saw', 'basic'],
@ -173,7 +173,7 @@ class InstrumentTreeController(object):
'vendor': ''}, 'vendor': ''},
'3': {'group': '', '3': {'group': '',
'id': '3', 'id': '3',
'id-key': ('0', '3'), 'idKey': ('0', '3'),
'license': '', 'license': '',
'name': 'Triangle Wave', 'name': 'Triangle Wave',
'tags': ['triangle', 'complex'], 'tags': ['triangle', 'complex'],
@ -196,8 +196,8 @@ class InstrumentTreeController(object):
""" """
#Reset everything except our cached data. #Reset everything except our cached data.
self.treeWidget.clear() #will delete the C++ objects. We need to delete the PyQt objects ourselves, like so: self.treeWidget.clear() #will delete the C++ objects. We need to delete the PyQt objects ourselves, like so:
self.guiLibraries = {} # id-key : GuiLibrary self.guiLibraries = {} # idKey : GuiLibrary
self.guiInstruments = {} # id-key : GuiInstrument self.guiInstruments = {} # idKey : GuiInstrument
self.currentlyNested = nested self.currentlyNested = nested
if data: if data:
@ -235,9 +235,9 @@ class InstrumentTreeController(object):
else: else:
self.treeWidget.addTopLevelItem(gi) self.treeWidget.addTopLevelItem(gi)
gi.injectWidgets() #only possible after gi.init() was done and item inserted. gi.injectWidgets() #only possible after gi.init() was done and item inserted.
self.guiInstruments[instrumentDict["id-key"]] = gi self.guiInstruments[instrumentDict["idKey"]] = gi
if instrumentDict["id-key"] in self._cachedLastInstrumentStatus: if instrumentDict["idKey"] in self._cachedLastInstrumentStatus:
gi.updateStatus(self._cachedLastInstrumentStatus[instrumentDict["id-key"]]) gi.updateStatus(self._cachedLastInstrumentStatus[instrumentDict["idKey"]])
self._adjustColumnSize() self._adjustColumnSize()
@ -266,12 +266,12 @@ class InstrumentTreeController(object):
def react_instrumentStatusChanged(self, instrumentStatus:dict): def react_instrumentStatusChanged(self, instrumentStatus:dict):
self.parentMainWindow.qtApp.restoreOverrideCursor() #Sometimes the instrument was loaded with a cursor animation self.parentMainWindow.qtApp.restoreOverrideCursor() #Sometimes the instrument was loaded with a cursor animation
gi = self.guiInstruments[instrumentStatus["id-key"]] gi = self.guiInstruments[instrumentStatus["idKey"]]
gi.updateStatus(instrumentStatus) gi.updateStatus(instrumentStatus)
self._adjustColumnSize() self._adjustColumnSize()
#We also cache the last status, as we cache the initial data. This way we can delete and recreate TreeItems without requesting new status data from the engine #We also cache the last status, as we cache the initial data. This way we can delete and recreate TreeItems without requesting new status data from the engine
self._cachedLastInstrumentStatus[instrumentStatus["id-key"]] = instrumentStatus self._cachedLastInstrumentStatus[instrumentStatus["idKey"]] = instrumentStatus
def react_startLoadingSamples(self, idKey:tuple): def react_startLoadingSamples(self, idKey:tuple):
"""Will be overriden by instrument status change / variant chosen""" """Will be overriden by instrument status change / variant chosen"""
@ -300,8 +300,8 @@ class GuiLibrary(QtWidgets.QTreeWidgetItem):
self.name = libraryDict["name"] self.name = libraryDict["name"]
#No dynamic data here. Everything gets created once. #No dynamic data here. Everything gets created once.
#self.setText(COLUMNS.index("id-key"), str(libraryDict["id"]).zfill(leadingZeroesForZfill)) #self.setText(COLUMNS.index("idKey"), str(libraryDict["id"]).zfill(leadingZeroesForZfill))
self.setData(COLUMNS.index("id-key"), 0, int(libraryDict["id"])) #set data allows sorting by actual numbers. 0 is the data role, which is just "display text". self.setData(COLUMNS.index("idKey"), 0, int(libraryDict["id"])) #set data allows sorting by actual numbers. 0 is the data role, which is just "display text".
self.setText(COLUMNS.index("name"), str(libraryDict["name"])) self.setText(COLUMNS.index("name"), str(libraryDict["name"]))
self.setText(COLUMNS.index("tags"), str(libraryDict["description"])[:42]+"") self.setText(COLUMNS.index("tags"), str(libraryDict["description"])[:42]+"")
@ -337,9 +337,9 @@ class GuiInstrument(QtWidgets.QTreeWidgetItem):
def __init__(self, parentTreeController, instrumentDict): def __init__(self, parentTreeController, instrumentDict):
GuiInstrument.allItems[instrumentDict["id-key"]] = self GuiInstrument.allItems[instrumentDict["idKey"]] = self
self.parentTreeController = parentTreeController self.parentTreeController = parentTreeController
self.idKey = instrumentDict["id-key"] self.idKey = instrumentDict["idKey"]
#Start with empty columns. We fill in later in _writeColumns #Start with empty columns. We fill in later in _writeColumns
super().__init__([], type=1000) #type 0 is default qt type. 1000 is subclassed user type) super().__init__([], type=1000) #type 0 is default qt type. 1000 is subclassed user type)
@ -350,7 +350,7 @@ class GuiInstrument(QtWidgets.QTreeWidgetItem):
#nameColumnIndex = self.columns.index("prettyName") #nameColumnIndex = self.columns.index("prettyName")
#self.setText(nameColumnIndex, "hello") #self.setText(nameColumnIndex, "hello")
self.setTextAlignment(self.columns.index("id-key"), QtCore.Qt.AlignHCenter) self.setTextAlignment(self.columns.index("idKey"), QtCore.Qt.AlignHCenter)
self.state = None #by self.update... self.state = None #by self.update...
self.instrumentDict = None self.instrumentDict = None
@ -512,7 +512,7 @@ class GuiInstrument(QtWidgets.QTreeWidgetItem):
t = ", ".join(instrumentDict[key]) t = ", ".join(instrumentDict[key])
self.setText(index, t) self.setText(index, t)
elif key == "id-key": #tuple elif key == "idKey": #tuple
libId, instrId = instrumentDict[key] libId, instrId = instrumentDict[key]
zeros = int(instrumentDict["instrumentsInLibraryCount"]/10)+1 zeros = int(instrumentDict["instrumentsInLibraryCount"]/10)+1
instIdZFilled = str(instrId).zfill(zeros) instIdZFilled = str(instrId).zfill(zeros)

6
qtgui/selectedinstrumentcontroller.py

@ -41,7 +41,7 @@ class SelectedInstrumentController(object):
def __init__(self, parentMainWindow): def __init__(self, parentMainWindow):
self.parentMainWindow = parentMainWindow self.parentMainWindow = parentMainWindow
self.currentIdKey = None self.currentIdKey = None
self.engineData = {} # id-key tuple : engine library metadata dict. self.engineData = {} # idKey tuple : engine library metadata dict.
self.statusUpdates = {} # same as engineData, but with incremental status updates. One is guranteed to exist at startup self.statusUpdates = {} # same as engineData, but with incremental status updates. One is guranteed to exist at startup
#Our Widgets #Our Widgets
@ -166,7 +166,7 @@ class SelectedInstrumentController(object):
Data: Data:
#Static ids #Static ids
result["id"] = self.metadata["id"] result["id"] = self.metadata["id"]
result["id-key"] = self.idKey #redundancy for convenience. result["idKey"] = self.idKey #redundancy for convenience.
#Dynamic data #Dynamic data
result["currentVariant"] = self.currentVariant # str result["currentVariant"] = self.currentVariant # str
@ -180,7 +180,7 @@ class SelectedInstrumentController(object):
This callback is called again by the GUI directly when switching the instrument with a This callback is called again by the GUI directly when switching the instrument with a
mouseclick in instrumentChanged and the GUI will use the cached data. mouseclick in instrumentChanged and the GUI will use the cached data.
""" """
idkey = instrumentStatus["id-key"] idkey = instrumentStatus["idKey"]
libraryId, instrumentId = idkey libraryId, instrumentId = idkey
if not libraryId in self.statusUpdates: if not libraryId in self.statusUpdates:

Loading…
Cancel
Save