Browse Source

explicitly unload all instruments on quit to circumvent a jackd freeze

master
Nils 2 years ago
parent
commit
dcdb46cba8
  1. 33
      engine/api.py
  2. 2
      engine/main.py
  3. 3
      qtgui/instrument.py
  4. 1
      qtgui/mainwindow.py

33
engine/api.py

@ -24,6 +24,8 @@ import logging; logger = logging.getLogger(__name__); logger.info("import")
#Standard Library Modules
from typing import List, Set, Dict, Tuple
import atexit
#Third Party Modules
from calfbox import cbox
@ -142,23 +144,44 @@ def startEngine(nsmClient, additionalData):
callbacks._auditionerVolumeChanged()
atexit.register(unloadAllInstrumentSamples) #this will handle all python exceptions, but not segfaults of C modules.
logger.info("Tembro api startEngine complete")
def loadAllInstrumentSamples():
"""Actually load all instrument samples"""
logger.info(f"Loading all instruments.")
for instrument in Instrument.allInstruments.values():
callbacks._startLoadingSamples(*instrument.idKey)
instrument.loadSamples()
callbacks._instrumentStatusChanged(*instrument.idKey)
callbacks._dataChanged
callbacks._dataChanged()
def unloadAllInstrumentSamples():
"""Cleanup.
It turned out relying on cbox closes ports or so too fast for JACK (not confirmed).
If too many instruments (> ~12) were loaded the program will freeze on quit and freeze
JACK as well.
A controlled deactivating of instruments circumvents this.
#TODO: Fixing jack2 is out of scope for us. If that even is a jack2 error.
The order of atexit is the reverse order of registering. Since the template stopSession
is registered before api.startEngine it is safe to rely on atexit.
The function could also be used from the menu of course.
"""
logger.info(f"Unloading all instruments.")
for instrument in Instrument.allInstruments.values():
if instrument.enabled:
instrument.disable()
callbacks._instrumentStatusChanged(*instrument.idKey)
callbacks._dataChanged() #Needs to be here to have incremental updates in the gui.
def unloadInstrumentSamples(idkey:tuple):
instrument = Instrument.allInstruments[idkey]
instrument.disable()
callbacks._instrumentStatusChanged(*instrument.idKey)
callbacks._dataChanged
callbacks._dataChanged()
def loadInstrumentSamples(idkey:tuple):
"""Load one .sfz from a library."""
@ -170,7 +193,7 @@ def loadInstrumentSamples(idkey:tuple):
instrument.loadSamples()
callbacks._instrumentStatusChanged(*instrument.idKey)
callbacks._dataChanged
callbacks._dataChanged()
def chooseVariantByIndex(idkey:tuple, variantIndex:int):
"""Choose a variant of an already enabled instrument"""
@ -178,7 +201,7 @@ def chooseVariantByIndex(idkey:tuple, variantIndex:int):
instrument = Instrument.allInstruments[idkey]
instrument.chooseVariantByIndex(variantIndex)
callbacks._instrumentStatusChanged(*instrument.idKey)
callbacks._dataChanged
callbacks._dataChanged()
def setInstrumentMixerVolume(idkey:tuple, value:float):
"""From 0 to -21.

2
engine/main.py

@ -59,13 +59,11 @@ class Data(TemplateData):
session = self.parentSession #self.parentSession is already defined in template.data. We just want to work conveniently in init with it by creating a local var.
self._processAfterInit()
def _processAfterInit(self):
session = self.parentSession #We just want to work conveniently in init with it by creating a local var.
self.cachedSerializedDataForStartEngine = None
def parseAndLoadInstrumentLibraries(self, baseSamplePath):
"""Called once by api.startEngine, which receives the global sample path from
the GUI"""

3
qtgui/instrument.py

@ -438,6 +438,9 @@ class GuiInstrument(QtWidgets.QTreeWidgetItem):
self.mixSendDial.setEnabled(False)
self.mixSendDial.setUpdatesEnabled(False) #this is a hack to make the widget disappear. Because hiding a QTreeWidgetItem does not work.
self.parentTreeController.parentMainWindow.qtApp.processEvents() #actually show the new state, even if mass unloading (which did not work before. But loading worked!)
def _mixSendDialContextMenuEvent(self, event):
if self._cachedInstrumentStatus["mixerEnabled"]:
mixerMuteText = QtCore.QCoreApplication.translate("InstrumentMixerLevelContextMenu", "Mute/Disable Mixer-Send for {}".format(self.instrumentDict["name"]))

1
qtgui/mainwindow.py

@ -113,6 +113,7 @@ class MainWindow(TemplateMainWindow):
#self.menu.addMenuEntry("menuEdit", "actionNils", "Nils", lambda: print("Merle"))
self.menu.addMenuEntry("menuEdit", "actionSampleDirPathDialog", "Sample Files Location", ChooseDownloadDirectory)
self.menu.addMenuEntry("menuEdit", "actionLoadSamples", QtCore.QCoreApplication.translate("Menu", "Load all Instrument Samples (slow!)"), api.loadAllInstrumentSamples)
self.menu.addMenuEntry("menuEdit", "actionUnloadSamples", QtCore.QCoreApplication.translate("Menu", "Unload all Instrument Samples (also slow.)"), api.unloadAllInstrumentSamples)
#self.menu.connectMenuEntry("actionNils", lambda: print("Override"))
self.menu.addSubmenu("menuView", QtCore.QCoreApplication.translate("Menu", "View"))

Loading…
Cancel
Save