#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Carla plugin host (plugin UI)
# Copyright (C) 2013 Filipe Coelho <falktx@falktx.com>
#
# 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

# ------------------------------------------------------------------------------------------------------------
# Helper object

class PluginStoreInfo(object):
    __slots__ = [
        'pluginInfo',
        'pluginRealName',
        'audioCountInfo',
        'midiCountInfo',
        'parameterCount',
        'parameterCountInfo',
        'parameterInfoS',
        'parameterDataS',
        'parameterRangeS',
        'parameterValueS',
        'programCount',
        'programCurrent',
        'programNameS',
        'midiProgramCount',
        'midiProgramCurrent',
        'midiProgramDataS',
        'peaks'
    ]

# ------------------------------------------------------------------------------------------------------------
# Host Plugin object

class PluginHost(object):
    def __init__(self, sampleRate):
        object.__init__(self)

        self.fSupportedFileExts = ""
        self.fBufferSize = 0
        self.fSampleRate = sampleRate
        self.fLastError  = ""
        self.fIsRunning  = True

        self.fPluginsInfo = []

    def _add(self, pluginId):
        if len(self.fPluginsInfo) != pluginId:
            return

        info = PluginStoreInfo()
        info.pluginInfo = PyCarlaPluginInfo
        info.pluginRealName = ""
        info.audioCountInfo = PyCarlaPortCountInfo
        info.midiCountInfo  = PyCarlaPortCountInfo
        info.parameterCount = 0
        info.parameterCountInfo = PyCarlaPortCountInfo
        info.parameterInfoS  = []
        info.parameterDataS  = []
        info.parameterRangeS = []
        info.parameterValueS = []
        info.programCount = 0
        info.programCurrent = -1
        info.programNameS = []
        info.midiProgramCount = 0
        info.midiProgramCurrent = -1
        info.midiProgramDataS = []
        info.peaks = [0.0, 0.0, 0.0, 0.0]
        self.fPluginsInfo.append(info)

    def _set_pluginInfo(self, pluginId, info):
        self.fPluginsInfo[pluginId].pluginInfo = info

    def _set_pluginName(self, pluginId, name):
        self.fPluginsInfo[pluginId].pluginInfo['name'] = name

    def _set_pluginRealName(self, pluginId, realName):
        self.fPluginsInfo[pluginId].pluginRealName = realName

    def _set_audioCountInfo(self, pluginId, info):
        self.fPluginsInfo[pluginId].audioCountInfo = info

    def _set_midiCountInfo(self, pluginId, info):
        self.fPluginsInfo[pluginId].midiCountInfo = info

    def _set_parameterCountInfo(self, pluginId, count, info):
        self.fPluginsInfo[pluginId].parameterCount = count
        self.fPluginsInfo[pluginId].parameterCountInfo = info

        # clear
        self.fPluginsInfo[pluginId].parameterInfoS  = []
        self.fPluginsInfo[pluginId].parameterDataS  = []
        self.fPluginsInfo[pluginId].parameterRangeS = []
        self.fPluginsInfo[pluginId].parameterValueS = []

        # add placeholders
        for x in range(count):
            self.fPluginsInfo[pluginId].parameterInfoS.append(PyCarlaParameterInfo)
            self.fPluginsInfo[pluginId].parameterDataS.append(PyParameterData)
            self.fPluginsInfo[pluginId].parameterRangeS.append(PyParameterRanges)
            self.fPluginsInfo[pluginId].parameterValueS.append(0.0)

    def _set_programCount(self, pluginId, count):
        self.fPluginsInfo[pluginId].programCount = count

        # clear
        self.fPluginsInfo[pluginId].programNameS = []

        # add placeholders
        for x in range(count):
            self.fPluginsInfo[pluginId].programNameS.append("")

    def _set_midiProgramCount(self, pluginId, count):
        self.fPluginsInfo[pluginId].midiProgramCount = count

        # clear
        self.fPluginsInfo[pluginId].midiProgramDataS = []

        # add placeholders
        for x in range(count):
            self.fPluginsInfo[pluginId].midiProgramDataS.append(PyMidiProgramData)

    def _set_parameterInfoS(self, pluginId, paramIndex, info):
        if pluginId < len(self.fPluginsInfo) and paramIndex < self.fPluginsInfo[pluginId].parameterCount:
            self.fPluginsInfo[pluginId].parameterInfoS[paramIndex] = info

    def _set_parameterDataS(self, pluginId, paramIndex, data):
        if pluginId < len(self.fPluginsInfo) and paramIndex < self.fPluginsInfo[pluginId].parameterCount:
            self.fPluginsInfo[pluginId].parameterDataS[paramIndex] = data

    def _set_parameterRangeS(self, pluginId, paramIndex, ranges):
        if pluginId < len(self.fPluginsInfo) and paramIndex < self.fPluginsInfo[pluginId].parameterCount:
            self.fPluginsInfo[pluginId].parameterRangeS[paramIndex] = ranges

    def _set_parameterValueS(self, pluginId, paramIndex, value):
        if pluginId < len(self.fPluginsInfo) and paramIndex < self.fPluginsInfo[pluginId].parameterCount:
            self.fPluginsInfo[pluginId].parameterValueS[paramIndex] = value

    def _set_parameterDefault(self, pluginId, paramIndex, value):
        if pluginId < len(self.fPluginsInfo) and paramIndex < self.fPluginsInfo[pluginId].parameterCount:
            self.fPluginsInfo[pluginId].parameterRangeS[paramIndex]['def'] = value

    def _set_parameterMidiChannel(self, pluginId, paramIndex, channel):
        if pluginId < len(self.fPluginsInfo) and paramIndex < self.fPluginsInfo[pluginId].parameterCount:
            self.fPluginsInfo[pluginId].parameterDataS[paramIndex]['midiChannel'] = channel

    def _set_parameterMidiCC(self, pluginId, paramIndex, cc):
        if pluginId < len(self.fPluginsInfo) and paramIndex < self.fPluginsInfo[pluginId].parameterCount:
            self.fPluginsInfo[pluginId].parameterDataS[paramIndex]['midiCC'] = cc

    def _set_currentProgram(self, pluginId, pIndex):
        self.fPluginsInfo[pluginId].programCurrent = pIndex

    def _set_currentMidiProgram(self, pluginId, mpIndex):
        self.fPluginsInfo[pluginId].midiProgramCurrent = mpIndex

    def _set_programNameS(self, pluginId, pIndex, name):
        if pIndex < self.fPluginsInfo[pluginId].programCount:
            self.fPluginsInfo[pluginId].programNameS[pIndex] = name

    def _set_midiProgramDataS(self, pluginId, mpIndex, data):
        if mpIndex < self.fPluginsInfo[pluginId].midiProgramCount:
            self.fPluginsInfo[pluginId].midiProgramDataS[mpIndex] = data

    def _set_peaks(self, pluginId, in1, in2, out1, out2):
        self.fPluginsInfo[pluginId].peaks = [in1, in2, out1, out2]

    # -------------------------------------------------------------------

    def get_complete_license_text(self):
        return ""

    def get_supported_file_extensions(self):
        return self.fSupportedFileExts

    # -------------------------------------------------------------------

    def get_engine_driver_count(self):
        return 1

    def get_engine_driver_name(self, index):
        return "Plugin"

    def get_engine_driver_device_names(self, index):
        return []

    def get_engine_driver_device_info(self, index, name):
        return PyEngineDriverDeviceInfo

    # -------------------------------------------------------------------

    def get_internal_plugin_count(self):
        return 0

    def get_internal_plugin_info(self, index):
        return None

    # -------------------------------------------------------------------

    def engine_init(self, driverName, clientName):
        return True

    def engine_close(self):
        return True

    def engine_idle(self):
        if Carla.gui.idleExternalUI():
            return

        self.fIsRunning = False
        Carla.gui.d_uiQuit()

    def is_engine_running(self):
        return self.fIsRunning

    def set_engine_about_to_close(self):
        pass

    def set_engine_callback(self, func):
        pass

    def set_engine_option(self, option, value, valueStr):
        Carla.gui.send(["set_engine_option", option, value, valueStr])

    # -------------------------------------------------------------------

    def set_file_callback(self, func):
        pass

    def load_file(self, filename):
        Carla.gui.send(["load_file", filename])
        return True

    def load_project(self, filename):
        Carla.gui.send(["load_project", filename])
        return True

    def save_project(self, filename):
        Carla.gui.send(["save_project", filename])
        return True

    # -------------------------------------------------------------------

    def patchbay_connect(self, portIdA, portIdB):
        Carla.gui.send(["patchbay_connect", portIdA, portIdB])
        return True

    def patchbay_disconnect(self, connectionId):
        Carla.gui.send(["patchbay_disconnect", connectionId])
        return True

    def patchbay_refresh(self):
        Carla.gui.send(["patchbay_refresh"])
        return True

    # -------------------------------------------------------------------

    def transport_play(self):
        Carla.gui.send(["transport_play"])

    def transport_pause(self):
        Carla.gui.send(["transport_pause"])

    def transport_relocate(self, frame):
        Carla.gui.send(["transport_relocate"])

    def get_current_transport_frame(self):
        return 0

    def get_transport_info(self):
        return PyCarlaTransportInfo

    # -------------------------------------------------------------------

    def add_plugin(self, btype, ptype, filename, name, label, extraPtr):
        Carla.gui.send(["add_plugin", btype, ptype, filename, name, label])
        return True

    def remove_plugin(self, pluginId):
        Carla.gui.send(["remove_plugin", pluginId])
        return True

    def remove_all_plugins(self):
        Carla.gui.send(["remove_all_plugins"])
        return True

    def rename_plugin(self, pluginId, newName):
        Carla.gui.send(["rename_plugin", pluginId, newName])
        return newName

    def clone_plugin(self, pluginId):
        Carla.gui.send(["clone_plugin", pluginId])
        return True

    def replace_plugin(self, pluginId):
        Carla.gui.send(["replace_plugin", pluginId])
        return True

    def switch_plugins(self, pluginIdA, pluginIdB):
        Carla.gui.send(["switch_plugins", pluginIdA, pluginIdB])
        return True

    # -------------------------------------------------------------------

    def load_plugin_state(self, pluginId, filename):
        Carla.gui.send(["load_plugin_state", pluginId, filename])
        return True

    def save_plugin_state(self, pluginId, filename):
        Carla.gui.send(["save_plugin_state", pluginId, filename])
        return True

    # -------------------------------------------------------------------

    def get_plugin_info(self, pluginId):
        return self.fPluginsInfo[pluginId].pluginInfo

    def get_audio_port_count_info(self, pluginId):
        return self.fPluginsInfo[pluginId].audioCountInfo

    def get_midi_port_count_info(self, pluginId):
        return self.fPluginsInfo[pluginId].midiCountInfo

    def get_parameter_count_info(self, pluginId):
        return self.fPluginsInfo[pluginId].parameterCountInfo

    def get_parameter_info(self, pluginId, parameterId):
        return self.fPluginsInfo[pluginId].parameterInfoS[parameterId]

    def get_parameter_scalepoint_info(self, pluginId, parameterId, scalePointId):
        return PyCarlaScalePointInfo

    # -------------------------------------------------------------------

    def get_parameter_data(self, pluginId, parameterId):
        return self.fPluginsInfo[pluginId].parameterDataS[parameterId]

    def get_parameter_ranges(self, pluginId, parameterId):
        return self.fPluginsInfo[pluginId].parameterRangeS[parameterId]

    def get_midi_program_data(self, pluginId, midiProgramId):
        return self.fPluginsInfo[pluginId].midiProgramDataS[midiProgramId]

    def get_custom_data(self, pluginId, customDataId):
        return PyCustomData

    def get_chunk_data(self, pluginId):
        return ""

    # -------------------------------------------------------------------

    def get_parameter_count(self, pluginId):
        return self.fPluginsInfo[pluginId].parameterCount

    def get_program_count(self, pluginId):
        return self.fPluginsInfo[pluginId].programCount

    def get_midi_program_count(self, pluginId):
        return self.fPluginsInfo[pluginId].midiProgramCount

    def get_custom_data_count(self, pluginId):
        return 0

    # -------------------------------------------------------------------

    def get_parameter_text(self, pluginId, parameterId, value):
        return ""

    def get_program_name(self, pluginId, programId):
        return self.fPluginsInfo[pluginId].programNameS[programId]

    def get_midi_program_name(self, pluginId, midiProgramId):
        return self.fPluginsInfo[pluginId].midiProgramDataS[midiProgramId]['label']

    def get_real_plugin_name(self, pluginId):
        return self.fPluginsInfo[pluginId].pluginRealName

    # -------------------------------------------------------------------

    def get_current_program_index(self, pluginId):
        return self.fPluginsInfo[pluginId].programCurrent

    def get_current_midi_program_index(self, pluginId):
        return self.fPluginsInfo[pluginId].midiProgramCurrent

    def get_default_parameter_value(self, pluginId, parameterId):
        return self.fPluginsInfo[pluginId].parameterRangeS[parameterId]['def']

    def get_current_parameter_value(self, pluginId, parameterId):
        return self.fPluginsInfo[pluginId].parameterValueS[parameterId]

    def get_input_peak_value(self, pluginId, isLeft):
        return self.fPluginsInfo[pluginId].peaks[0 if isLeft else 1]

    def get_output_peak_value(self, pluginId, isLeft):
        return self.fPluginsInfo[pluginId].peaks[2 if isLeft else 3]

    # -------------------------------------------------------------------

    def set_option(self, pluginId, option, yesNo):
        Carla.gui.send(["set_option", pluginId, option, yesNo])

    def set_active(self, pluginId, onOff):
        Carla.gui.send(["set_active", pluginId, onOff])

    def set_drywet(self, pluginId, value):
        Carla.gui.send(["set_drywet", pluginId, value])

    def set_volume(self, pluginId, value):
        Carla.gui.send(["set_volume", pluginId, value])

    def set_balance_left(self, pluginId, value):
        Carla.gui.send(["set_balance_left", pluginId, value])

    def set_balance_right(self, pluginId, value):
        Carla.gui.send(["set_balance_right", pluginId, value])

    def set_panning(self, pluginId, value):
        Carla.gui.send(["set_panning", pluginId, value])

    def set_ctrl_channel(self, pluginId, channel):
        Carla.gui.send(["set_ctrl_channel", pluginId, channel])

    # -------------------------------------------------------------------

    def set_parameter_value(self, pluginId, parameterId, value):
        Carla.gui.send(["set_parameter_value", pluginId, parameterId, value])

    def set_parameter_midi_channel(self, pluginId, parameterId, channel):
        Carla.gui.send(["set_parameter_midi_channel", pluginId, parameterId, channel])

    def set_parameter_midi_cc(self, pluginId, parameterId, cc):
        Carla.gui.send(["set_parameter_midi_cc", pluginId, parameterId, cc])

    def set_program(self, pluginId, programId):
        Carla.gui.send(["set_program", pluginId, programId])

    def set_midi_program(self, pluginId, midiProgramId):
        Carla.gui.send(["set_midi_program", pluginId, midiProgramId])

    def set_custom_data(self, pluginId, type_, key, value):
        Carla.gui.send(["set_custom_data", pluginId, type_, key, value])

    def set_chunk_data(self, pluginId, chunkData):
        Carla.gui.send(["set_chunk_data", pluginId, chunkData])

    # -------------------------------------------------------------------

    def prepare_for_save(self, pluginId):
        Carla.gui.send(["prepare_for_save", pluginId])

    def send_midi_note(self, pluginId, channel, note, velocity):
        Carla.gui.send(["send_midi_note", pluginId, channel, note, velocity])

    def show_custom_ui(self, pluginId, yesNo):
        Carla.gui.send(["show_custom_ui", pluginId, yesNo])

    # -------------------------------------------------------------------

    def get_buffer_size(self):
        return self.fBufferSize

    def get_sample_rate(self):
        return self.fSampleRate

    def get_last_error(self):
        return self.fLastError

    def get_host_osc_url_tcp(self):
        return ""

    def get_host_osc_url_udp(self):
        return ""

