#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Carla plugin host (plugin UI) # Copyright (C) 2013-2014 Filipe Coelho # # This program 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 2 of # the License, or 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. # # For a full copy of the GNU General Public License see the GPL.txt file # ------------------------------------------------------------------------------------------------------------ # Imports (Custom Stuff) from carla_host import * from externalui import ExternalUI # ------------------------------------------------------------------------------------------------------------ # Host Plugin object class PluginHost(CarlaHostPlugin): def __init__(self, sampleRate): CarlaHostPlugin.__init__(self) self.fIsRunning = True self.fSampleRate = sampleRate # ------------------------------------------------------------------- def sendMsg(self, lines): # FIXME gCarla.gui.send(lines) return True # ------------------------------------------------------------------- def engine_init(self, driverName, clientName): return True def engine_close(self): return True def engine_idle(self): if gCarla.gui.idleExternalUI(): return self.fIsRunning = False gCarla.gui.d_uiQuit() def is_engine_running(self): return self.fIsRunning def set_engine_about_to_close(self): return # ------------------------------------------------------------------------------------------------------------ # Main Window class CarlaMiniW(ExternalUI, HostWindow): def __init__(self): ExternalUI.__init__(self) HostWindow.__init__(self, None) if sys.argv[0].lower().endswith("/carla-plugin-patchbay"): from carla_patchbay import CarlaPatchbayW self.fContainer = CarlaPatchbayW(self) self.setupContainer(True, self.fContainer.themeData) else: from carla_rack import CarlaRackW self.fContainer = CarlaRackW(self) self.setupContainer(False) self.setWindowTitle(self.fUiName) self.showUiIfTesting() # ------------------------------------------------------------------- # ExternalUI Callbacks def d_uiShow(self): self.show() def d_uiHide(self): self.hide() def d_uiQuit(self): self.close() app.quit() def d_uiTitleChanged(self, uiTitle): self.setWindowTitle(uiTitle) # ------------------------------------------------------------------- # Qt events def closeEvent(self, event): self.closeExternalUI() HostWindow.closeEvent(self, event) # ------------------------------------------------------------------- # Custom idler def idleExternalUI(self): while True: if self.fPipeRecv is None: return True try: msg = self.fPipeRecv.readline().strip() except IOError: return False if not msg: return True elif msg.startswith("PEAKS_"): pluginId = int(msg.replace("PEAKS_", "")) in1, in2, out1, out2 = [float(i) for i in self.readlineblock().split(":")] gCarla.host._set_peaks(pluginId, in1, in2, out1, out2) elif msg.startswith("PARAMVAL_"): pluginId, paramId = [int(i) for i in msg.replace("PARAMVAL_", "").split(":")] paramValue = float(self.readlineblock()) gCarla.host._set_parameterValueS(pluginId, paramId, paramValue) elif msg.startswith("ENGINE_CALLBACK_"): action = int(msg.replace("ENGINE_CALLBACK_", "")) pluginId = int(self.readlineblock()) value1 = int(self.readlineblock()) value2 = int(self.readlineblock()) value3 = float(self.readlineblock()) valueStr = self.readlineblock().replace("\r", "\n") if action == ENGINE_CALLBACK_PLUGIN_RENAMED: gCarla.host._set_pluginName(pluginId, valueStr) elif action == ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED: if value1 < 0: gCarla.host._set_parameterValueInt(pluginId, value1, value3) else: gCarla.host._set_parameterValueS(pluginId, value1, value3) elif action == ENGINE_CALLBACK_PARAMETER_DEFAULT_CHANGED: gCarla.host._set_parameterDefault(pluginId, value1, value3) elif action == ENGINE_CALLBACK_PARAMETER_MIDI_CC_CHANGED: gCarla.host._set_parameterMidiCC(pluginId, value1, value2) elif action == ENGINE_CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED: gCarla.host._set_parameterMidiChannel(pluginId, value1, value2) elif action == ENGINE_CALLBACK_PROGRAM_CHANGED: gCarla.host._set_currentProgram(pluginId, value1) elif action == ENGINE_CALLBACK_MIDI_PROGRAM_CHANGED: gCarla.host._set_currentMidiProgram(pluginId, value1) engineCallback(None, action, pluginId, value1, value2, value3, valueStr) elif msg.startswith("PLUGIN_INFO_"): pluginId = int(msg.replace("PLUGIN_INFO_", "")) gCarla.host._add(pluginId) type_, category, hints, uniqueId, optsAvail, optsEnabled = [int(i) for i in self.readlineblock().split(":")] filename = self.readlineblock().replace("\r", "\n") name = self.readlineblock().replace("\r", "\n") iconName = self.readlineblock().replace("\r", "\n") realName = self.readlineblock().replace("\r", "\n") label = self.readlineblock().replace("\r", "\n") maker = self.readlineblock().replace("\r", "\n") copyright = self.readlineblock().replace("\r", "\n") pinfo = { 'type': type_, 'category': category, 'hints': hints, 'optionsAvailable': optsAvail, 'optionsEnabled': optsEnabled, 'filename': filename, 'name': name, 'label': label, 'maker': maker, 'copyright': copyright, 'iconName': iconName, 'patchbayClientId': 0, 'uniqueId': uniqueId } gCarla.host._set_pluginInfo(pluginId, pinfo) gCarla.host._set_pluginRealName(pluginId, realName) elif msg.startswith("AUDIO_COUNT_"): pluginId, ins, outs = [int(i) for i in msg.replace("AUDIO_COUNT_", "").split(":")] gCarla.host._set_audioCountInfo(pluginId, {'ins': ins, 'outs': outs}) elif msg.startswith("MIDI_COUNT_"): pluginId, ins, outs = [int(i) for i in msg.replace("MIDI_COUNT_", "").split(":")] gCarla.host._set_midiCountInfo(pluginId, {'ins': ins, 'outs': outs}) elif msg.startswith("PARAMETER_COUNT_"): pluginId, ins, outs, count = [int(i) for i in msg.replace("PARAMETER_COUNT_", "").split(":")] gCarla.host._set_parameterCountInfo(pluginId, count, {'ins': ins, 'outs': outs}) elif msg.startswith("PARAMETER_DATA_"): pluginId, paramId = [int(i) for i in msg.replace("PARAMETER_DATA_", "").split(":")] paramType, paramHints, midiChannel, midiCC = [int(i) for i in self.readlineblock().split(":")] paramName = self.readlineblock().replace("\r", "\n") paramUnit = self.readlineblock().replace("\r", "\n") paramInfo = { 'name': paramName, 'symbol': "", 'unit': paramUnit, 'scalePointCount': 0, } gCarla.host._set_parameterInfoS(pluginId, paramId, paramInfo) paramData = { 'type': paramType, 'hints': paramHints, 'index': paramId, 'rindex': -1, 'midiCC': midiCC, 'midiChannel': midiChannel } gCarla.host._set_parameterDataS(pluginId, paramId, paramData) elif msg.startswith("PARAMETER_RANGES_"): pluginId, paramId = [int(i) for i in msg.replace("PARAMETER_RANGES_", "").split(":")] def_, min_, max_, step, stepSmall, stepLarge = [float(i) for i in self.readlineblock().split(":")] paramRanges = { 'def': def_, 'min': min_, 'max': max_, 'step': step, 'stepSmall': stepSmall, 'stepLarge': stepLarge } gCarla.host._set_parameterRangeS(pluginId, paramId, paramRanges) elif msg.startswith("PROGRAM_COUNT_"): pluginId, count, current = [int(i) for i in msg.replace("PROGRAM_COUNT_", "").split(":")] gCarla.host._set_programCount(pluginId, count) gCarla.host._set_currentProgram(pluginId, current) elif msg.startswith("PROGRAM_NAME_"): pluginId, progId = [int(i) for i in msg.replace("PROGRAM_NAME_", "").split(":")] progName = self.readlineblock().replace("\r", "\n") gCarla.host._set_programNameS(pluginId, progId, progName) elif msg.startswith("MIDI_PROGRAM_COUNT_"): pluginId, count, current = [int(i) for i in msg.replace("MIDI_PROGRAM_COUNT_", "").split(":")] gCarla.host._set_midiProgramCount(pluginId, count) gCarla.host._set_currentMidiProgram(pluginId, current) elif msg.startswith("MIDI_PROGRAM_DATA_"): pluginId, midiProgId = [int(i) for i in msg.replace("MIDI_PROGRAM_DATA_", "").split(":")] bank, program = [int(i) for i in self.readlineblock().split(":")] name = self.readlineblock().replace("\r", "\n") gCarla.host._set_midiProgramDataS(pluginId, midiProgId, {'bank': bank, 'program': program, 'name': name}) elif msg == "error": error = self.readlineblock().replace("\r", "\n") engineCallback(None, ENGINE_CALLBACK_ERROR, 0, 0, 0, 0.0, error) elif msg == "show": self.d_uiShow() elif msg == "hide": self.d_uiHide() elif msg == "quit": self.fQuitReceived = True self.d_uiQuit() elif msg == "uiTitle": uiTitle = self.readlineblock().replace("\r", "\n") self.d_uiTitleChanged(uiTitle) else: print("unknown message: \"" + msg + "\"") return True # ------------------------------------------------------------------------------------------------------------ # Main if __name__ == '__main__': # ------------------------------------------------------------- # App initialization app = CarlaApplication("Carla2-Plugin") # ------------------------------------------------------------- # Set-up custom signal handling setUpSignals() # ------------------------------------------------------------- # Init plugin host data gCarla.isControl = False gCarla.isLocal = True gCarla.isPlugin = True gCarla.processMode = ENGINE_PROCESS_MODE_CONTINUOUS_RACK # ------------------------------------------------------------- # Create GUI first gCarla.gui = CarlaMiniW() # ------------------------------------------------------------- # Init plugin host now gCarla.host = PluginHost(gCarla.gui.d_getSampleRate()) initHost("Carla-Plugin") # simulate an engire started callback engineCallback(None, ENGINE_CALLBACK_ENGINE_STARTED, 0, ENGINE_PROCESS_MODE_CONTINUOUS_RACK, ENGINE_TRANSPORT_MODE_PLUGIN, 0.0, "Plugin") # ------------------------------------------------------------- # App-Loop ret = app.exec_() # disable parenting gCarla.host.set_engine_option(ENGINE_OPTION_FRONTEND_WIN_ID, 0, "0") sys.exit(ret)