instrument.get_output_slot(0).rec_wet.attach(self.outputMergerRouter)#output_slot is 0 based and means a pair. Most sfz instrument have only one stereo pair. #TODO: And what if not?
instrument.get_output_slot(0).rec_wet.attach(self.outputMergerRouter)#output_slot is 0 based and means a pair. Most sfz instrument have only one stereo pair. #TODO: And what if not?
self.currentVariant:str=""#This is the currently loaded variant. Only set after actual loading samples. That means it is "" even from a savefile and only set later.
self.currentVariant:str=""#This is the currently loaded variant. Only set after actual loading samples. That means it is "" even from a savefile and only set later.
#We could call self.loadSamples() now, but we delay that for the user experience. See docstring.
self.audioOutputs=[]#jack audio output ports uuids, compatible with cbox. Multiple of 2 because stereo pairs. They get created on enable and deleted on disable. Between these points they are static. All variants have the same number of outputs.
#Set in self.enable()
self.outputMergerRouters=[]#use index as slot index
self.routerToGlobalSummingStereoMixers=[]#use index as slot index
self.monoOutputPortsNames=[]#without jack client name. Empty if not enabled
#We could call self.loadSamples() now, but we delay that for the user experience. See docstring.
defexportStatus(self)->dict:
defexportStatus(self)->dict:
"""The call-often function to get the instrument status. Includes only data that can
"""The call-often function to get the instrument status. Includes only data that can
@ -150,6 +126,7 @@ class Instrument(object):
result["playableKeys"]=self.playableKeys
result["playableKeys"]=self.playableKeys
result["keyLabels"]=self.keyLabels
result["keyLabels"]=self.keyLabels
result["controlLabels"]=self.controlLabels#CCs
result["controlLabels"]=self.controlLabels#CCs
result["outputLabels"]=self.outputLabels
returnresult
returnresult
defexportMetadata(self)->dict:
defexportMetadata(self)->dict:
@ -221,7 +198,7 @@ class Instrument(object):
ifnotvariantSfzFileNameinself.variants:
ifnotvariantSfzFileNameinself.variants:
raiseValueError("Variant not in list: {}{}".format(variantSfzFileName,self.variants))
raiseValueError("Variant not in list: {}{}".format(variantSfzFileName,self.variants))
logger.info(f"Start loading samples for instrument {variantSfzFileName} with id key {self.idKey}")
logger.info(f"Start loading instrument variant {variantSfzFileName} with id key {self.idKey}")
#help (self.allInstrumentLayers[self.defaultPortUid].engine) #shows the functions to load programs into channels etc.
#help (self.allInstrumentLayers[self.defaultPortUid].engine) #shows the functions to load programs into channels etc.
raiseRuntimeError(f"{self.name} tried to switch to enabled, but it already was. This is not a trivial error. Please make sure no user-function can reach this state.")
raiseRuntimeError(f"{self.name} tried to switch to enabled, but it already was. This is not a trivial error. Please make sure no user-function can reach this state.")
@ -478,49 +487,95 @@ class Instrument(object):
#Calfbox. The JACK ports are constructed without samples at first.
#Calfbox. The JACK ports are constructed without samples at first.
self.scene=cbox.Document.get_engine().new_scene()#We need an individual scene for each instrument. Midi Routing is based on scenes.
self.scene=cbox.Document.get_engine().new_scene()#We need an individual scene for each instrument. Midi Routing is based on scenes.
self.scene.clear()
self.scene.clear()
self.sfzSamplerLayer=self.scene.add_new_instrument_layer(self.midiInputPortName,"sampler")#"sampler" is the cbox sfz engine
#We set temporary config settings before creating the instrument.
#In the past with only stereo outputs and self.scene.add_new_instrument_layer this was not needed
#but now we want multi outputs and need this little work around
instrumentName=str(self.idKey)#the instrument name is not visible anywhere. It is an internal name only.
self.program=None#return object from self.instrumentLayer.engine.load_patch_from_tar
self.program=None#return object from self.instrumentLayer.engine.load_patch_from_tar
#self.scene.status().layers[0].set_ignore_program_changes(1) #TODO: ignore different channels. We only want one channel per scene/instrument/port. #TODO: Add generic filters to filter out redundant tasks like mixing and panning, which should be done in an audio mixer.
#self.scene.status().layers[0].set_ignore_program_changes(1) #TODO: ignore different channels. We only want one channel per scene/instrument/port. #TODO: Add generic filters to filter out redundant tasks like mixing and panning, which should be done in an audio mixer.
#self.instrumentLayer.engine.set_polyphony(int)
#self.instrumentLayer.engine.set_polyphony(int)
#Create Stereo Audio Ouput Ports
#Connect to our own pair but also to a generic mixer port that is in Data()
instrument.get_output_slot(0).rec_wet.attach(self.outputMergerRouter)#output_slot is 0 based and means a pair. Most sfz instrument have only one stereo pair. #TODO: And what if not?
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.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)#Route midi input to the scene. Without this we have no sound, but the python processor would still work.
cbox.JackIO.route_midi_input(self.cboxMidiPortUid,self.scene.uuid)#Route midi input to the scene. Without this we have no sound, but the python processor would still work.
self.scene.status().layers[0].get_instrument().engine.load_patch_from_string(0,"","","")#fill with null instruments, hopefully replacing the loaded sfz data.
self.scene.status().layers[0].get_instrument().engine.load_patch_from_string(0,"","","")#fill with null instruments, hopefully replacing the loaded sfz data.
instrument=self.sfzSamplerLayer.get_instrument()
instrument=self.instrumentLayer
instrument.get_output_slot(0).rec_wet.detach(self.outputMergerRouter)#output_slot is 0 based and means a pair. Most sfz instrument have only one stereo pair. #TODO: And what if not?
hardwareAudioPorts=cbox.JackIO.get_ports("system*",cbox.JackIO.AUDIO_TYPE,cbox.JackIO.PORT_IS_SINK|cbox.JackIO.PORT_IS_PHYSICAL)#don't sort. This is correctly sorted. Another sorted will do 1, 10, 11,
hardwareAudioPorts=cbox.JackIO.get_ports("system*",cbox.JackIO.AUDIO_TYPE,cbox.JackIO.PORT_IS_SINK|cbox.JackIO.PORT_IS_PHYSICAL)#don't sort. This is correctly sorted. Another sorted will do 1, 10, 11,
forinstrinself.allInstr():#this is sorted alphabetically, which is the library id
R=clientName+":"+instr.midiInputPortName+"_R"
#Use the real names, not the pretty metadata names
#We save the instrument object so that the real function updateJackMetadataSorting below can quickly check if an instrument is currently enabled and send this to jack metadata, or not