# ------------------------------------------------------------------------------------------------------------
# Main Window

class CarlaMiniW(HostWindow, ExternalUI):
    def __init__(self):
        HostWindow.__init__(self, None)
        ExternalUI.__init__(self)

        if False:
            from carla_patchbay import CarlaPatchbayW
            self.fContainer = CarlaPatchbayW(self)
        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.fPipeRecv.readline().strip().split(":")]
                Carla.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.fPipeRecv.readline().strip())
                Carla.host._set_parameterValueS(pluginId, paramId, paramValue)

            elif msg.startswith("ENGINE_CALLBACK_"):
                action   = int(msg.replace("ENGINE_CALLBACK_", ""))
                pluginId = int(self.fPipeRecv.readline().strip())
                value1   = int(self.fPipeRecv.readline().strip())
                value2   = int(self.fPipeRecv.readline().strip())
                value3   = float(self.fPipeRecv.readline().strip())
                valueStr = self.fPipeRecv.readline().strip().replace("\r", "\n")

                if action == ENGINE_CALLBACK_PLUGIN_RENAMED:
                    Carla.host._set_pluginName(pluginId, valueStr)
                elif action == ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED:
                    Carla.host._set_parameterValueS(pluginId, value1, value3)
                elif action == ENGINE_CALLBACK_PARAMETER_DEFAULT_CHANGED:
                    Carla.host._set_parameterDefault(pluginId, value1, value3)
                elif action == ENGINE_CALLBACK_PARAMETER_MIDI_CC_CHANGED:
                    Carla.host._set_parameterMidiCC(pluginId, value1, value2)
                elif action == ENGINE_CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED:
                    Carla.host._set_parameterMidiChannel(pluginId, value1, value2)
                elif action == ENGINE_CALLBACK_PROGRAM_CHANGED:
                    Carla.host._set_currentProgram(pluginId, value1)
                elif action == ENGINE_CALLBACK_MIDI_PROGRAM_CHANGED:
                    Carla.host._set_currentMidiProgram(pluginId, value1)

                engineCallback(None, action, pluginId, value1, value2, value3, valueStr)

            elif msg.startswith("PLUGIN_INFO_"):
                pluginId = int(msg.replace("PLUGIN_INFO_", ""))
                Carla.host._add(pluginId)

                type_, category, hints, uniqueId, optsAvail, optsEnabled = [int(i) for i in self.fPipeRecv.readline().strip().split(":")]
                filename  = self.fPipeRecv.readline().strip().replace("\r", "\n")
                name      = self.fPipeRecv.readline().strip().replace("\r", "\n")
                iconName  = self.fPipeRecv.readline().strip().replace("\r", "\n")
                realName  = self.fPipeRecv.readline().strip().replace("\r", "\n")
                label     = self.fPipeRecv.readline().strip().replace("\r", "\n")
                maker     = self.fPipeRecv.readline().strip().replace("\r", "\n")
                copyright = self.fPipeRecv.readline().strip().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
                }
                Carla.host._set_pluginInfo(pluginId, pinfo)
                Carla.host._set_pluginRealName(pluginId, realName)

            elif msg.startswith("AUDIO_COUNT_"):
                pluginId, ins, outs = [int(i) for i in msg.replace("AUDIO_COUNT_", "").split(":")]
                Carla.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(":")]
                Carla.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(":")]
                Carla.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.fPipeRecv.readline().strip().split(":")]
                paramName = self.fPipeRecv.readline().strip().replace("\r", "\n")
                paramUnit = self.fPipeRecv.readline().strip().replace("\r", "\n")

                paramInfo = {
                    'name': paramName,
                    'symbol': "",
                    'unit': paramUnit,
                    'scalePointCount': 0,
                }
                Carla.host._set_parameterInfoS(pluginId, paramId, paramInfo)

                paramData = {
                    'type': paramType,
                    'hints': paramHints,
                    'index': paramId,
                    'rindex': -1,
                    'midiCC': midiCC,
                    'midiChannel': midiChannel
                }
                Carla.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.fPipeRecv.readline().strip().split(":")]

                paramRanges = {
                    'def': def_,
                    'min': min_,
                    'max': max_,
                    'step': step,
                    'stepSmall': stepSmall,
                    'stepLarge': stepLarge
                }
                Carla.host._set_parameterRangeS(pluginId, paramId, paramRanges)

            elif msg.startswith("PROGRAM_COUNT_"):
                pluginId, count, current = [int(i) for i in msg.replace("PROGRAM_COUNT_", "").split(":")]
                Carla.host._set_programCount(pluginId, count)
                Carla.host._set_currentProgram(pluginId, current)

            elif msg.startswith("PROGRAM_NAME_"):
                pluginId, progId = [int(i) for i in msg.replace("PROGRAM_NAME_", "").split(":")]
                progName = self.fPipeRecv.readline().strip().replace("\r", "\n")
                Carla.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(":")]
                Carla.host._set_midiProgramCount(pluginId, count)
                Carla.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.fPipeRecv.readline().strip().split(":")]
                name = self.fPipeRecv.readline().strip().replace("\r", "\n")
                Carla.host._set_midiProgramDataS(pluginId, midiProgId, {'bank': bank, 'program': program, 'name': name})

            elif msg == "error":
                error = self.fPipeRecv.readline().strip().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.fPipeRecv.readline().strip().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

    Carla.isControl = False
    Carla.isLocal   = True
    Carla.isPlugin  = True

    # -------------------------------------------------------------
    # Create GUI first

    Carla.gui = CarlaMiniW()

    # -------------------------------------------------------------
    # Init plugin host now

    Carla.host = PluginHost(Carla.gui.d_getSampleRate())

    initHost("Carla-Plugin")

    # set our gui as parent for all plugins UIs
    Carla.host.set_engine_option(ENGINE_OPTION_FRONTEND_WIN_ID, 0, str(Carla.gui.winId()))

    # 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
    Carla.host.set_engine_option(ENGINE_OPTION_FRONTEND_WIN_ID, 0, "0")

    sys.exit(ret)
