| @@ -0,0 +1,5 @@ | |||
| The pixmaps in this folder were taken from the following projects: | |||
| dial_01 - TAL Plugins | |||
| dial_02 - TAL Plugins | |||
| kbd_* - FL Studio | |||
| @@ -0,0 +1,33 @@ | |||
| #!/usr/bin/make -f | |||
| # Makefile for carla backend # | |||
| # ----------------------------------------- # | |||
| # Created by falkTX | |||
| # | |||
| all: | |||
| # -------------------------------------------------------------- | |||
| # carla_backend.so: carla_engine.so carla_native.so carla_plugin.so carla_standalone.so | |||
| # $(CXX) $^ | |||
| carla_engine.so: | |||
| $(MAKE) -C engine | |||
| carla_native.so: | |||
| $(MAKE) -C native | |||
| carla_plugin.so: | |||
| $(MAKE) -C plugin | |||
| carla_standalone.so: | |||
| $(MAKE) -C standalone | |||
| # -------------------------------------------------------------- | |||
| clean: | |||
| rm -f *.so | |||
| $(MAKE) clean -C engine | |||
| $(MAKE) clean -C native | |||
| $(MAKE) clean -C plugin | |||
| $(MAKE) clean -C standalone | |||
| @@ -0,0 +1,35 @@ | |||
| /* | |||
| * Carla Plugin Engine | |||
| * Copyright (C) 2012 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 COPYING file | |||
| */ | |||
| #ifndef __DISTRHO_PLUGIN_INFO_H__ | |||
| #define __DISTRHO_PLUGIN_INFO_H__ | |||
| #define DISTRHO_PLUGIN_NAME "Carla" | |||
| #define DISTRHO_PLUGIN_HAS_UI 0 | |||
| #define DISTRHO_PLUGIN_IS_SYNTH 1 | |||
| #define DISTRHO_PLUGIN_NUM_INPUTS 2 | |||
| #define DISTRHO_PLUGIN_NUM_OUTPUTS 2 | |||
| #define DISTRHO_PLUGIN_WANT_LATENCY 1 | |||
| #define DISTRHO_PLUGIN_WANT_PROGRAMS 1 | |||
| #define DISTRHO_PLUGIN_WANT_STATE 1 | |||
| #define DISTRHO_PLUGIN_URI "http://kxstudio.sf.net/carla" | |||
| #endif // __DISTRHO_PLUGIN_INFO_H__ | |||
| @@ -0,0 +1,52 @@ | |||
| # QtCreator project file | |||
| QT = core gui | |||
| CONFIG = debug | |||
| CONFIG += link_pkgconfig qt shared warn_on | |||
| DEFINES = DEBUG | |||
| DEFINES += QTCREATOR_TEST | |||
| # ZynAddSubFX | |||
| DEFINES += WANT_ZYNADDSUBFX | |||
| PKGCONFIG = fftw3 mxml | |||
| TARGET = carla_native | |||
| TEMPLATE = lib | |||
| VERSION = 0.5.0 | |||
| SOURCES = \ | |||
| bypass.c \ | |||
| midi-split.c \ | |||
| midi-through.c \ | |||
| zynaddsubfx.cpp \ | |||
| zynaddsubfx-src.cpp | |||
| SOURCES += \ | |||
| distrho-3bandeq.cpp | |||
| HEADERS = \ | |||
| ../carla_native.h \ | |||
| ../carla_native.hpp | |||
| HEADERS += \ | |||
| distrho/DistrhoPluginCarla.cpp | |||
| HEADERS += \ | |||
| ../../includes/carla_defines.hpp \ | |||
| ../../includes/carla_midi.h \ | |||
| ../../utils/carla_utils.hpp \ | |||
| ../../utils/carla_juce_utils.hpp | |||
| INCLUDEPATH = . .. \ | |||
| 3bandeq distrho \ | |||
| ../../includes \ | |||
| ../../utils \ | |||
| ../../libs/distrho-plugin-toolkit | |||
| LIBS = -lGL -lpthread | |||
| QMAKE_CFLAGS *= -std=c99 | |||
| QMAKE_CXXFLAGS *= -std=c++0x | |||
| @@ -0,0 +1,61 @@ | |||
| #!/usr/bin/make -f | |||
| # Makefile for carla-plugin # | |||
| # ------------------------------------ # | |||
| # Created by falkTX | |||
| # | |||
| include ../../Makefile.mk | |||
| # -------------------------------------------------------------- | |||
| BUILD_CXX_FLAGS += -fvisibility=hidden -fPIC -I. -I.. -I../../includes -I../../libs -I../../utils | |||
| BUILD_CXX_FLAGS += $(shell pkg-config --cflags liblo QtCore QtGui) | |||
| LINK_FLAGS += -shared | |||
| LINK_FLAGS += $(shell pkg-config --libs liblo QtCore QtGui) | |||
| ifeq ($(HAVE_SUIL),true) | |||
| BUILD_CXX_FLAGS += $(shell pkg-config --cflags suil-0) -DWANT_SUIL | |||
| LINK_FLAGS += $(shell pkg-config --libs suil-0) | |||
| endif | |||
| ifeq ($(HAVE_FLUIDSYNTH),true) | |||
| BUILD_CXX_FLAGS += $(shell pkg-config --cflags fluidsynth) -DWANT_FLUIDSYNTH | |||
| LINK_FLAGS += $(shell pkg-config --libs fluidsynth) | |||
| endif | |||
| ifeq ($(HAVE_LINUXSAMPLER),true) | |||
| BUILD_CXX_FLAGS += $(shell pkg-config --cflags linuxsampler) -DWANT_LINUXSAMPLER | |||
| LINK_FLAGS += $(shell pkg-config --libs linuxsampler) | |||
| endif | |||
| # -------------------------------------------------------------- | |||
| OBJS = \ | |||
| carla_plugin.cpp.o | |||
| # carla_plugin_thread.cpp.o \ | |||
| # carla_bridge.cpp.o \ | |||
| # native.cpp.o \ | |||
| # ladspa.cpp.o \ | |||
| # dssi.cpp.o \ | |||
| # lv2.cpp.o \ | |||
| # vst.cpp.o \ | |||
| # fluidsynth.cpp.o \ | |||
| # linuxsampler.cpp.o | |||
| TARGET = ../carla_plugin.so | |||
| # -------------------------------------------------------------- | |||
| all: $(TARGET) | |||
| clean: | |||
| rm -f $(OBJS) $(TARGET) | |||
| # -------------------------------------------------------------- | |||
| %.cpp.o: %.cpp | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||
| $(TARGET): $(OBJS) | |||
| $(CXX) $^ $(LINK_FLAGS) -o $@ | |||
| @@ -0,0 +1,60 @@ | |||
| # QtCreator project file | |||
| QT = core gui | |||
| CONFIG = debug | |||
| CONFIG += link_pkgconfig qt shared warn_on | |||
| DEFINES = DEBUG | |||
| DEFINES += QTCREATOR_TEST | |||
| # Plugins | |||
| DEFINES += WANT_LADSPA WANT_DSSI WANT_LV2 WANT_VST | |||
| # Samplers | |||
| DEFINES += WANT_FLUIDSYNTH WANT_LINUXSAMPLER | |||
| # ZynAddSubFX | |||
| DEFINES += WANT_ZYNADDSUBFX | |||
| # Misc | |||
| DEFINES += WANT_SUIL | |||
| PKGCONFIG = liblo suil-0 fluidsynth linuxsampler | |||
| TARGET = carla_plugin | |||
| TEMPLATE = lib | |||
| VERSION = 0.5.0 | |||
| SOURCES = \ | |||
| carla_plugin.cpp | |||
| # carla_plugin_thread.cpp \ | |||
| # carla_bridge.cpp \ | |||
| # native.cpp \ | |||
| # ladspa.cpp \ | |||
| # dssi.cpp \ | |||
| # lv2.cpp \ | |||
| # vst.cpp \ | |||
| # fluidsynth.cpp \ | |||
| # linuxsampler.cpp | |||
| HEADERS = \ | |||
| carla_plugin_internal.hpp \ | |||
| carla_plugin_thread.hpp | |||
| HEADERS += \ | |||
| ../carla_backend.hpp \ | |||
| ../carla_engine.hpp \ | |||
| ../carla_native.h \ | |||
| ../carla_plugin.hpp | |||
| INCLUDEPATH = . .. \ | |||
| ../../includes \ | |||
| ../../libs \ | |||
| ../../utils | |||
| # FIXME | |||
| INCLUDEPATH += \ | |||
| /opt/kxstudio/include | |||
| QMAKE_CXXFLAGS *= -std=c++0x | |||
| @@ -0,0 +1,577 @@ | |||
| /* | |||
| * Carla Plugin | |||
| * Copyright (C) 2011-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 | |||
| */ | |||
| #ifndef __CARLA_PLUGIN_INTERNAL_HPP__ | |||
| #define __CARLA_PLUGIN_INTERNAL_HPP__ | |||
| #include "carla_plugin.hpp" | |||
| #include "carla_plugin_thread.hpp" | |||
| #include "carla_engine.hpp" | |||
| #ifdef BUILD_BRIDGE | |||
| # include "carla_bridge_osc.hpp" | |||
| #else | |||
| # include "carla_osc_utils.hpp" | |||
| #endif | |||
| #include "rt_list.hpp" | |||
| #define CARLA_DECLARE_NON_COPY_STRUCT(structName) \ | |||
| structName(const structName&) = delete; | |||
| #define CARLA_DECLARE_NON_COPY_STRUCT_WITH_LEAK_DETECTOR(structName) \ | |||
| CARLA_DECLARE_NON_COPY_STRUCT(structName) \ | |||
| CARLA_LEAK_DETECTOR(structName) | |||
| #define CARLA_PROCESS_CONTINUE_CHECK if (! fData->enabled) { fData->engine->callback(CALLBACK_DEBUG, fData->id, 0, 0, 0.0, nullptr); return; } | |||
| CARLA_BACKEND_START_NAMESPACE | |||
| // ----------------------------------------------------------------------- | |||
| struct PluginAudioPort { | |||
| uint32_t rindex; | |||
| CarlaEngineAudioPort* port; | |||
| PluginAudioPort() | |||
| : rindex(0), | |||
| port(nullptr) {} | |||
| ~PluginAudioPort() | |||
| { | |||
| CARLA_ASSERT(port == nullptr); | |||
| } | |||
| CARLA_DECLARE_NON_COPY_STRUCT_WITH_LEAK_DETECTOR(PluginAudioPort) | |||
| }; | |||
| struct PluginAudioData { | |||
| uint32_t count; | |||
| PluginAudioPort* ports; | |||
| PluginAudioData() | |||
| : count(0), | |||
| ports(nullptr) {} | |||
| ~PluginAudioData() | |||
| { | |||
| CARLA_ASSERT(ports == nullptr); | |||
| } | |||
| void createNew(const size_t count) | |||
| { | |||
| CARLA_ASSERT(ports == nullptr); | |||
| if (ports == nullptr) | |||
| ports = new PluginAudioPort[count]; | |||
| } | |||
| void free() | |||
| { | |||
| CARLA_ASSERT(ports != nullptr); | |||
| if (ports != nullptr) | |||
| { | |||
| delete[] ports; | |||
| ports = nullptr; | |||
| } | |||
| } | |||
| CARLA_DECLARE_NON_COPY_STRUCT_WITH_LEAK_DETECTOR(PluginAudioData) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| struct PluginEventData { | |||
| CarlaEngineEventPort* portIn; | |||
| CarlaEngineEventPort* portOut; | |||
| PluginEventData() | |||
| : portIn(nullptr), | |||
| portOut(nullptr) {} | |||
| ~PluginEventData() | |||
| { | |||
| CARLA_ASSERT(portIn == nullptr); | |||
| CARLA_ASSERT(portOut == nullptr); | |||
| } | |||
| CARLA_DECLARE_NON_COPY_STRUCT_WITH_LEAK_DETECTOR(PluginEventData) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| struct PluginParameterData { | |||
| uint32_t count; | |||
| ParameterData* data; | |||
| ParameterRanges* ranges; | |||
| PluginParameterData() | |||
| : count(0), | |||
| data(nullptr), | |||
| ranges(nullptr) {} | |||
| ~PluginParameterData() | |||
| { | |||
| CARLA_ASSERT(data == nullptr); | |||
| CARLA_ASSERT(ranges == nullptr); | |||
| } | |||
| void createNew(const size_t count) | |||
| { | |||
| CARLA_ASSERT(data == nullptr); | |||
| CARLA_ASSERT(ranges == nullptr); | |||
| if (data == nullptr) | |||
| data = new ParameterData[count]; | |||
| if (ranges == nullptr) | |||
| ranges = new ParameterRanges[count]; | |||
| } | |||
| void free() | |||
| { | |||
| CARLA_ASSERT(data != nullptr); | |||
| CARLA_ASSERT(ranges != nullptr); | |||
| if (data != nullptr) | |||
| { | |||
| delete[] data; | |||
| data = nullptr; | |||
| } | |||
| if (ranges != nullptr) | |||
| { | |||
| delete[] ranges; | |||
| ranges = nullptr; | |||
| } | |||
| } | |||
| CARLA_DECLARE_NON_COPY_STRUCT_WITH_LEAK_DETECTOR(PluginParameterData) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| typedef const char* ProgramName; | |||
| struct PluginProgramData { | |||
| uint32_t count; | |||
| int32_t current; | |||
| ProgramName* names; | |||
| PluginProgramData() | |||
| : count(0), | |||
| current(-1), | |||
| names(nullptr) {} | |||
| ~PluginProgramData() | |||
| { | |||
| CARLA_ASSERT(names == nullptr); | |||
| } | |||
| void createNew(const size_t count) | |||
| { | |||
| CARLA_ASSERT(names == nullptr); | |||
| if (names == nullptr) | |||
| names = new ProgramName[count]; | |||
| } | |||
| void free() | |||
| { | |||
| CARLA_ASSERT(names != nullptr); | |||
| if (names != nullptr) | |||
| { | |||
| delete[] names; | |||
| names = nullptr; | |||
| } | |||
| } | |||
| CARLA_DECLARE_NON_COPY_STRUCT_WITH_LEAK_DETECTOR(PluginProgramData) | |||
| }; | |||
| struct PluginMidiProgramData { | |||
| uint32_t count; | |||
| int32_t current; | |||
| MidiProgramData* data; | |||
| PluginMidiProgramData() | |||
| : count(0), | |||
| current(-1), | |||
| data(nullptr) {} | |||
| ~PluginMidiProgramData() | |||
| { | |||
| CARLA_ASSERT(data == nullptr); | |||
| } | |||
| void createNew(const size_t count) | |||
| { | |||
| CARLA_ASSERT(data == nullptr); | |||
| if (data == nullptr) | |||
| data = new MidiProgramData[count]; | |||
| } | |||
| void free() | |||
| { | |||
| CARLA_ASSERT(data != nullptr); | |||
| if (data != nullptr) | |||
| { | |||
| delete[] data; | |||
| data = nullptr; | |||
| } | |||
| } | |||
| const MidiProgramData& getCurrent() | |||
| { | |||
| return data[current]; | |||
| } | |||
| CARLA_DECLARE_NON_COPY_STRUCT_WITH_LEAK_DETECTOR(PluginMidiProgramData) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| struct PluginPostRtEvent { | |||
| PluginPostRtEventType type; | |||
| int32_t value1; | |||
| int32_t value2; | |||
| double value3; | |||
| PluginPostRtEvent() | |||
| : type(kPluginPostRtEventNull), | |||
| value1(-1), | |||
| value2(-1), | |||
| value3(0.0) {} | |||
| CARLA_DECLARE_NON_COPY_STRUCT(PluginPostRtEvent) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| struct ExternalMidiNote { | |||
| int8_t channel; // invalid = -1 | |||
| uint8_t note; | |||
| uint8_t velo; | |||
| ExternalMidiNote() | |||
| : channel(-1), | |||
| note(0), | |||
| velo(0) {} | |||
| CARLA_DECLARE_NON_COPY_STRUCT(ExternalMidiNote) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| const unsigned int PLUGIN_OPTION2_HAS_MIDI_IN = 0x1; | |||
| const unsigned int PLUGIN_OPTION2_HAS_MIDI_OUT = 0x2; | |||
| struct CarlaPluginProtectedData { | |||
| unsigned short id; | |||
| CarlaEngine* const engine; | |||
| CarlaEngineClient* client; | |||
| unsigned int hints; | |||
| unsigned int options; | |||
| unsigned int options2; | |||
| bool active; | |||
| bool activeBefore; | |||
| bool enabled; | |||
| void* lib; | |||
| const char* name; | |||
| const char* filename; | |||
| // misc | |||
| int8_t ctrlInChannel; | |||
| uint32_t latency; | |||
| float** latencyBuffers; | |||
| // data | |||
| PluginAudioData audioIn; | |||
| PluginAudioData audioOut; | |||
| PluginEventData event; | |||
| PluginParameterData param; | |||
| PluginProgramData prog; | |||
| PluginMidiProgramData midiprog; | |||
| NonRtList<CustomData> custom; | |||
| struct ExternalNotes { | |||
| CarlaMutex mutex; | |||
| RtList<ExternalMidiNote> data; | |||
| ExternalNotes() | |||
| : data(32, 512) {} | |||
| } extNotes; | |||
| struct PostRtEvents { | |||
| CarlaMutex mutex; | |||
| RtList<PluginPostRtEvent> data; | |||
| PostRtEvents() | |||
| : data(152, 512) {} | |||
| void append(const PluginPostRtEvent& event) | |||
| { | |||
| data.append(event); | |||
| } | |||
| } postRtEvents; | |||
| struct PostProc { | |||
| double dryWet; | |||
| double volume; | |||
| double balanceLeft; | |||
| double balanceRight; | |||
| double panning; | |||
| PostProc() | |||
| : dryWet(1.0), | |||
| volume(1.0), | |||
| balanceLeft(-1.0), | |||
| balanceRight(1.0), | |||
| panning(0.0) {} | |||
| } postProc; | |||
| struct OSC { | |||
| CarlaOscData data; | |||
| CarlaPluginThread* thread; | |||
| OSC() | |||
| : thread(nullptr) {} | |||
| } osc; | |||
| CarlaPluginProtectedData(CarlaEngine* const engine_, const unsigned short id_) | |||
| : id(id_), | |||
| engine(engine_), | |||
| client(nullptr), | |||
| hints(0x0), | |||
| options(0x0), | |||
| options2(0x0), | |||
| active(false), | |||
| activeBefore(false), | |||
| enabled(false), | |||
| lib(nullptr), | |||
| name(nullptr), | |||
| filename(nullptr), | |||
| ctrlInChannel(-1), | |||
| latency(0), | |||
| latencyBuffers(nullptr) {} | |||
| CarlaPluginProtectedData() = delete; | |||
| CARLA_LEAK_DETECTOR(CarlaPluginProtectedData) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| CARLA_BACKEND_END_NAMESPACE | |||
| #endif // __CARLA_PLUGIN_INTERNAL_HPP__ | |||
| // common includes | |||
| //#include <cmath> | |||
| //#include <vector> | |||
| //#include <QtCore/QMutex> | |||
| //#include <QtGui/QMainWindow> | |||
| //#ifdef Q_WS_X11 | |||
| //# include <QtGui/QX11EmbedContainer> | |||
| //typedef QX11EmbedContainer GuiContainer; | |||
| //#else | |||
| //# include <QtGui/QWidget> | |||
| //typedef QWidget GuiContainer; | |||
| //#endif | |||
| #if 0 | |||
| // ------------------------------------------------------------------- | |||
| // Extra | |||
| ExternalMidiNote extMidiNotes[MAX_MIDI_EVENTS]; | |||
| // ------------------------------------------------------------------- | |||
| // Utilities | |||
| static double fixParameterValue(double& value, const ParameterRanges& ranges) | |||
| { | |||
| if (value < ranges.min) | |||
| value = ranges.min; | |||
| else if (value > ranges.max) | |||
| value = ranges.max; | |||
| return value; | |||
| } | |||
| static float fixParameterValue(float& value, const ParameterRanges& ranges) | |||
| { | |||
| if (value < ranges.min) | |||
| value = ranges.min; | |||
| else if (value > ranges.max) | |||
| value = ranges.max; | |||
| return value; | |||
| } | |||
| friend class CarlaEngine; // FIXME | |||
| friend class CarlaEngineJack; | |||
| #endif | |||
| #if 0 | |||
| // ------------------------------------------------------------------- | |||
| /*! | |||
| * \class ScopedDisabler | |||
| * | |||
| * \brief Carla plugin scoped disabler | |||
| * | |||
| * This is a handy class that temporarily disables a plugin during a function scope.\n | |||
| * It should be used when the plugin needs reload or state change, something like this: | |||
| * \code | |||
| * { | |||
| * const CarlaPlugin::ScopedDisabler m(plugin); | |||
| * plugin->setChunkData(data); | |||
| * } | |||
| * \endcode | |||
| */ | |||
| class ScopedDisabler | |||
| { | |||
| public: | |||
| /*! | |||
| * Disable plugin \a plugin if \a disable is true. | |||
| * The plugin is re-enabled in the deconstructor of this class if \a disable is true. | |||
| * | |||
| * \param plugin The plugin to disable | |||
| * \param disable Wherever to disable the plugin or not, true by default | |||
| */ | |||
| ScopedDisabler(CarlaPlugin* const plugin, const bool disable = true) | |||
| : m_plugin(plugin), | |||
| m_disable(disable) | |||
| { | |||
| if (m_disable) | |||
| { | |||
| m_plugin->engineProcessLock(); | |||
| m_plugin->setEnabled(false); | |||
| m_plugin->engineProcessUnlock(); | |||
| } | |||
| } | |||
| ~ScopedDisabler() | |||
| { | |||
| if (m_disable) | |||
| { | |||
| m_plugin->engineProcessLock(); | |||
| m_plugin->setEnabled(true); | |||
| m_plugin->engineProcessUnlock(); | |||
| } | |||
| } | |||
| private: | |||
| CarlaPlugin* const m_plugin; | |||
| const bool m_disable; | |||
| }; | |||
| /*! | |||
| * \class CarlaPluginGUI | |||
| * | |||
| * \brief Carla Backend gui plugin class | |||
| * | |||
| * \see CarlaPlugin | |||
| */ | |||
| class CarlaPluginGUI : public QMainWindow | |||
| { | |||
| public: | |||
| /*! | |||
| * \class Callback | |||
| * | |||
| * \brief Carla plugin GUI callback | |||
| */ | |||
| class Callback | |||
| { | |||
| public: | |||
| virtual ~Callback() {} | |||
| virtual void guiClosedCallback() = 0; | |||
| }; | |||
| // ------------------------------------------------------------------- | |||
| // Constructor and destructor | |||
| /*! | |||
| * TODO | |||
| */ | |||
| CarlaPluginGUI(QWidget* const parent, Callback* const callback); | |||
| /*! | |||
| * TODO | |||
| */ | |||
| ~CarlaPluginGUI(); | |||
| // ------------------------------------------------------------------- | |||
| // Get data | |||
| /*! | |||
| * TODO | |||
| */ | |||
| GuiContainer* getContainer() const; | |||
| /*! | |||
| * TODO | |||
| */ | |||
| WId getWinId() const; | |||
| // ------------------------------------------------------------------- | |||
| // Set data | |||
| /*! | |||
| * TODO | |||
| */ | |||
| void setNewSize(const int width, const int height); | |||
| /*! | |||
| * TODO | |||
| */ | |||
| void setResizable(const bool resizable); | |||
| /*! | |||
| * TODO | |||
| */ | |||
| void setTitle(const char* const title); | |||
| /*! | |||
| * TODO | |||
| */ | |||
| void setVisible(const bool yesNo); | |||
| // ------------------------------------------------------------------- | |||
| private: | |||
| Callback* const m_callback; | |||
| GuiContainer* m_container; | |||
| QByteArray m_geometry; | |||
| bool m_resizable; | |||
| void hideEvent(QHideEvent* const event); | |||
| void closeEvent(QCloseEvent* const event); | |||
| }; | |||
| #endif | |||
| @@ -0,0 +1,164 @@ | |||
| /* | |||
| * Carla Plugin | |||
| * Copyright (C) 2012 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 COPYING file | |||
| */ | |||
| #include "carla_plugin.hpp" | |||
| #include <QtCore/QProcess> | |||
| CARLA_BACKEND_START_NAMESPACE | |||
| const char* PluginThreadMode2str(const CarlaPluginThread::PluginThreadMode mode) | |||
| { | |||
| switch (mode) | |||
| { | |||
| case CarlaPluginThread::PLUGIN_THREAD_DSSI_GUI: | |||
| return "PLUGIN_THREAD_DSSI_GUI"; | |||
| case CarlaPluginThread::PLUGIN_THREAD_LV2_GUI: | |||
| return "PLUGIN_THREAD_LV2_GUI"; | |||
| case CarlaPluginThread::PLUGIN_THREAD_VST_GUI: | |||
| return "PLUGIN_THREAD_VST_GUI"; | |||
| case CarlaPluginThread::PLUGIN_THREAD_BRIDGE: | |||
| return "PLUGIN_THREAD_BRIDGE"; | |||
| } | |||
| qWarning("CarlaPluginThread::PluginThreadMode2str(%i) - invalid mode", mode); | |||
| return nullptr; | |||
| } | |||
| CarlaPluginThread::CarlaPluginThread(CarlaBackend::CarlaEngine* const engine_, CarlaBackend::CarlaPlugin* const plugin_, const PluginThreadMode mode_, QObject* const parent) | |||
| : QThread(parent), | |||
| engine(engine_), | |||
| plugin(plugin_), | |||
| mode(mode_) | |||
| { | |||
| qDebug("CarlaPluginThread::CarlaPluginThread(plugin:\"%s\", engine:\"%s\", %s)", plugin->name(), engine->getName(), PluginThreadMode2str(mode)); | |||
| m_process = nullptr; | |||
| } | |||
| CarlaPluginThread::~CarlaPluginThread() | |||
| { | |||
| if (m_process) | |||
| delete m_process; | |||
| } | |||
| void CarlaPluginThread::setOscData(const char* const binary, const char* const label, const char* const data1) | |||
| { | |||
| m_binary = QString(binary); | |||
| m_label = QString(label); | |||
| m_data1 = QString(data1); | |||
| } | |||
| void CarlaPluginThread::run() | |||
| { | |||
| qDebug("CarlaPluginThread::run()"); | |||
| if (! m_process) | |||
| { | |||
| m_process = new QProcess(nullptr); | |||
| m_process->setProcessChannelMode(QProcess::ForwardedChannels); | |||
| #ifndef BUILD_BRIDGE | |||
| m_process->setProcessEnvironment(engine->getOptionsAsProcessEnvironment()); | |||
| #endif | |||
| } | |||
| QString name(plugin->name() ? plugin->name() : "(none)"); | |||
| QStringList arguments; | |||
| switch (mode) | |||
| { | |||
| case PLUGIN_THREAD_DSSI_GUI: | |||
| /* osc_url */ arguments << QString("%1/%2").arg(engine->getOscServerPathUDP()).arg(plugin->id()); | |||
| /* filename */ arguments << plugin->filename(); | |||
| /* label */ arguments << m_label; | |||
| /* ui-title */ arguments << QString("%1 (GUI)").arg(plugin->name()); | |||
| break; | |||
| case PLUGIN_THREAD_LV2_GUI: | |||
| /* osc_url */ arguments << QString("%1/%2").arg(engine->getOscServerPathTCP()).arg(plugin->id()); | |||
| /* URI */ arguments << m_label; | |||
| /* ui-URI */ arguments << m_data1; | |||
| /* ui-title */ arguments << QString("%1 (GUI)").arg(plugin->name()); | |||
| break; | |||
| case PLUGIN_THREAD_VST_GUI: | |||
| /* osc_url */ arguments << QString("%1/%2").arg(engine->getOscServerPathTCP()).arg(plugin->id()); | |||
| /* filename */ arguments << plugin->filename(); | |||
| /* ui-title */ arguments << QString("%1 (GUI)").arg(plugin->name()); | |||
| break; | |||
| case PLUGIN_THREAD_BRIDGE: | |||
| /* osc_url */ arguments << QString("%1/%2").arg(engine->getOscServerPathTCP()).arg(plugin->id()); | |||
| /* stype */ arguments << m_data1; | |||
| /* filename */ arguments << plugin->filename(); | |||
| /* name */ arguments << name; | |||
| /* label */ arguments << m_label; | |||
| break; | |||
| } | |||
| m_process->start(m_binary, arguments); | |||
| m_process->waitForStarted(); | |||
| switch (mode) | |||
| { | |||
| case PLUGIN_THREAD_DSSI_GUI: | |||
| case PLUGIN_THREAD_LV2_GUI: | |||
| case PLUGIN_THREAD_VST_GUI: | |||
| if (plugin->waitForOscGuiShow()) | |||
| { | |||
| m_process->waitForFinished(-1); | |||
| if (m_process->exitCode() == 0) | |||
| { | |||
| // Hide | |||
| engine->callback(CarlaBackend::CALLBACK_SHOW_GUI, plugin->id(), 0, 0, 0.0, nullptr); | |||
| qWarning("CarlaPluginThread::run() - GUI closed"); | |||
| } | |||
| else | |||
| { | |||
| // Kill | |||
| engine->callback(CarlaBackend::CALLBACK_SHOW_GUI, plugin->id(), -1, 0, 0.0, nullptr); | |||
| qWarning("CarlaPluginThread::run() - GUI crashed"); | |||
| break; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| qDebug("CarlaPluginThread::run() - GUI timeout"); | |||
| engine->callback(CarlaBackend::CALLBACK_SHOW_GUI, plugin->id(), 0, 0, 0.0, nullptr); | |||
| } | |||
| break; | |||
| case PLUGIN_THREAD_BRIDGE: | |||
| m_process->waitForFinished(-1); | |||
| if (m_process->exitCode() != 0) | |||
| { | |||
| qWarning("CarlaPluginThread::run() - bridge crashed"); | |||
| QString errorString = QString("Plugin '%1' has crashed!\n" | |||
| "Saving now will lose its current settings.\n" | |||
| "Please remove this plugin, and not rely on it from this point.").arg(plugin->name()); | |||
| engine->setLastError(errorString.toUtf8().constData()); | |||
| engine->callback(CarlaBackend::CALLBACK_ERROR, plugin->id(), 0, 0, 0.0, nullptr); | |||
| } | |||
| break; | |||
| } | |||
| } | |||
| CARLA_BACKEND_END_NAMESPACE | |||
| @@ -0,0 +1,60 @@ | |||
| /* | |||
| * Carla Plugin Thread | |||
| * Copyright (C) 2012-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 | |||
| */ | |||
| #ifndef CARLA_PLUGIN_THREAD_HPP | |||
| #define CARLA_PLUGIN_THREAD_HPP | |||
| #include "carla_backend_utils.hpp" | |||
| #include <QtCore/QThread> | |||
| class QProcess; | |||
| CARLA_BACKEND_START_NAMESPACE | |||
| class CarlaPluginThread : public QThread | |||
| { | |||
| public: | |||
| enum PluginThreadMode { | |||
| PLUGIN_THREAD_DSSI_GUI, | |||
| PLUGIN_THREAD_LV2_GUI, | |||
| PLUGIN_THREAD_VST_GUI, | |||
| PLUGIN_THREAD_BRIDGE | |||
| }; | |||
| CarlaPluginThread(CarlaEngine* const engine, CarlaPlugin* const plugin, const PluginThreadMode mode, QObject* const parent = nullptr); | |||
| ~CarlaPluginThread(); | |||
| void setOscData(const char* const binary, const char* const label, const char* const data1=""); | |||
| protected: | |||
| void run(); | |||
| private: | |||
| CarlaEngine* const kEngine; | |||
| CarlaPlugin* const kPlugin; | |||
| const PluginThreadMode kMode; | |||
| CarlaString fBinary; | |||
| CarlaString fLabel; | |||
| CarlaString fData1; | |||
| QProcess* fProcess; | |||
| }; | |||
| CARLA_BACKEND_END_NAMESPACE | |||
| #endif // CARLA_PLUGIN_THREAD_HPP | |||
| @@ -0,0 +1,991 @@ | |||
| /* | |||
| * Carla LinuxSampler Plugin | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| // TODO - setMidiProgram() | |||
| #ifdef BUILD_BRIDGE | |||
| # error Should not use linuxsampler for bridges! | |||
| #endif | |||
| #include "carla_plugin.hpp" | |||
| #ifdef WANT_LINUXSAMPLER | |||
| #include "linuxsampler/EngineFactory.h" | |||
| #include <linuxsampler/Sampler.h> | |||
| namespace LinuxSampler { | |||
| // ----------------------------------------------------------------------- | |||
| // LinuxSampler static values | |||
| static const float VOLUME_MAX = 3.16227766f; // +10 dB | |||
| static const float VOLUME_MIN = 0.0f; // -inf dB | |||
| // ----------------------------------------------------------------------- | |||
| // LinuxSampler AudioOutputDevice Plugin | |||
| class AudioOutputDevicePlugin : public AudioOutputDevice | |||
| { | |||
| public: | |||
| AudioOutputDevicePlugin(CarlaBackend::CarlaEngine* const engine, CarlaBackend::CarlaPlugin* const plugin) | |||
| : AudioOutputDevice(std::map<String, DeviceCreationParameter*>()), | |||
| m_engine(engine), | |||
| m_plugin(plugin) | |||
| { | |||
| CARLA_ASSERT(engine); | |||
| CARLA_ASSERT(plugin); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // LinuxSampler virtual methods | |||
| void Play() | |||
| { | |||
| } | |||
| bool IsPlaying() | |||
| { | |||
| return m_engine->isRunning() && m_plugin->enabled(); | |||
| } | |||
| void Stop() | |||
| { | |||
| } | |||
| uint MaxSamplesPerCycle() | |||
| { | |||
| return m_engine->getBufferSize(); | |||
| } | |||
| uint SampleRate() | |||
| { | |||
| return m_engine->getSampleRate(); | |||
| } | |||
| String Driver() | |||
| { | |||
| return "AudioOutputDevicePlugin"; | |||
| } | |||
| AudioChannel* CreateChannel(uint channelNr) | |||
| { | |||
| return new AudioChannel(channelNr, nullptr, 0); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // Give public access to the RenderAudio call | |||
| int Render(uint samples) | |||
| { | |||
| return RenderAudio(samples); | |||
| } | |||
| private: | |||
| CarlaBackend::CarlaEngine* const m_engine; | |||
| CarlaBackend::CarlaPlugin* const m_plugin; | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| // LinuxSampler MidiInputDevice Plugin | |||
| class MidiInputDevicePlugin : public MidiInputDevice | |||
| { | |||
| public: | |||
| MidiInputDevicePlugin(Sampler* const sampler) | |||
| : MidiInputDevice(std::map<String, DeviceCreationParameter*>(), sampler) | |||
| { | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // LinuxSampler virtual methods | |||
| void Listen() | |||
| { | |||
| } | |||
| void StopListen() | |||
| { | |||
| } | |||
| String Driver() | |||
| { | |||
| return "MidiInputDevicePlugin"; | |||
| } | |||
| MidiInputPort* CreateMidiPort() | |||
| { | |||
| return new MidiInputPortPlugin(this, Ports.size()); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // Properly delete port (deconstructor is protected) | |||
| void DeleteMidiPort(MidiInputPort* const port) | |||
| { | |||
| delete (MidiInputPortPlugin*)port; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // MIDI Port implementation for this plugin MIDI input driver | |||
| // (Constructor and deconstructor are protected) | |||
| class MidiInputPortPlugin : public MidiInputPort | |||
| { | |||
| protected: | |||
| MidiInputPortPlugin(MidiInputDevicePlugin* const device, const int portNumber) | |||
| : MidiInputPort(device, portNumber) | |||
| { | |||
| } | |||
| friend class MidiInputDevicePlugin; | |||
| }; | |||
| }; | |||
| } // namespace LinuxSampler | |||
| // ----------------------------------------------------------------------- | |||
| #include <QtCore/QFileInfo> | |||
| CARLA_BACKEND_START_NAMESPACE | |||
| /*! | |||
| * @defgroup CarlaBackendLinuxSamplerPlugin Carla Backend LinuxSampler Plugin | |||
| * | |||
| * The Carla Backend LinuxSampler Plugin.\n | |||
| * http://www.linuxsampler.org/ | |||
| * @{ | |||
| */ | |||
| class LinuxSamplerPlugin : public CarlaPlugin | |||
| { | |||
| public: | |||
| LinuxSamplerPlugin(CarlaEngine* const engine_, const unsigned short id, const bool isGIG) | |||
| : CarlaPlugin(engine_, id) | |||
| { | |||
| qDebug("LinuxSamplerPlugin::LinuxSamplerPlugin()"); | |||
| m_type = isGIG ? PLUGIN_GIG : PLUGIN_SFZ; | |||
| sampler = new LinuxSampler::Sampler; | |||
| sampler_channel = nullptr; | |||
| engine = nullptr; | |||
| engine_channel = nullptr; | |||
| instrument = nullptr; | |||
| audioOutputDevice = new LinuxSampler::AudioOutputDevicePlugin(engine_, this); | |||
| midiInputDevice = new LinuxSampler::MidiInputDevicePlugin(sampler); | |||
| midiInputPort = midiInputDevice->CreateMidiPort(); | |||
| m_isGIG = isGIG; | |||
| m_label = nullptr; | |||
| m_maker = nullptr; | |||
| } | |||
| ~LinuxSamplerPlugin() | |||
| { | |||
| qDebug("LinuxSamplerPlugin::~LinuxSamplerPlugin()"); | |||
| if (m_activeBefore) | |||
| audioOutputDevice->Stop(); | |||
| if (sampler_channel) | |||
| { | |||
| midiInputPort->Disconnect(sampler_channel->GetEngineChannel()); | |||
| sampler->RemoveSamplerChannel(sampler_channel); | |||
| } | |||
| midiInputDevice->DeleteMidiPort(midiInputPort); | |||
| delete audioOutputDevice; | |||
| delete midiInputDevice; | |||
| delete sampler; | |||
| instrumentIds.clear(); | |||
| if (m_label) | |||
| free((void*)m_label); | |||
| if (m_maker) | |||
| free((void*)m_maker); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // Information (base) | |||
| PluginCategory category() | |||
| { | |||
| return PLUGIN_CATEGORY_SYNTH; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // Information (per-plugin data) | |||
| void getLabel(char* const strBuf) | |||
| { | |||
| strncpy(strBuf, m_label, STR_MAX); | |||
| } | |||
| void getMaker(char* const strBuf) | |||
| { | |||
| strncpy(strBuf, m_maker, STR_MAX); | |||
| } | |||
| void getCopyright(char* const strBuf) | |||
| { | |||
| getMaker(strBuf); | |||
| } | |||
| void getRealName(char* const strBuf) | |||
| { | |||
| strncpy(strBuf, m_name, STR_MAX); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // Plugin state | |||
| void reload() | |||
| { | |||
| qDebug("LinuxSamplerPlugin::reload() - start"); | |||
| CARLA_ASSERT(instrument); | |||
| const ProcessMode processMode(x_engine->getOptions().processMode); | |||
| // Safely disable plugin for reload | |||
| const ScopedDisabler m(this); | |||
| if (x_client->isActive()) | |||
| x_client->deactivate(); | |||
| // Remove client ports | |||
| removeClientPorts(); | |||
| // Delete old data | |||
| deleteBuffers(); | |||
| uint32_t aOuts; | |||
| aOuts = 2; | |||
| aOut.ports = new CarlaEngineAudioPort*[aOuts]; | |||
| aOut.rindexes = new uint32_t[aOuts]; | |||
| const int portNameSize = x_engine->maxPortNameSize(); | |||
| CarlaString portName; | |||
| // --------------------------------------- | |||
| // Audio Outputs | |||
| { | |||
| portName.clear(); | |||
| if (processMode == PROCESS_MODE_SINGLE_CLIENT) | |||
| { | |||
| portName = m_name; | |||
| portName += ":"; | |||
| } | |||
| portName += "out-left"; | |||
| portName.truncate(portNameSize); | |||
| aOut.ports[0] = (CarlaEngineAudioPort*)x_client->addPort(CarlaEnginePortTypeAudio, portName, false); | |||
| aOut.rindexes[0] = 0; | |||
| } | |||
| { | |||
| portName.clear(); | |||
| if (processMode == PROCESS_MODE_SINGLE_CLIENT) | |||
| { | |||
| portName = m_name; | |||
| portName += ":"; | |||
| } | |||
| portName += "out-right"; | |||
| portName.truncate(portNameSize); | |||
| aOut.ports[1] = (CarlaEngineAudioPort*)x_client->addPort(CarlaEnginePortTypeAudio, portName, false); | |||
| aOut.rindexes[1] = 1; | |||
| } | |||
| // --------------------------------------- | |||
| // Control Input | |||
| { | |||
| portName.clear(); | |||
| if (processMode == PROCESS_MODE_SINGLE_CLIENT) | |||
| { | |||
| portName = m_name; | |||
| portName += ":"; | |||
| } | |||
| portName += "control-in"; | |||
| portName.truncate(portNameSize); | |||
| param.portCin = (CarlaEngineControlPort*)x_client->addPort(CarlaEnginePortTypeControl, portName, true); | |||
| } | |||
| // --------------------------------------- | |||
| // MIDI Input | |||
| { | |||
| portName.clear(); | |||
| if (processMode == PROCESS_MODE_SINGLE_CLIENT) | |||
| { | |||
| portName = m_name; | |||
| portName += ":"; | |||
| } | |||
| portName += "midi-in"; | |||
| portName.truncate(portNameSize); | |||
| midi.portMin = (CarlaEngineMidiPort*)x_client->addPort(CarlaEnginePortTypeMIDI, portName, true); | |||
| } | |||
| // --------------------------------------- | |||
| aOut.count = aOuts; | |||
| // plugin checks | |||
| m_hints &= ~(PLUGIN_IS_SYNTH | PLUGIN_USES_CHUNKS | PLUGIN_CAN_DRYWET | PLUGIN_CAN_VOLUME | PLUGIN_CAN_BALANCE | PLUGIN_CAN_FORCE_STEREO); | |||
| m_hints |= PLUGIN_IS_SYNTH; | |||
| m_hints |= PLUGIN_CAN_VOLUME; | |||
| m_hints |= PLUGIN_CAN_BALANCE; | |||
| m_hints |= PLUGIN_CAN_FORCE_STEREO; | |||
| reloadPrograms(true); | |||
| x_client->activate(); | |||
| qDebug("LinuxSamplerPlugin::reload() - end"); | |||
| } | |||
| void reloadPrograms(bool init) | |||
| { | |||
| qDebug("LinuxSamplerPlugin::reloadPrograms(%s)", bool2str(init)); | |||
| // Delete old programs | |||
| if (midiprog.count > 0) | |||
| { | |||
| for (uint32_t i=0; i < midiprog.count; i++) | |||
| free((void*)midiprog.data[i].name); | |||
| delete[] midiprog.data; | |||
| } | |||
| midiprog.count = 0; | |||
| midiprog.data = nullptr; | |||
| // Query new programs | |||
| uint32_t i = 0; | |||
| midiprog.count += instrumentIds.size(); | |||
| // sound kits must always have at least 1 midi-program | |||
| CARLA_ASSERT(midiprog.count > 0); | |||
| if (midiprog.count > 0) | |||
| midiprog.data = new MidiProgramData[midiprog.count]; | |||
| // Update data | |||
| for (i=0; i < midiprog.count; i++) | |||
| { | |||
| LinuxSampler::InstrumentManager::instrument_info_t info = instrument->GetInstrumentInfo(instrumentIds[i]); | |||
| // FIXME - use % 128 stuff | |||
| midiprog.data[i].bank = 0; | |||
| midiprog.data[i].program = i; | |||
| midiprog.data[i].name = strdup(info.InstrumentName.c_str()); | |||
| } | |||
| // Update OSC Names | |||
| if (x_engine->isOscControlRegistered()) | |||
| { | |||
| x_engine->osc_send_control_set_midi_program_count(m_id, midiprog.count); | |||
| for (i=0; i < midiprog.count; i++) | |||
| x_engine->osc_send_control_set_midi_program_data(m_id, i, midiprog.data[i].bank, midiprog.data[i].program, midiprog.data[i].name); | |||
| } | |||
| if (init) | |||
| { | |||
| if (midiprog.count > 0) | |||
| setMidiProgram(0, false, false, false, true); | |||
| } | |||
| else | |||
| { | |||
| x_engine->callback(CALLBACK_RELOAD_PROGRAMS, m_id, 0, 0, 0.0, nullptr); | |||
| } | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // Plugin processing | |||
| void process(float** const, float** const outBuffer, const uint32_t frames, const uint32_t framesOffset) | |||
| { | |||
| uint32_t i, k; | |||
| uint32_t midiEventCount = 0; | |||
| double aOutsPeak[2] = { 0.0 }; | |||
| CARLA_PROCESS_CONTINUE_CHECK; | |||
| // -------------------------------------------------------------------------------------------------------- | |||
| // Parameters Input [Automation] | |||
| if (m_active && m_activeBefore) | |||
| { | |||
| bool allNotesOffSent = false; | |||
| const CarlaEngineControlEvent* cinEvent; | |||
| uint32_t time, nEvents = param.portCin->getEventCount(); | |||
| uint32_t nextBankId = 0; | |||
| if (midiprog.current >= 0 && midiprog.count > 0) | |||
| nextBankId = midiprog.data[midiprog.current].bank; | |||
| for (i=0; i < nEvents; i++) | |||
| { | |||
| cinEvent = param.portCin->getEvent(i); | |||
| if (! cinEvent) | |||
| continue; | |||
| time = cinEvent->time - framesOffset; | |||
| if (time >= frames) | |||
| continue; | |||
| // Control change | |||
| switch (cinEvent->type) | |||
| { | |||
| case CarlaEngineNullEvent: | |||
| break; | |||
| case CarlaEngineParameterChangeEvent: | |||
| { | |||
| double value; | |||
| // Control backend stuff | |||
| if (cinEvent->channel == m_ctrlInChannel) | |||
| { | |||
| if (MIDI_IS_CONTROL_BREATH_CONTROLLER(cinEvent->parameter) && (m_hints & PLUGIN_CAN_DRYWET) > 0) | |||
| { | |||
| value = cinEvent->value; | |||
| setDryWet(value, false, false); | |||
| postponeEvent(PluginPostEventParameterChange, PARAMETER_DRYWET, 0, value); | |||
| continue; | |||
| } | |||
| if (MIDI_IS_CONTROL_CHANNEL_VOLUME(cinEvent->parameter) && (m_hints & PLUGIN_CAN_VOLUME) > 0) | |||
| { | |||
| value = cinEvent->value*127/100; | |||
| setVolume(value, false, false); | |||
| postponeEvent(PluginPostEventParameterChange, PARAMETER_VOLUME, 0, value); | |||
| continue; | |||
| } | |||
| if (MIDI_IS_CONTROL_BALANCE(cinEvent->parameter) && (m_hints & PLUGIN_CAN_BALANCE) > 0) | |||
| { | |||
| double left, right; | |||
| value = cinEvent->value/0.5 - 1.0; | |||
| if (value < 0.0) | |||
| { | |||
| left = -1.0; | |||
| right = (value*2)+1.0; | |||
| } | |||
| else if (value > 0.0) | |||
| { | |||
| left = (value*2)-1.0; | |||
| right = 1.0; | |||
| } | |||
| else | |||
| { | |||
| left = -1.0; | |||
| right = 1.0; | |||
| } | |||
| setBalanceLeft(left, false, false); | |||
| setBalanceRight(right, false, false); | |||
| postponeEvent(PluginPostEventParameterChange, PARAMETER_BALANCE_LEFT, 0, left); | |||
| postponeEvent(PluginPostEventParameterChange, PARAMETER_BALANCE_RIGHT, 0, right); | |||
| continue; | |||
| } | |||
| } | |||
| #if 0 | |||
| // Control plugin parameters | |||
| for (k=0; k < param.count; k++) | |||
| { | |||
| if (param.data[k].midiChannel != cinEvent->channel) | |||
| continue; | |||
| if (param.data[k].midiCC != cinEvent->parameter) | |||
| continue; | |||
| if (param.data[k].type != PARAMETER_INPUT) | |||
| continue; | |||
| if (param.data[k].hints & PARAMETER_IS_AUTOMABLE) | |||
| { | |||
| if (param.data[k].hints & PARAMETER_IS_BOOLEAN) | |||
| { | |||
| value = cinEvent->value < 0.5 ? param.ranges[k].min : param.ranges[k].max; | |||
| } | |||
| else | |||
| { | |||
| value = cinEvent->value * (param.ranges[k].max - param.ranges[k].min) + param.ranges[k].min; | |||
| if (param.data[k].hints & PARAMETER_IS_INTEGER) | |||
| value = rint(value); | |||
| } | |||
| setParameterValue(k, value, false, false, false); | |||
| postponeEvent(PluginPostEventParameterChange, k, 0, value); | |||
| } | |||
| } | |||
| #endif | |||
| break; | |||
| } | |||
| case CarlaEngineMidiBankChangeEvent: | |||
| if (cinEvent->channel == m_ctrlInChannel) | |||
| nextBankId = rint(cinEvent->value); | |||
| break; | |||
| case CarlaEngineMidiProgramChangeEvent: | |||
| if (cinEvent->channel == m_ctrlInChannel) | |||
| { | |||
| uint32_t nextProgramId = rint(cinEvent->value); | |||
| for (k=0; k < midiprog.count; k++) | |||
| { | |||
| if (midiprog.data[k].bank == nextBankId && midiprog.data[k].program == nextProgramId) | |||
| { | |||
| setMidiProgram(k, false, false, false, false); | |||
| postponeEvent(PluginPostEventMidiProgramChange, k, 0, 0.0); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| break; | |||
| case CarlaEngineAllSoundOffEvent: | |||
| if (cinEvent->channel == m_ctrlInChannel) | |||
| { | |||
| if (midi.portMin && ! allNotesOffSent) | |||
| sendMidiAllNotesOff(); | |||
| audioOutputDevice->Stop(); | |||
| audioOutputDevice->Play(); | |||
| postponeEvent(PluginPostEventParameterChange, PARAMETER_ACTIVE, 0, 0.0); | |||
| postponeEvent(PluginPostEventParameterChange, PARAMETER_ACTIVE, 0, 1.0); | |||
| allNotesOffSent = true; | |||
| } | |||
| break; | |||
| case CarlaEngineAllNotesOffEvent: | |||
| if (cinEvent->channel == m_ctrlInChannel) | |||
| { | |||
| if (midi.portMin && ! allNotesOffSent) | |||
| sendMidiAllNotesOff(); | |||
| allNotesOffSent = true; | |||
| } | |||
| break; | |||
| } | |||
| } | |||
| } // End of Parameters Input | |||
| CARLA_PROCESS_CONTINUE_CHECK; | |||
| // -------------------------------------------------------------------------------------------------------- | |||
| // MIDI Input | |||
| if (m_active && m_activeBefore) | |||
| { | |||
| // ---------------------------------------------------------------------------------------------------- | |||
| // MIDI Input (External) | |||
| { | |||
| engineMidiLock(); | |||
| for (i=0; i < MAX_MIDI_EVENTS && midiEventCount < MAX_MIDI_EVENTS; i++) | |||
| { | |||
| if (extMidiNotes[i].channel < 0) | |||
| break; | |||
| if (extMidiNotes[i].velo) | |||
| midiInputPort->DispatchNoteOn(extMidiNotes[i].note, extMidiNotes[i].velo, m_ctrlInChannel, 0); | |||
| else | |||
| midiInputPort->DispatchNoteOff(extMidiNotes[i].note, extMidiNotes[i].velo, m_ctrlInChannel, 0); | |||
| extMidiNotes[i].channel = -1; // mark as invalid | |||
| midiEventCount += 1; | |||
| } | |||
| engineMidiUnlock(); | |||
| } // End of MIDI Input (External) | |||
| CARLA_PROCESS_CONTINUE_CHECK; | |||
| // ---------------------------------------------------------------------------------------------------- | |||
| // MIDI Input (System) | |||
| { | |||
| const CarlaEngineMidiEvent* minEvent; | |||
| uint32_t time, nEvents = midi.portMin->getEventCount(); | |||
| for (i=0; i < nEvents && midiEventCount < MAX_MIDI_EVENTS; i++) | |||
| { | |||
| minEvent = midi.portMin->getEvent(i); | |||
| if (! minEvent) | |||
| continue; | |||
| time = minEvent->time - framesOffset; | |||
| if (time >= frames) | |||
| continue; | |||
| uint8_t status = minEvent->data[0]; | |||
| uint8_t channel = status & 0x0F; | |||
| // Fix bad note-off | |||
| if (MIDI_IS_STATUS_NOTE_ON(status) && minEvent->data[2] == 0) | |||
| status -= 0x10; | |||
| if (MIDI_IS_STATUS_NOTE_OFF(status)) | |||
| { | |||
| uint8_t note = minEvent->data[1]; | |||
| midiInputPort->DispatchNoteOff(note, 0, channel, time); | |||
| postponeEvent(PluginPostEventNoteOff, channel, note, 0.0); | |||
| } | |||
| else if (MIDI_IS_STATUS_NOTE_ON(status)) | |||
| { | |||
| uint8_t note = minEvent->data[1]; | |||
| uint8_t velo = minEvent->data[2]; | |||
| midiInputPort->DispatchNoteOn(note, velo, channel, time); | |||
| postponeEvent(PluginPostEventNoteOn, channel, note, velo); | |||
| } | |||
| else if (MIDI_IS_STATUS_POLYPHONIC_AFTERTOUCH(status)) | |||
| { | |||
| //uint8_t note = minEvent->data[1]; | |||
| //uint8_t pressure = minEvent->data[2]; | |||
| // TODO, not in linuxsampler API? | |||
| } | |||
| else if (MIDI_IS_STATUS_AFTERTOUCH(status)) | |||
| { | |||
| uint8_t pressure = minEvent->data[1]; | |||
| midiInputPort->DispatchControlChange(MIDI_STATUS_AFTERTOUCH, pressure, channel, time); | |||
| } | |||
| else if (MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status)) | |||
| { | |||
| uint8_t lsb = minEvent->data[1]; | |||
| uint8_t msb = minEvent->data[2]; | |||
| midiInputPort->DispatchPitchbend(((msb << 7) | lsb) - 8192, channel, time); | |||
| } | |||
| else | |||
| continue; | |||
| midiEventCount += 1; | |||
| } | |||
| } // End of MIDI Input (System) | |||
| } // End of MIDI Input | |||
| CARLA_PROCESS_CONTINUE_CHECK; | |||
| // -------------------------------------------------------------------------------------------------------- | |||
| // Plugin processing | |||
| if (m_active) | |||
| { | |||
| if (! m_activeBefore) | |||
| { | |||
| for (int c=0; c < MAX_MIDI_CHANNELS; c++) | |||
| { | |||
| midiInputPort->DispatchControlChange(MIDI_CONTROL_ALL_SOUND_OFF, 0, c); | |||
| midiInputPort->DispatchControlChange(MIDI_CONTROL_ALL_NOTES_OFF, 0, c); | |||
| } | |||
| audioOutputDevice->Play(); | |||
| } | |||
| audioOutputDevice->Channel(0)->SetBuffer(outBuffer[0]); | |||
| audioOutputDevice->Channel(1)->SetBuffer(outBuffer[1]); | |||
| // QUESTION: Need to clear it before? | |||
| audioOutputDevice->Render(frames); | |||
| } | |||
| else | |||
| { | |||
| if (m_activeBefore) | |||
| audioOutputDevice->Stop(); | |||
| } | |||
| CARLA_PROCESS_CONTINUE_CHECK; | |||
| // -------------------------------------------------------------------------------------------------------- | |||
| // Post-processing (dry/wet, volume and balance) | |||
| if (m_active) | |||
| { | |||
| bool do_volume = x_volume != 1.0; | |||
| bool do_balance = (x_balanceLeft != -1.0 || x_balanceRight != 1.0); | |||
| double bal_rangeL, bal_rangeR; | |||
| float oldBufLeft[do_balance ? frames : 0]; | |||
| for (i=0; i < aOut.count; i++) | |||
| { | |||
| // Balance | |||
| if (do_balance) | |||
| { | |||
| if (i%2 == 0) | |||
| memcpy(&oldBufLeft, outBuffer[i], sizeof(float)*frames); | |||
| bal_rangeL = (x_balanceLeft+1.0)/2; | |||
| bal_rangeR = (x_balanceRight+1.0)/2; | |||
| for (k=0; k < frames; k++) | |||
| { | |||
| if (i%2 == 0) | |||
| { | |||
| // left output | |||
| outBuffer[i][k] = oldBufLeft[k]*(1.0-bal_rangeL); | |||
| outBuffer[i][k] += outBuffer[i+1][k]*(1.0-bal_rangeR); | |||
| } | |||
| else | |||
| { | |||
| // right | |||
| outBuffer[i][k] = outBuffer[i][k]*bal_rangeR; | |||
| outBuffer[i][k] += oldBufLeft[k]*bal_rangeL; | |||
| } | |||
| } | |||
| } | |||
| // Volume | |||
| if (do_volume) | |||
| { | |||
| for (k=0; k < frames; k++) | |||
| outBuffer[i][k] *= x_volume; | |||
| } | |||
| // Output VU | |||
| if (x_engine->getOptions().processMode != PROCESS_MODE_CONTINUOUS_RACK) | |||
| { | |||
| for (k=0; i < 2 && k < frames; k++) | |||
| { | |||
| if (std::abs(outBuffer[i][k]) > aOutsPeak[i]) | |||
| aOutsPeak[i] = std::abs(outBuffer[i][k]); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| // disable any output sound if not active | |||
| for (i=0; i < aOut.count; i++) | |||
| carla_zeroF(outBuffer[i], frames); | |||
| aOutsPeak[0] = 0.0; | |||
| aOutsPeak[1] = 0.0; | |||
| } // End of Post-processing | |||
| CARLA_PROCESS_CONTINUE_CHECK; | |||
| // -------------------------------------------------------------------------------------------------------- | |||
| // Peak Values | |||
| x_engine->setOutputPeak(m_id, 0, aOutsPeak[0]); | |||
| x_engine->setOutputPeak(m_id, 1, aOutsPeak[1]); | |||
| m_activeBefore = m_active; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| bool init(const char* filename, const char* const name, const char* label) | |||
| { | |||
| QFileInfo file(filename); | |||
| if (file.exists() && file.isFile() && file.isReadable()) | |||
| { | |||
| const char* stype = m_isGIG ? "gig" : "sfz"; | |||
| try { | |||
| engine = LinuxSampler::EngineFactory::Create(stype); | |||
| } | |||
| catch (LinuxSampler::Exception& e) | |||
| { | |||
| x_engine->setLastError(e.what()); | |||
| return false; | |||
| } | |||
| try { | |||
| instrument = engine->GetInstrumentManager(); | |||
| } | |||
| catch (LinuxSampler::Exception& e) | |||
| { | |||
| x_engine->setLastError(e.what()); | |||
| return false; | |||
| } | |||
| try { | |||
| instrumentIds = instrument->GetInstrumentFileContent(filename); | |||
| } | |||
| catch (LinuxSampler::Exception& e) | |||
| { | |||
| x_engine->setLastError(e.what()); | |||
| return false; | |||
| } | |||
| if (instrumentIds.size() > 0) | |||
| { | |||
| LinuxSampler::InstrumentManager::instrument_info_t info = instrument->GetInstrumentInfo(instrumentIds[0]); | |||
| m_label = strdup(info.Product.c_str()); | |||
| m_maker = strdup(info.Artists.c_str()); | |||
| m_filename = strdup(filename); | |||
| if (name) | |||
| m_name = x_engine->getUniquePluginName(name); | |||
| else | |||
| m_name = x_engine->getUniquePluginName(label && label[0] ? label : info.InstrumentName.c_str()); | |||
| sampler_channel = sampler->AddSamplerChannel(); | |||
| sampler_channel->SetEngineType(stype); | |||
| sampler_channel->SetAudioOutputDevice(audioOutputDevice); | |||
| //sampler_channel->SetMidiInputDevice(midiInputDevice); | |||
| //sampler_channel->SetMidiInputChannel(LinuxSampler::midi_chan_1); | |||
| midiInputPort->Connect(sampler_channel->GetEngineChannel(), LinuxSampler::midi_chan_all); | |||
| engine_channel = sampler_channel->GetEngineChannel(); | |||
| engine_channel->Connect(audioOutputDevice); | |||
| engine_channel->PrepareLoadInstrument(filename, 0); // todo - find instrument from label | |||
| engine_channel->LoadInstrument(); | |||
| engine_channel->Volume(LinuxSampler::VOLUME_MAX); | |||
| x_client = x_engine->addClient(this); | |||
| if (x_client->isOk()) | |||
| return true; | |||
| else | |||
| x_engine->setLastError("Failed to register plugin client"); | |||
| } | |||
| else | |||
| x_engine->setLastError("Failed to find any instruments"); | |||
| } | |||
| else | |||
| x_engine->setLastError("Requested file is not valid or does not exist"); | |||
| return false; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| static CarlaPlugin* newLinuxSampler(const initializer& init, bool isGIG); | |||
| private: | |||
| LinuxSampler::Sampler* sampler; | |||
| LinuxSampler::SamplerChannel* sampler_channel; | |||
| LinuxSampler::Engine* engine; | |||
| LinuxSampler::EngineChannel* engine_channel; | |||
| LinuxSampler::InstrumentManager* instrument; | |||
| std::vector<LinuxSampler::InstrumentManager::instrument_id_t> instrumentIds; | |||
| LinuxSampler::AudioOutputDevicePlugin* audioOutputDevice; | |||
| LinuxSampler::MidiInputDevicePlugin* midiInputDevice; | |||
| LinuxSampler::MidiInputPort* midiInputPort; | |||
| bool m_isGIG; | |||
| const char* m_label; | |||
| const char* m_maker; | |||
| }; | |||
| CarlaPlugin* LinuxSamplerPlugin::newLinuxSampler(const initializer& init, bool isGIG) | |||
| { | |||
| qDebug("LinuxSamplerPlugin::newLinuxSampler(%p, \"%s\", \"%s\", \"%s\", %s)", init.engine, init.filename, init.name, init.label, bool2str(isGIG)); | |||
| short id = init.engine->getNewPluginId(); | |||
| if (id < 0 || id > init.engine->maxPluginNumber()) | |||
| { | |||
| init.engine->setLastError("Maximum number of plugins reached"); | |||
| return nullptr; | |||
| } | |||
| LinuxSamplerPlugin* const plugin = new LinuxSamplerPlugin(init.engine, id, isGIG); | |||
| if (! plugin->init(init.filename, init.name, init.label)) | |||
| { | |||
| delete plugin; | |||
| return nullptr; | |||
| } | |||
| plugin->reload(); | |||
| plugin->registerToOscClient(); | |||
| return plugin; | |||
| } | |||
| /**@}*/ | |||
| CARLA_BACKEND_END_NAMESPACE | |||
| #else // WANT_LINUXSAMPLER | |||
| # warning linuxsampler not available (no GIG and SFZ support) | |||
| #endif | |||
| CARLA_BACKEND_START_NAMESPACE | |||
| CarlaPlugin* CarlaPlugin::newGIG(const initializer& init) | |||
| { | |||
| qDebug("CarlaPlugin::newGIG(%p, \"%s\", \"%s\", \"%s\")", init.engine, init.filename, init.name, init.label); | |||
| #ifdef WANT_LINUXSAMPLER | |||
| return LinuxSamplerPlugin::newLinuxSampler(init, true); | |||
| #else | |||
| init.engine->setLastError("linuxsampler support not available"); | |||
| return nullptr; | |||
| #endif | |||
| } | |||
| CarlaPlugin* CarlaPlugin::newSFZ(const initializer& init) | |||
| { | |||
| qDebug("CarlaPlugin::newSFZ(%p, \"%s\", \"%s\", \"%s\")", init.engine, init.filename, init.name, init.label); | |||
| #ifdef WANT_LINUXSAMPLER | |||
| return LinuxSamplerPlugin::newLinuxSampler(init, false); | |||
| #else | |||
| init.engine->setLastError("linuxsampler support not available"); | |||
| return nullptr; | |||
| #endif | |||
| } | |||
| CARLA_BACKEND_END_NAMESPACE | |||
| @@ -0,0 +1,480 @@ | |||
| #!/usr/bin/make -f | |||
| # Makefile for carla-bridges # | |||
| # ---------------------------------------- # | |||
| # Created by falkTX | |||
| # | |||
| include ../Makefile.mk | |||
| HAVE_GTK2 = $(shell pkg-config --exists gtk+-2.0 && echo true) | |||
| HAVE_GTK3 = $(shell pkg-config --exists gtk+-3.0 && echo true) | |||
| # -------------------------------------------------------------- | |||
| BUILD_CXX_FLAGS += -DBUILD_BRIDGE | |||
| BUILD_CXX_FLAGS += -I. -I../carla-includes -I../carla-utils | |||
| BUILD_CXX_FLAGS += $(shell pkg-config --cflags liblo QtCore QtXml) | |||
| LINK_FLAGS += $(shell pkg-config --libs liblo QtCore QtXml) | |||
| ifeq ($(CARLA_PLUGIN_SUPPORT),true) | |||
| BUILD_CXX_FLAGS += -DWANT_LADSPA -DWANT_DSSI -DWANT_LV2 -DWANT_VST | |||
| endif | |||
| ifeq ($(HAVE_QT5),true) | |||
| QT_UI_FLAGS = $(shell pkg-config --cflags QtWidgets) | |||
| QT_UI_LIBS = $(shell pkg-config --libs QtWidgets) | |||
| else | |||
| QT_UI_FLAGS = $(shell pkg-config --cflags QtGui) | |||
| QT_UI_LIBS = $(shell pkg-config --libs QtGui) | |||
| endif | |||
| # -------------------------------------------------------------- | |||
| # Plugin bridges | |||
| BUILD_PLUGIN_FLAGS = $(BUILD_CXX_FLAGS) -DBUILD_BRIDGE_PLUGIN -DBRIDGE_PLUGIN | |||
| BUILD_PLUGIN_FLAGS += -DCARLA_ENGINE_JACK -I../carla-backend -I../carla-engine -I../carla-jackbridge -I../carla-plugin | |||
| BUILD_PLUGIN_FLAGS += $(shell pkg-config --cflags jack) $(QT_UI_FLAGS) | |||
| LINK_PLUGIN_FLAGS = $(LINK_FLAGS) | |||
| LINK_PLUGIN_FLAGS += $(QT_UI_LIBS) | |||
| POSIX_BUILD_FLAGS = $(BUILD_PLUGIN_FLAGS) | |||
| POSIX_32BIT_FLAGS = $(32BIT_FLAGS) -L/usr/lib32 -L/usr/lib/i386-linux-gnu | |||
| POSIX_64BIT_FLAGS = $(64BIT_FLAGS) -L/usr/lib64 -L/usr/lib/x86_64-linux-gnu | |||
| POSIX_LINK_FLAGS = $(LINK_PLUGIN_FLAGS) $(shell pkg-config --libs jack) -ldl | |||
| WIN_BUILD_FLAGS = $(BUILD_PLUGIN_FLAGS) -DJACKBRIDGE_EXPORT -DPTW32_STATIC_LIB | |||
| WIN_32BIT_FLAGS = $(32BIT_FLAGS) | |||
| WIN_64BIT_FLAGS = $(64BIT_FLAGS) | |||
| WIN_LINK_FLAGS = $(LINK_PLUGIN_FLAGS) -mwindows -L../carla-jackbridge -lpthread -lwinspool -lole32 -luuid -limm32 -lshell32 -lws2_32 | |||
| # -------------------------------------------------------------- | |||
| # UI bridges | |||
| BUILD_UI_FLAGS = $(BUILD_CXX_FLAGS) -DBUILD_BRIDGE_UI | |||
| LINK_UI_FLAGS = $(LINK_FLAGS) -ldl | |||
| BUILD_UI_LV2_FLAGS = $(BUILD_UI_FLAGS) -DBRIDGE_LV2 | |||
| BUILD_UI_VST_FLAGS = $(BUILD_UI_FLAGS) -DBRIDGE_VST | |||
| BUILD_UI_LV2_GTK2_FLAGS = $(BUILD_UI_LV2_FLAGS) -DBRIDGE_GTK2 -DBRIDGE_LV2_GTK2 $(shell pkg-config --cflags gtk+-2.0) | |||
| LINK_UI_LV2_GTK2_FLAGS = $(LINK_UI_FLAGS) $(shell pkg-config --libs gtk+-2.0) | |||
| BUILD_UI_LV2_GTK3_FLAGS = $(BUILD_UI_LV2_FLAGS) -DBRIDGE_GTK3 -DBRIDGE_LV2_GTK3 $(shell pkg-config --cflags gtk+-3.0) | |||
| LINK_UI_LV2_GTK3_FLAGS = $(LINK_UI_FLAGS) $(shell pkg-config --libs gtk+-3.0) | |||
| BUILD_UI_LV2_QT4_FLAGS = $(BUILD_UI_LV2_FLAGS) -DBRIDGE_QT4 -DBRIDGE_LV2_QT4 $(shell pkg-config --cflags QtGui) | |||
| LINK_UI_LV2_QT4_FLAGS = $(LINK_UI_FLAGS) $(shell pkg-config --libs QtGui) | |||
| BUILD_UI_LV2_QT5_FLAGS = $(BUILD_UI_LV2_FLAGS) -DBRIDGE_QT5 -DBRIDGE_LV2_QT5 $(shell pkg-config --cflags QtWidgets) | |||
| LINK_UI_LV2_QT5_FLAGS = $(LINK_UI_FLAGS) $(shell pkg-config --libs QtWidgets) | |||
| BUILD_UI_LV2_HWND_FLAGS = $(BUILD_UI_VST_FLAGS) -DBRIDGE_HWND -DBRIDGE_LV2_HWND $(QT_UI_FLAGS) | |||
| LINK_UI_LV2_HWND_FLAGS = $(LINK_FLAGS) $(QT_UI_LIBS) -mwindows -static | |||
| BUILD_UI_LV2_X11_FLAGS = $(BUILD_UI_LV2_FLAGS) -DBRIDGE_X11 -DBRIDGE_LV2_X11 $(QT_UI_FLAGS) | |||
| LINK_UI_LV2_X11_FLAGS = $(LINK_UI_FLAGS) $(QT_UI_LIBS) | |||
| BUILD_UI_VST_HWND_FLAGS = $(BUILD_UI_VST_FLAGS) -DBRIDGE_HWND -DBRIDGE_VST_HWND $(QT_UI_FLAGS) | |||
| LINK_UI_VST_HWND_FLAGS = $(LINK_FLAGS) $(QT_UI_LIBS) -mwindows -static | |||
| BUILD_UI_VST_X11_FLAGS = $(BUILD_UI_VST_FLAGS) -DBRIDGE_X11 -DBRIDGE_VST_X11 $(QT_UI_FLAGS) | |||
| LINK_UI_VST_X11_FLAGS = $(LINK_UI_FLAGS) $(QT_UI_LIBS) | |||
| # -------------------------------------------------------------- | |||
| ifeq ($(CARLA_PLUGIN_SUPPORT),true) | |||
| all: native ui_lv2-gtk2 ui_lv2-gtk3 ui_lv2-qt4 ui_lv2-qt5 ui_lv2-x11 ui_vst-x11 | |||
| ifeq ($(HAVE_GTK2),true) | |||
| ui_lv2-gtk2: carla-bridge-lv2-gtk2 | |||
| else | |||
| ui_lv2-gtk2: | |||
| endif | |||
| ifeq ($(HAVE_GTK3),true) | |||
| ui_lv2-gtk3: carla-bridge-lv2-gtk3 | |||
| else | |||
| ui_lv2-gtk3: | |||
| endif | |||
| ifneq ($(HAVE_QT5),true) | |||
| ui_lv2-qt4: carla-bridge-lv2-qt4 | |||
| ui_lv2-qt5: | |||
| else | |||
| ui_lv2-qt4: | |||
| ui_lv2-qt5: carla-bridge-lv2-qt5 | |||
| endif | |||
| ui_lv2-hwnd: carla-bridge-lv2-hwnd.exe | |||
| ui_lv2-x11: carla-bridge-lv2-x11 | |||
| ui_vst-hwnd: carla-bridge-vst-hwnd.exe | |||
| ui_vst-x11: carla-bridge-vst-x11 | |||
| else # CARLA_PLUGIN_SUPPORT | |||
| all: | |||
| endif | |||
| # -------------------------------------------------------------- | |||
| native: carla-bridge-native | |||
| posix32: carla-bridge-posix32 | |||
| posix64: carla-bridge-posix64 | |||
| win32: carla-bridge-win32.exe | |||
| win64: carla-bridge-win64.exe | |||
| # -------------------------------------------------------------- | |||
| # UI common | |||
| %__lv2.o: %.cpp | |||
| $(CXX) $< $(BUILD_UI_LV2_FLAGS) -c -o $@ | |||
| %__vst.o: %.cpp | |||
| $(CXX) $< $(BUILD_UI_VST_FLAGS) -c -o $@ | |||
| OBJS_UI_LV2_LIBS = \ | |||
| ../carla-lilv/carla_lilv.a \ | |||
| ../carla-rtmempool/carla_rtmempool.a | |||
| # -------------------------------------------------------------- | |||
| # ui_lv2-gtk2 | |||
| OBJS_UI_LV2_GTK2 = carla_bridge_ui-lv2__lv2-gtk2.o \ | |||
| carla_bridge_client__lv2.o carla_bridge_osc__lv2.o \ | |||
| carla_bridge_toolkit__lv2.o carla_bridge_toolkit-gtk__lv2-gtk2.o | |||
| carla-bridge-lv2-gtk2: $(OBJS_UI_LV2_GTK2) $(OBJS_UI_LV2_LIBS) | |||
| $(CXX) $^ $(LINK_UI_LV2_GTK2_FLAGS) -o $@ && $(STRIP) $@ | |||
| %__lv2-gtk2.o: %.cpp | |||
| $(CXX) $< $(BUILD_UI_LV2_GTK2_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| # ui_lv2-gtk3 | |||
| OBJS_UI_LV2_GTK3 = carla_bridge_ui-lv2__lv2-gtk3.o \ | |||
| carla_bridge_client__lv2.o carla_bridge_osc__lv2.o \ | |||
| carla_bridge_toolkit__lv2.o carla_bridge_toolkit-gtk__lv2-gtk3.o | |||
| carla-bridge-lv2-gtk3: $(OBJS_UI_LV2_GTK3) $(OBJS_UI_LV2_LIBS) | |||
| $(CXX) $^ $(LINK_UI_LV2_GTK3_FLAGS) -o $@ && $(STRIP) $@ | |||
| %__lv2-gtk3.o: %.cpp | |||
| $(CXX) $< $(BUILD_UI_LV2_GTK3_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| # ui_lv2-qt4 | |||
| OBJS_UI_LV2_QT4 = carla_bridge_ui-lv2__lv2-qt4.o \ | |||
| carla_bridge_client__lv2.o carla_bridge_osc__lv2.o \ | |||
| carla_bridge_toolkit__lv2.o carla_bridge_toolkit-qt__lv2-qt4.o | |||
| carla-bridge-lv2-qt4: $(OBJS_UI_LV2_QT4) $(OBJS_UI_LV2_LIBS) | |||
| $(CXX) $^ $(LINK_UI_LV2_QT4_FLAGS) -o $@ && $(STRIP) $@ | |||
| %__lv2-qt4.o: %.cpp | |||
| $(CXX) $< $(BUILD_UI_LV2_QT4_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| # ui_lv2-qt5 | |||
| OBJS_UI_LV2_QT5 = carla_bridge_ui-lv2__lv2-qt5.o \ | |||
| carla_bridge_client__lv2.o carla_bridge_osc__lv2.o \ | |||
| carla_bridge_toolkit__lv2.o carla_bridge_toolkit-qt__lv2-qt5.o | |||
| carla-bridge-lv2-qt5: $(OBJS_UI_LV2_QT5) $(OBJS_UI_LV2_LIBS) | |||
| $(CXX) $^ $(LINK_UI_LV2_QT5_FLAGS) -o $@ && $(STRIP) $@ | |||
| %__lv2-qt5.o: %.cpp | |||
| $(CXX) $< $(BUILD_UI_LV2_QT5_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| # ui_lv2-x11 | |||
| OBJS_UI_LV2_X11 = carla_bridge_ui-lv2__lv2-x11.o \ | |||
| carla_bridge_client__lv2.o carla_bridge_osc__lv2.o \ | |||
| carla_bridge_toolkit__lv2.o carla_bridge_toolkit-qt__lv2-x11.o | |||
| carla-bridge-lv2-x11: $(OBJS_UI_LV2_X11) $(OBJS_UI_LV2_LIBS) | |||
| $(CXX) $^ $(LINK_UI_LV2_X11_FLAGS) -o $@ && $(STRIP) $@ | |||
| %__lv2-x11.o: %.cpp | |||
| $(CXX) $< $(BUILD_UI_LV2_X11_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| # ui_vst-hwnd | |||
| OBJS_UI_VST_HWND = carla_bridge_ui-vst__vst-hwnd.o \ | |||
| carla_bridge_client__vst.o carla_bridge_osc__vst.o \ | |||
| carla_bridge_toolkit__vst.o carla_bridge_toolkit-qt__vst-hwnd.o | |||
| carla-bridge-vst-hwnd.exe: $(OBJS_UI_VST_HWND) | |||
| $(CXX) $^ $(LINK_UI_VST_HWND_FLAGS) -o $@ && $(STRIP) $@ | |||
| %__vst-hwnd.o: %.cpp | |||
| $(CXX) $< $(BUILD_UI_VST_HWND_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| # ui_vst-x11 | |||
| OBJS_UI_VST_X11 = carla_bridge_ui-vst__vst-x11.o \ | |||
| carla_bridge_client__vst.o carla_bridge_osc__vst.o \ | |||
| carla_bridge_toolkit__vst.o carla_bridge_toolkit-qt__vst-x11.o | |||
| carla-bridge-vst-x11: $(OBJS_UI_VST_X11) | |||
| $(CXX) $^ $(LINK_UI_VST_X11_FLAGS) -o $@ && $(STRIP) $@ | |||
| %__vst-x11.o: %.cpp | |||
| $(CXX) $< $(BUILD_UI_VST_X11_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| # native | |||
| NATIVE_BUILD_FLAGS = $(BUILD_PLUGIN_FLAGS) | |||
| NATIVE_LINK_FLAGS = $(LINK_PLUGIN_FLAGS) $(shell pkg-config --libs jack) -ldl | |||
| ifeq ($(HAVE_SUIL),true) | |||
| NATIVE_BUILD_FLAGS += $(shell pkg-config --cflags suil-0) -DWANT_SUIL | |||
| NATIVE_LINK_FLAGS += $(shell pkg-config --libs suil-0) | |||
| endif | |||
| OBJS_NATIVE = carla_bridge_plugin__native.o \ | |||
| carla_bridge_client__native.o carla_bridge_osc__native.o \ | |||
| carla_bridge_toolkit__native.o | |||
| # carla | |||
| OBJS_NATIVE += \ | |||
| ../carla/Shared__native.o | |||
| # carla-engine | |||
| OBJS_NATIVE += \ | |||
| ../carla-engine/carla_engine__native.o \ | |||
| ../carla-engine/carla_engine_osc__native.o \ | |||
| ../carla-engine/carla_engine_thread__native.o \ | |||
| ../carla-engine/jack__native.o | |||
| # carla-plugin | |||
| OBJS_NATIVE += \ | |||
| ../carla-plugin/carla_plugin__native.o \ | |||
| ../carla-plugin/carla_plugin_thread__native.o \ | |||
| ../carla-plugin/ladspa__native.o \ | |||
| ../carla-plugin/dssi__native.o \ | |||
| ../carla-plugin/lv2__native.o \ | |||
| ../carla-plugin/vst__native.o | |||
| # libs | |||
| OBJS_NATIVE += \ | |||
| ../carla-lilv/carla_lilv.a \ | |||
| ../carla-rtmempool/carla_rtmempool.a | |||
| carla-bridge-native: $(OBJS_NATIVE) | |||
| $(CXX) $^ $(NATIVE_LINK_FLAGS) -o $@ && $(STRIP) $@ | |||
| %__native.o: %.cpp | |||
| $(CXX) $< $(NATIVE_BUILD_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| # posix32 | |||
| OBJS_POSIX32 = carla_bridge_plugin__posix32.o \ | |||
| carla_bridge_client__posix32.o carla_bridge_osc__posix32.o \ | |||
| carla_bridge_toolkit__posix32.o | |||
| # carla | |||
| OBJS_POSIX32 += \ | |||
| ../carla/Shared__posix32.o | |||
| # carla-engine | |||
| OBJS_POSIX32 += \ | |||
| ../carla-engine/carla_engine__posix32.o \ | |||
| ../carla-engine/carla_engine_osc__posix32.o \ | |||
| ../carla-engine/carla_engine_thread__posix32.o \ | |||
| ../carla-engine/jack__posix32.o | |||
| # carla-plugin | |||
| OBJS_POSIX32 += \ | |||
| ../carla-plugin/carla_plugin__posix32.o \ | |||
| ../carla-plugin/carla_plugin_thread__posix32.o \ | |||
| ../carla-plugin/ladspa__posix32.o \ | |||
| ../carla-plugin/dssi__posix32.o \ | |||
| ../carla-plugin/lv2__posix32.o \ | |||
| ../carla-plugin/vst__posix32.o | |||
| # libs | |||
| OBJS_POSIX32 += \ | |||
| ../carla-lilv/carla_lilv_posix32.a \ | |||
| ../carla-rtmempool/carla_rtmempool_posix32.a | |||
| carla-bridge-posix32: $(OBJS_POSIX32) | |||
| $(CXX) $^ $(POSIX_LINK_FLAGS) $(POSIX_32BIT_FLAGS) -o $@ && $(STRIP) $@ | |||
| %__posix32.o: %.cpp | |||
| $(CXX) $< $(POSIX_BUILD_FLAGS) $(POSIX_32BIT_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| # posix64 | |||
| OBJS_POSIX64 = carla_bridge_plugin__posix64.o \ | |||
| carla_bridge_client__posix64.o carla_bridge_osc__posix64.o \ | |||
| carla_bridge_toolkit__posix64.o | |||
| # carla | |||
| OBJS_POSIX64 += \ | |||
| ../carla/Shared__posix64.o | |||
| # carla-engine | |||
| OBJS_POSIX64 += \ | |||
| ../carla-engine/carla_engine__posix64.o \ | |||
| ../carla-engine/carla_engine_osc__posix64.o \ | |||
| ../carla-engine/carla_engine_thread__posix64.o \ | |||
| ../carla-engine/jack__posix64.o | |||
| # carla-plugin | |||
| OBJS_POSIX64 += \ | |||
| ../carla-plugin/carla_plugin__posix64.o \ | |||
| ../carla-plugin/carla_plugin_thread__posix64.o \ | |||
| ../carla-plugin/ladspa__posix64.o \ | |||
| ../carla-plugin/dssi__posix64.o \ | |||
| ../carla-plugin/lv2__posix64.o \ | |||
| ../carla-plugin/vst__posix64.o | |||
| # libs | |||
| OBJS_POSIX64 += \ | |||
| ../carla-lilv/carla_lilv_posix64.a \ | |||
| ../carla-rtmempool/carla_rtmempool_posix64.a | |||
| carla-bridge-posix64: $(OBJS_POSIX64) | |||
| $(CXX) $^ $(POSIX_LINK_FLAGS) $(POSIX_64BIT_FLAGS) -o $@ && $(STRIP) $@ | |||
| %__posix64.o: %.cpp | |||
| $(CXX) $< $(POSIX_BUILD_FLAGS) $(POSIX_64BIT_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| # win32 | |||
| OBJS_WIN32 = carla_bridge_plugin__win32.o \ | |||
| carla_bridge_client__win32.o carla_bridge_osc__win32.o \ | |||
| carla_bridge_toolkit__win32.o | |||
| # carla | |||
| OBJS_WIN32 += \ | |||
| ../carla/Shared__win32.o | |||
| # carla-engine | |||
| OBJS_WIN32 += \ | |||
| ../carla-engine/carla_engine__win32.o \ | |||
| ../carla-engine/carla_engine_osc__win32.o \ | |||
| ../carla-engine/carla_engine_thread__win32.o \ | |||
| ../carla-engine/jack__win32.o | |||
| # carla-plugin | |||
| OBJS_WIN32 += \ | |||
| ../carla-plugin/carla_plugin__win32.o \ | |||
| ../carla-plugin/carla_plugin_thread__win32.o \ | |||
| ../carla-plugin/ladspa__win32.o \ | |||
| ../carla-plugin/dssi__win32.o \ | |||
| ../carla-plugin/lv2__win32.o \ | |||
| ../carla-plugin/vst__win32.o | |||
| # libs | |||
| OBJS_WIN32 += \ | |||
| ../carla-lilv/carla_lilv_win32.a \ | |||
| ../carla-rtmempool/carla_rtmempool_win32.a | |||
| carla-bridge-win32.exe: $(OBJS_WIN32) ../carla-jackbridge/libcarla-jackbridge-win32.dll | |||
| $(CXX) $(OBJS_WIN32) $(WIN_32BIT_FLAGS) $(WIN_LINK_FLAGS) -lcarla-jackbridge-win32 -o $@ && $(STRIP) $@ | |||
| %__win32.o: %.cpp | |||
| $(CXX) $< $(WIN_BUILD_FLAGS) $(WIN_32BIT_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| # win64 | |||
| OBJS_WIN64 = carla_bridge_plugin__win64.o \ | |||
| carla_bridge_client__win64.o carla_bridge_osc__win64.o \ | |||
| carla_bridge_toolkit__win64.o | |||
| # carla | |||
| OBJS_WIN64 += \ | |||
| ../carla/Shared__win64.o | |||
| # carla-engine | |||
| OBJS_WIN64 += \ | |||
| ../carla-engine/carla_engine__win64.o \ | |||
| ../carla-engine/carla_engine_osc__win64.o \ | |||
| ../carla-engine/carla_engine_thread__win64.o \ | |||
| ../carla-engine/jack__win64.o | |||
| # carla-plugin | |||
| OBJS_WIN64 += \ | |||
| ../carla-plugin/carla_plugin__win64.o \ | |||
| ../carla-plugin/carla_plugin_thread__win64.o \ | |||
| ../carla-plugin/ladspa__win64.o \ | |||
| ../carla-plugin/dssi__win64.o \ | |||
| ../carla-plugin/lv2__win64.o \ | |||
| ../carla-plugin/vst__win64.o | |||
| # libs | |||
| OBJS_WIN64 += \ | |||
| ../carla-lilv/carla_lilv_win64.a \ | |||
| ../carla-rtmempool/carla_rtmempool_win64.a | |||
| carla-bridge-win64.exe: $(OBJS_WIN64) ../carla-jackbridge/libcarla-jackbridge-win64.dll | |||
| $(CXX) $(OBJS_WIN64) $(WIN_64BIT_FLAGS) $(WIN_LINK_FLAGS) -lcarla-jackbridge-win64 -o $@ && $(STRIP) $@ | |||
| %__win64.o: %.cpp | |||
| $(CXX) $< $(WIN_BUILD_FLAGS) $(WIN_64BIT_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| ../carla-lilv/carla_lilv.a: | |||
| $(MAKE) -C ../carla-lilv | |||
| ../carla-lilv/carla_lilv_posix32.a: | |||
| $(MAKE) -C ../carla-lilv posix32 | |||
| ../carla-lilv/carla_lilv_posix64.a: | |||
| $(MAKE) -C ../carla-lilv posix64 | |||
| ../carla-lilv/carla_lilv_win32.a: | |||
| $(MAKE) -C ../carla-lilv win32 | |||
| ../carla-lilv/carla_lilv_win64.a: | |||
| $(MAKE) -C ../carla-lilv win64 | |||
| ../carla-rtmempool/carla_rtmempool.a: | |||
| $(MAKE) -C ../carla-rtmempool | |||
| ../carla-rtmempool/carla_rtmempool_posix32.a: | |||
| $(MAKE) -C ../carla-rtmempool posix32 | |||
| ../carla-rtmempool/carla_rtmempool_posix64.a: | |||
| $(MAKE) -C ../carla-rtmempool posix64 | |||
| ../carla-rtmempool/carla_rtmempool_win32.a: | |||
| $(MAKE) -C ../carla-rtmempool win32 | |||
| ../carla-rtmempool/carla_rtmempool_win64.a: | |||
| $(MAKE) -C ../carla-rtmempool win64 | |||
| ../carla-jackbridge/libcarla-jackbridge-win32.dll: | |||
| $(MAKE) -C ../carla-jackbridge win32 | |||
| ../carla-jackbridge/libcarla-jackbridge-win64.dll: | |||
| $(MAKE) -C ../carla-jackbridge win64 | |||
| # -------------------------------------------------------------- | |||
| doxygen: carla_bridge.doxygen | |||
| doxygen $< | |||
| clean: | |||
| rm -f *.o *.dll *.so *.exe | |||
| rm -f $(OBJS_NATIVE) | |||
| rm -f $(OBJS_POSIX32) | |||
| rm -f $(OBJS_POSIX64) | |||
| rm -f $(OBJS_WIN32) | |||
| rm -f $(OBJS_WIN64) | |||
| rm -f carla-bridge-lv2-gtk2 carla-bridge-lv2-gtk3 carla-bridge-lv2-qt4 carla-bridge-lv2-qt5 carla-bridge-lv2-x11 carla-bridge-vst-x11 | |||
| rm -f carla-bridge-native carla-bridge-posix32 carla-bridge-posix64 | |||
| @@ -0,0 +1,34 @@ | |||
| /* | |||
| * Carla Bridge | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #ifndef CARLA_BRIDGE_HPP | |||
| #define CARLA_BRIDGE_HPP | |||
| #include "carla_defines.hpp" | |||
| #define CARLA_BRIDGE_START_NAMESPACE namespace CarlaBridge { | |||
| #define CARLA_BRIDGE_END_NAMESPACE } | |||
| #define CARLA_BRIDGE_USE_NAMESPACE using namespace CarlaBridge; | |||
| CARLA_BRIDGE_START_NAMESPACE | |||
| class CarlaBridgeClient; | |||
| class CarlaBridgeToolkit; | |||
| CARLA_BRIDGE_END_NAMESPACE | |||
| #endif // CARLA_BRIDGE_HPP | |||
| @@ -0,0 +1,335 @@ | |||
| /* | |||
| * Carla Bridge Client | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #include "carla_bridge_client.hpp" | |||
| #include "carla_bridge_toolkit.hpp" | |||
| #ifdef BUILD_BRIDGE_UI | |||
| # include "carla_lib_utils.hpp" | |||
| #endif | |||
| #include <cstdlib> | |||
| #include <cstring> | |||
| CARLA_BRIDGE_START_NAMESPACE | |||
| // --------------------------------------------------------------------- | |||
| CarlaBridgeClient::CarlaBridgeClient(const char* const uiTitle) | |||
| : m_osc(this), | |||
| m_toolkit(CarlaBridgeToolkit::createNew(this, uiTitle)) | |||
| { | |||
| qDebug("CarlaBridgeClient::CarlaBridgeClient(\"%s\")", uiTitle); | |||
| m_oscData = nullptr; | |||
| #ifdef BUILD_BRIDGE_UI | |||
| m_uiFilename = nullptr; | |||
| m_uiLib = nullptr; | |||
| m_uiQuit = false; | |||
| #endif | |||
| } | |||
| CarlaBridgeClient::~CarlaBridgeClient() | |||
| { | |||
| qDebug("CarlaBridgeClient::~CarlaBridgeClient()"); | |||
| #ifdef BUILD_BRIDGE_UI | |||
| if (m_uiFilename) | |||
| free(m_uiFilename); | |||
| #endif | |||
| delete m_toolkit; | |||
| } | |||
| #ifdef BUILD_BRIDGE_UI | |||
| // --------------------------------------------------------------------- | |||
| // ui initialization | |||
| bool CarlaBridgeClient::init(const char* const, const char* const) | |||
| { | |||
| qDebug("CarlaBridgeClient::init()"); | |||
| // Test for single init | |||
| { | |||
| static bool initiated = false; | |||
| CARLA_ASSERT(! initiated); | |||
| initiated = true; | |||
| } | |||
| m_uiQuit = false; | |||
| m_toolkit->init(); | |||
| return false; | |||
| } | |||
| void CarlaBridgeClient::close() | |||
| { | |||
| qDebug("CarlaBridgeClient::close()"); | |||
| if (! m_uiQuit) | |||
| { | |||
| m_uiQuit = true; | |||
| if (isOscControlRegistered()) | |||
| sendOscExiting(); | |||
| } | |||
| m_toolkit->quit(); | |||
| } | |||
| #endif | |||
| // --------------------------------------------------------------------- | |||
| // osc stuff | |||
| bool CarlaBridgeClient::oscInit(const char* const url) | |||
| { | |||
| qDebug("CarlaBridgeClient::oscInit(\"%s\")", url); | |||
| const bool ret = m_osc.init(url); | |||
| m_oscData = m_osc.getControlData(); | |||
| return ret; | |||
| } | |||
| bool CarlaBridgeClient::oscIdle() | |||
| { | |||
| m_osc.idle(); | |||
| #ifdef BUILD_BRIDGE_UI | |||
| return ! m_uiQuit; | |||
| #else | |||
| return true; | |||
| #endif | |||
| } | |||
| void CarlaBridgeClient::oscClose() | |||
| { | |||
| qDebug("CarlaBridgeClient::oscClose()"); | |||
| CARLA_ASSERT(m_oscData); | |||
| m_osc.close(); | |||
| m_oscData = nullptr; | |||
| } | |||
| bool CarlaBridgeClient::isOscControlRegistered() const | |||
| { | |||
| return m_osc.isControlRegistered(); | |||
| } | |||
| void CarlaBridgeClient::sendOscUpdate() | |||
| { | |||
| qDebug("CarlaBridgeClient::sendOscUpdate()"); | |||
| CARLA_ASSERT(m_oscData); | |||
| if (m_oscData && m_oscData->target) | |||
| osc_send_update(m_oscData, m_osc.getServerPath()); | |||
| } | |||
| #ifdef BUILD_BRIDGE_PLUGIN | |||
| void CarlaBridgeClient::sendOscBridgeUpdate() | |||
| { | |||
| qDebug("CarlaBridgeClient::sendOscBridgeUpdate()"); | |||
| CARLA_ASSERT(m_oscData); | |||
| CARLA_ASSERT(m_oscData->target && m_oscData->path); | |||
| if (m_oscData && m_oscData->target && m_oscData->path) | |||
| osc_send_bridge_update(m_oscData, m_oscData->path); | |||
| } | |||
| void CarlaBridgeClient::sendOscBridgeError(const char* const error) | |||
| { | |||
| qDebug("CarlaBridgeClient::sendOscBridgeError(\"%s\")", error); | |||
| CARLA_ASSERT(m_oscData); | |||
| CARLA_ASSERT(error); | |||
| if (m_oscData && m_oscData->target) | |||
| osc_send_bridge_error(m_oscData, error); | |||
| } | |||
| #endif | |||
| // --------------------------------------------------------------------- | |||
| // toolkit | |||
| void CarlaBridgeClient::toolkitShow() | |||
| { | |||
| qDebug("CarlaBridgeClient::toolkitShow()"); | |||
| m_toolkit->show(); | |||
| } | |||
| void CarlaBridgeClient::toolkitHide() | |||
| { | |||
| qDebug("CarlaBridgeClient::toolkitHide()"); | |||
| m_toolkit->hide(); | |||
| } | |||
| void CarlaBridgeClient::toolkitResize(const int width, const int height) | |||
| { | |||
| qDebug("CarlaBridgeClient::toolkitResize(%i, %i)", width, height); | |||
| m_toolkit->resize(width, height); | |||
| } | |||
| void CarlaBridgeClient::toolkitExec(const bool showGui) | |||
| { | |||
| qDebug("CarlaBridgeClient::toolkitExec(%s)", bool2str(showGui)); | |||
| m_toolkit->exec(showGui); | |||
| } | |||
| void CarlaBridgeClient::toolkitQuit() | |||
| { | |||
| qDebug("CarlaBridgeClient::toolkitQuit()"); | |||
| #ifdef BUILD_BRIDGE_UI | |||
| m_uiQuit = true; | |||
| #endif | |||
| m_toolkit->quit(); | |||
| } | |||
| // --------------------------------------------------------------------- | |||
| void CarlaBridgeClient::sendOscConfigure(const char* const key, const char* const value) | |||
| { | |||
| qDebug("CarlaBridgeClient::sendOscConfigure(\"%s\", \"%s\")", key, value); | |||
| CARLA_ASSERT(m_oscData); | |||
| if (m_oscData && m_oscData->target) | |||
| osc_send_configure(m_oscData, key, value); | |||
| } | |||
| void CarlaBridgeClient::sendOscControl(const int32_t index, const float value) | |||
| { | |||
| qDebug("CarlaBridgeClient::sendOscControl(%i, %f)", index, value); | |||
| CARLA_ASSERT(m_oscData); | |||
| if (m_oscData && m_oscData->target) | |||
| osc_send_control(m_oscData, index, value); | |||
| } | |||
| void CarlaBridgeClient::sendOscProgram(const int32_t index) | |||
| { | |||
| qDebug("CarlaBridgeClient::sendOscProgram(%i)", index); | |||
| CARLA_ASSERT(m_oscData); | |||
| if (m_oscData && m_oscData->target) | |||
| osc_send_program(m_oscData, index); | |||
| } | |||
| void CarlaBridgeClient::sendOscMidiProgram(const int32_t index) | |||
| { | |||
| qDebug("CarlaBridgeClient::sendOscMidiProgram(%i)", index); | |||
| CARLA_ASSERT(m_oscData); | |||
| if (m_oscData && m_oscData->target) | |||
| osc_send_midi_program(m_oscData, index); | |||
| } | |||
| void CarlaBridgeClient::sendOscMidi(const uint8_t midiBuf[4]) | |||
| { | |||
| qDebug("CarlaBridgeClient::sendOscMidi(%p)", midiBuf); | |||
| CARLA_ASSERT(m_oscData); | |||
| if (m_oscData && m_oscData->target) | |||
| osc_send_midi(m_oscData, midiBuf); | |||
| } | |||
| void CarlaBridgeClient::sendOscExiting() | |||
| { | |||
| qDebug("CarlaBridgeClient::sendOscExiting()"); | |||
| CARLA_ASSERT(m_oscData); | |||
| if (m_oscData && m_oscData->target) | |||
| osc_send_exiting(m_oscData); | |||
| } | |||
| #ifdef BRIDGE_LV2 | |||
| void CarlaBridgeClient::sendOscLv2TransferAtom(const int32_t portIndex, const char* const typeStr, const char* const atomBuf) | |||
| { | |||
| qDebug("CarlaBridgeClient::sendOscLv2TransferAtom(%i, \"%s\", \"%s\")", portIndex, typeStr, atomBuf); | |||
| CARLA_ASSERT(m_oscData); | |||
| if (m_oscData && m_oscData->target) | |||
| osc_send_lv2_transfer_atom(m_oscData, portIndex, typeStr, atomBuf); | |||
| } | |||
| void CarlaBridgeClient::sendOscLv2TransferEvent(const int32_t portIndex, const char* const typeStr, const char* const atomBuf) | |||
| { | |||
| qDebug("CarlaBridgeClient::sendOscLv2TransferEvent(%i, \"%s\", \"%s\")", portIndex, typeStr, atomBuf); | |||
| CARLA_ASSERT(m_oscData); | |||
| if (m_oscData && m_oscData->target) | |||
| osc_send_lv2_transfer_event(m_oscData, portIndex, typeStr, atomBuf); | |||
| } | |||
| #endif | |||
| // --------------------------------------------------------------------- | |||
| void* CarlaBridgeClient::getContainerId() | |||
| { | |||
| return m_toolkit->getContainerId(); | |||
| } | |||
| #ifdef BUILD_BRIDGE_UI | |||
| bool CarlaBridgeClient::uiLibOpen(const char* const filename) | |||
| { | |||
| CARLA_ASSERT(! m_uiLib); | |||
| CARLA_ASSERT(filename); | |||
| if (m_uiFilename) | |||
| free(m_uiFilename); | |||
| m_uiLib = lib_open(filename); | |||
| m_uiFilename = strdup(filename ? filename : ""); | |||
| return bool(m_uiLib); | |||
| } | |||
| bool CarlaBridgeClient::uiLibClose() | |||
| { | |||
| CARLA_ASSERT(m_uiLib); | |||
| if (m_uiLib) | |||
| { | |||
| const bool closed = lib_close(m_uiLib); | |||
| m_uiLib = nullptr; | |||
| return closed; | |||
| } | |||
| return false; | |||
| } | |||
| void* CarlaBridgeClient::uiLibSymbol(const char* const symbol) | |||
| { | |||
| CARLA_ASSERT(m_uiLib); | |||
| if (m_uiLib) | |||
| return lib_symbol(m_uiLib, symbol); | |||
| return nullptr; | |||
| } | |||
| const char* CarlaBridgeClient::uiLibError() | |||
| { | |||
| return lib_error(m_uiFilename); | |||
| } | |||
| #endif | |||
| CARLA_BRIDGE_END_NAMESPACE | |||
| @@ -0,0 +1,153 @@ | |||
| /* | |||
| * Carla Bridge Client | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #ifndef CARLA_BRIDGE_CLIENT_HPP | |||
| #define CARLA_BRIDGE_CLIENT_HPP | |||
| #include "carla_bridge_osc.hpp" | |||
| CARLA_BRIDGE_START_NAMESPACE | |||
| #if 0 | |||
| } // Fix editor indentation | |||
| #endif | |||
| /*! | |||
| * @defgroup CarlaBridgeClient Carla Bridge Client | |||
| * | |||
| * The Carla Bridge Client. | |||
| * @{ | |||
| */ | |||
| class CarlaBridgeClient | |||
| { | |||
| public: | |||
| CarlaBridgeClient(const char* const uiTitle); | |||
| virtual ~CarlaBridgeClient(); | |||
| #ifdef BUILD_BRIDGE_UI | |||
| // --------------------------------------------------------------------- | |||
| // ui initialization | |||
| virtual bool init(const char* const, const char* const); | |||
| virtual void close(); | |||
| #endif | |||
| #ifdef BUILD_BRIDGE_UI | |||
| // --------------------------------------------------------------------- | |||
| // ui management | |||
| virtual void* getWidget() const = 0; | |||
| virtual bool isResizable() const = 0; | |||
| virtual bool needsReparent() const = 0; | |||
| #endif | |||
| // --------------------------------------------------------------------- | |||
| // processing | |||
| virtual void setParameter(const int32_t rindex, const double value) = 0; | |||
| virtual void setProgram(const uint32_t index) = 0; | |||
| #ifdef BUILD_BRIDGE_PLUGIN | |||
| virtual void setMidiProgram(const uint32_t index) = 0; | |||
| #endif | |||
| #ifdef BUILD_BRIDGE_UI | |||
| virtual void setMidiProgram(const uint32_t bank, const uint32_t program) = 0; | |||
| #endif | |||
| virtual void noteOn(const uint8_t channel, const uint8_t note, const uint8_t velo) = 0; | |||
| virtual void noteOff(const uint8_t channel, const uint8_t note) = 0; | |||
| #ifdef BUILD_BRIDGE_PLUGIN | |||
| // --------------------------------------------------------------------- | |||
| // plugin | |||
| virtual void saveNow() = 0; | |||
| virtual void setCustomData(const char* const type, const char* const key, const char* const value) = 0; | |||
| virtual void setChunkData(const char* const filePath) = 0; | |||
| #endif | |||
| // --------------------------------------------------------------------- | |||
| // osc stuff | |||
| bool oscInit(const char* const url); | |||
| bool oscIdle(); | |||
| void oscClose(); | |||
| bool isOscControlRegistered() const; | |||
| void sendOscUpdate(); | |||
| #ifdef BUILD_BRIDGE_PLUGIN | |||
| void sendOscBridgeUpdate(); | |||
| void sendOscBridgeError(const char* const error); | |||
| #endif | |||
| // --------------------------------------------------------------------- | |||
| // toolkit | |||
| void toolkitShow(); | |||
| void toolkitHide(); | |||
| void toolkitResize(const int width, const int height); | |||
| void toolkitExec(const bool showGui); | |||
| void toolkitQuit(); | |||
| // --------------------------------------------------------------------- | |||
| protected: | |||
| void sendOscConfigure(const char* const key, const char* const value); | |||
| void sendOscControl(const int32_t index, const float value); | |||
| void sendOscProgram(const int32_t index); | |||
| void sendOscMidiProgram(const int32_t index); | |||
| void sendOscMidi(const uint8_t midiBuf[4]); | |||
| void sendOscExiting(); | |||
| #ifdef BRIDGE_LV2 | |||
| void sendOscLv2TransferAtom(const int32_t portIndex, const char* const typeStr, const char* const atomBuf); | |||
| void sendOscLv2TransferEvent(const int32_t portIndex, const char* const typeStr, const char* const atomBuf); | |||
| #endif | |||
| // --------------------------------------------------------------------- | |||
| void* getContainerId(); | |||
| #ifdef BUILD_BRIDGE_UI | |||
| bool uiLibOpen(const char* const filename); | |||
| bool uiLibClose(); | |||
| void* uiLibSymbol(const char* const symbol); | |||
| const char* uiLibError(); | |||
| #endif | |||
| // --------------------------------------------------------------------- | |||
| private: | |||
| CarlaBridgeOsc m_osc; | |||
| CarlaBridgeToolkit* const m_toolkit; | |||
| const CarlaOscData* m_oscData; | |||
| #ifdef BUILD_BRIDGE_UI | |||
| char* m_uiFilename; | |||
| void* m_uiLib; | |||
| bool m_uiQuit; | |||
| #else | |||
| friend class CarlaPluginClient; | |||
| #endif | |||
| }; | |||
| /**@}*/ | |||
| CARLA_BRIDGE_END_NAMESPACE | |||
| #endif // CARLA_BRIDGE_CLIENT_HPP | |||
| @@ -0,0 +1,409 @@ | |||
| /* | |||
| * Carla Bridge OSC | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #include "carla_bridge_osc.hpp" | |||
| #include "carla_bridge_client.hpp" | |||
| #include "carla_midi.h" | |||
| #include "carla_utils.hpp" | |||
| #include <QtCore/QString> | |||
| #include <QtCore/QStringList> | |||
| CARLA_BRIDGE_START_NAMESPACE | |||
| // ----------------------------------------------------------------------- | |||
| CarlaBridgeOsc::CarlaBridgeOsc(CarlaBridgeClient* const client_) | |||
| : client(client_) | |||
| { | |||
| qDebug("CarlaBridgeOsc::CarlaBridgeOsc(%p)", client); | |||
| CARLA_ASSERT(client); | |||
| m_name = nullptr; | |||
| m_nameSize = 0; | |||
| m_server = nullptr; | |||
| m_serverPath = nullptr; | |||
| } | |||
| CarlaBridgeOsc::~CarlaBridgeOsc() | |||
| { | |||
| qDebug("CarlaBridgeOsc::~CarlaBridgeOsc()"); | |||
| CARLA_ASSERT(! m_name); | |||
| CARLA_ASSERT(! m_server); | |||
| CARLA_ASSERT(! m_serverPath); | |||
| } | |||
| bool CarlaBridgeOsc::init(const char* const url) | |||
| { | |||
| qDebug("CarlaBridgeOsc::init(\"%s\")", url); | |||
| CARLA_ASSERT(! m_name); | |||
| CARLA_ASSERT(! m_server); | |||
| CARLA_ASSERT(! m_serverPath); | |||
| CARLA_ASSERT(m_nameSize == 0); | |||
| CARLA_ASSERT(url); | |||
| if (! url) | |||
| { | |||
| qWarning("CarlaBridgeOsc::init(\"%s\") - invalid url", url); | |||
| return false; | |||
| } | |||
| #ifdef BUILD_BRIDGE_PLUGIN | |||
| m_name = strdup("carla-bridge-plugin"); | |||
| #else | |||
| m_name = strdup("carla-bridge-ui"); | |||
| #endif | |||
| m_nameSize = strlen(m_name); | |||
| char* const host = lo_url_get_hostname(url); | |||
| char* const path = lo_url_get_path(url); | |||
| char* const port = lo_url_get_port(url); | |||
| if (! host) | |||
| { | |||
| qCritical("CarlaBridgeOsc::init(\"%s\") - failed to get url hostname", url); | |||
| return false; | |||
| } | |||
| if (! path) | |||
| { | |||
| free(host); | |||
| qCritical("CarlaBridgeOsc::init(\"%s\") - failed to get url path", url); | |||
| return false; | |||
| } | |||
| if (! port) | |||
| { | |||
| free(host); | |||
| free(path); | |||
| qCritical("CarlaBridgeOsc::init(\"%s\") - failed to get url port", url); | |||
| return false; | |||
| } | |||
| m_controlData.path = path; | |||
| m_controlData.target = lo_address_new_with_proto(LO_TCP, host, port); | |||
| free(host); | |||
| free(port); | |||
| if (! m_controlData.target) | |||
| { | |||
| qCritical("CarlaBridgeOsc::init(\"%s\") - failed to get new url address for host '%s' and port '%s'", url, host, port); | |||
| return false; | |||
| } | |||
| m_server = lo_server_new_with_proto(nullptr, LO_TCP, osc_error_handler); | |||
| if (! m_server) | |||
| { | |||
| qCritical("CarlaBridgeOsc::init(\"%s\") - failed to create new OSC server", url); | |||
| return false; | |||
| } | |||
| if (char* const serverUrl = lo_server_get_url(m_server)) | |||
| { | |||
| m_serverPath = strdup(QString("%1%2").arg(serverUrl).arg(m_name).toUtf8().constData()); | |||
| free(serverUrl); | |||
| } | |||
| else | |||
| m_serverPath = strdup(QString("%1carla-bridge").arg(serverUrl).toUtf8().constData()); | |||
| lo_server_add_method(m_server, nullptr, nullptr, osc_message_handler, this); | |||
| return true; | |||
| } | |||
| void CarlaBridgeOsc::idle() | |||
| { | |||
| CARLA_ASSERT(m_server); | |||
| if (m_server) | |||
| { | |||
| while (lo_server_recv_noblock(m_server, 0) != 0) {} | |||
| } | |||
| } | |||
| void CarlaBridgeOsc::close() | |||
| { | |||
| qDebug("CarlaBridgeOsc::close()"); | |||
| CARLA_ASSERT(m_name); | |||
| CARLA_ASSERT(m_server); | |||
| CARLA_ASSERT(m_serverPath); | |||
| m_nameSize = 0; | |||
| if (m_name) | |||
| { | |||
| free(m_name); | |||
| m_name = nullptr; | |||
| } | |||
| if (m_server) | |||
| { | |||
| lo_server_del_method(m_server, nullptr, nullptr); | |||
| lo_server_free(m_server); | |||
| m_server = nullptr; | |||
| } | |||
| if (m_serverPath) | |||
| { | |||
| free(m_serverPath); | |||
| m_serverPath = nullptr; | |||
| } | |||
| m_controlData.free(); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| int CarlaBridgeOsc::handleMessage(const char* const path, const int argc, const lo_arg* const* const argv, const char* const types, const lo_message msg) | |||
| { | |||
| qDebug("CarlaBridgeOsc::handleMessage(\"%s\", %i, %p, \"%s\", %p)", path, argc, argv, types, msg); | |||
| CARLA_ASSERT(m_name); | |||
| CARLA_ASSERT(m_server); | |||
| CARLA_ASSERT(m_serverPath); | |||
| CARLA_ASSERT(path); | |||
| if (! path) | |||
| { | |||
| qCritical("CarlaBridgeOsc::handleMessage() - got invalid path"); | |||
| return 1; | |||
| } | |||
| if (! (m_name && m_server && m_serverPath)) | |||
| { | |||
| qCritical("CarlaBridgeOsc::handleMessage(\"%s\", ...) - received message but client is offline", path); | |||
| return 1; | |||
| } | |||
| if (strlen(path) <= m_nameSize || strncmp(path+1, m_name, m_nameSize) != 0) | |||
| { | |||
| qWarning("CarlaBridgeOsc::handleMessage() - message not for this client: '%s' != '/%s/'", path, m_name); | |||
| return 1; | |||
| } | |||
| char method[32] = { 0 }; | |||
| strncpy(method, path + (m_nameSize + 2), 31); | |||
| if (method[0] == '\0') | |||
| { | |||
| qWarning("CarlaBridgeOsc::handleMessage(\"%s\", ...) - received message without method", path); | |||
| return 1; | |||
| } | |||
| // Common OSC methods | |||
| if (strcmp(method, "configure") == 0) | |||
| return handleMsgConfigure(argc, argv, types); | |||
| if (strcmp(method, "control") == 0) | |||
| return handleMsgControl(argc, argv, types); | |||
| if (strcmp(method, "program") == 0) | |||
| return handleMsgProgram(argc, argv, types); | |||
| if (strcmp(method, "midi_program") == 0) | |||
| return handleMsgMidiProgram(argc, argv, types); | |||
| if (strcmp(method, "midi") == 0) | |||
| return handleMsgMidi(argc, argv, types); | |||
| if (strcmp(method, "sample-rate") == 0) | |||
| return 0; // unused | |||
| if (strcmp(method, "show") == 0) | |||
| return handleMsgShow(); | |||
| if (strcmp(method, "hide") == 0) | |||
| return handleMsgHide(); | |||
| if (strcmp(method, "quit") == 0) | |||
| return handleMsgQuit(); | |||
| #ifdef BRIDGE_LV2 | |||
| // LV2 UI methods | |||
| if (strcmp(method, "lv2_atom_transfer") == 0) | |||
| return handleMsgLv2TransferAtom(argc, argv, types); | |||
| if (strcmp(method, "lv2_event_transfer") == 0) | |||
| return handleMsgLv2TransferEvent(argc, argv, types); | |||
| #endif | |||
| #ifdef BUILD_BRIDGE_PLUGIN | |||
| // Plugin methods | |||
| if (strcmp(method, "plugin_save_now") == 0) | |||
| return handleMsgPluginSaveNow(); | |||
| if (strcmp(method, "plugin_set_chunk") == 0) | |||
| return handleMsgPluginSetChunk(argc, argv, types); | |||
| if (strcmp(method, "plugin_set_custom_data") == 0) | |||
| return handleMsgPluginSetCustomData(argc, argv, types); | |||
| #endif | |||
| #if 0 | |||
| // TODO | |||
| else if (strcmp(method, "set_parameter_midi_channel") == 0) | |||
| return osc_set_parameter_midi_channel_handler(argv); | |||
| else if (strcmp(method, "set_parameter_midi_cc") == 0) | |||
| return osc_set_parameter_midi_channel_handler(argv); | |||
| #endif | |||
| qWarning("CarlaBridgeOsc::handleMessage(\"%s\", ...) - received unsupported OSC method '%s'", path, method); | |||
| return 1; | |||
| } | |||
| int CarlaBridgeOsc::handleMsgConfigure(CARLA_BRIDGE_OSC_HANDLE_ARGS) | |||
| { | |||
| qDebug("CarlaBridgeOsc::handleMsgConfigure()"); | |||
| CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(2, "ss"); | |||
| if (! client) | |||
| return 1; | |||
| // nothing here for now | |||
| return 0; | |||
| Q_UNUSED(argv); | |||
| } | |||
| int CarlaBridgeOsc::handleMsgControl(CARLA_BRIDGE_OSC_HANDLE_ARGS) | |||
| { | |||
| qDebug("CarlaBridgeOsc::handleMsgControl()"); | |||
| CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(2, "if"); | |||
| if (! client) | |||
| return 1; | |||
| const int32_t index = argv[0]->i; | |||
| const float value = argv[1]->f; | |||
| client->setParameter(index, value); | |||
| return 0; | |||
| } | |||
| int CarlaBridgeOsc::handleMsgProgram(CARLA_BRIDGE_OSC_HANDLE_ARGS) | |||
| { | |||
| qDebug("CarlaBridgeOsc::handleMsgProgram()"); | |||
| CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(1, "i"); | |||
| if (! client) | |||
| return 1; | |||
| const int32_t index = argv[0]->i; | |||
| client->setProgram(index); | |||
| return 0; | |||
| } | |||
| #ifdef BUILD_BRIDGE_PLUGIN | |||
| int CarlaBridgeOsc::handleMsgMidiProgram(CARLA_BRIDGE_OSC_HANDLE_ARGS) | |||
| { | |||
| qDebug("CarlaBridgeOsc::handleMsgMidiProgram()"); | |||
| CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(1, "i"); | |||
| if (! client) | |||
| return 1; | |||
| const int32_t index = argv[0]->i; | |||
| client->setMidiProgram(index); | |||
| return 0; | |||
| } | |||
| #else | |||
| int CarlaBridgeOsc::handleMsgMidiProgram(CARLA_BRIDGE_OSC_HANDLE_ARGS) | |||
| { | |||
| qDebug("CarlaBridgeOsc::handleMsgMidiProgram()"); | |||
| CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(2, "ii"); | |||
| if (! client) | |||
| return 1; | |||
| const int32_t bank = argv[0]->i; | |||
| const int32_t program = argv[1]->i; | |||
| client->setMidiProgram(bank, program); | |||
| return 0; | |||
| } | |||
| #endif | |||
| int CarlaBridgeOsc::handleMsgMidi(CARLA_BRIDGE_OSC_HANDLE_ARGS) | |||
| { | |||
| qDebug("CarlaBridgeOsc::handleMsgMidi()"); | |||
| CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(1, "m"); | |||
| if (! client) | |||
| return 1; | |||
| const uint8_t* const mdata = argv[0]->m; | |||
| const uint8_t data[4] = { mdata[0], mdata[1], mdata[2], mdata[3] }; | |||
| uint8_t status = data[1]; | |||
| uint8_t channel = status & 0x0F; | |||
| // Fix bad note-off | |||
| if (MIDI_IS_STATUS_NOTE_ON(status) && data[3] == 0) | |||
| status -= 0x10; | |||
| if (MIDI_IS_STATUS_NOTE_OFF(status)) | |||
| { | |||
| const uint8_t note = data[2]; | |||
| client->noteOff(channel, note); | |||
| } | |||
| else if (MIDI_IS_STATUS_NOTE_ON(status)) | |||
| { | |||
| const uint8_t note = data[2]; | |||
| const uint8_t velo = data[3]; | |||
| client->noteOn(channel, note, velo); | |||
| } | |||
| return 0; | |||
| } | |||
| int CarlaBridgeOsc::handleMsgShow() | |||
| { | |||
| qDebug("CarlaBridgeOsc::handleMsgShow()"); | |||
| if (! client) | |||
| return 1; | |||
| client->toolkitShow(); | |||
| return 0; | |||
| } | |||
| int CarlaBridgeOsc::handleMsgHide() | |||
| { | |||
| qDebug("CarlaBridgeOsc::handleMsgHide()"); | |||
| if (! client) | |||
| return 1; | |||
| client->toolkitHide(); | |||
| return 0; | |||
| } | |||
| int CarlaBridgeOsc::handleMsgQuit() | |||
| { | |||
| qDebug("CarlaBridgeOsc::handleMsgQuit()"); | |||
| if (! client) | |||
| return 1; | |||
| client->toolkitQuit(); | |||
| return 0; | |||
| } | |||
| CARLA_BRIDGE_END_NAMESPACE | |||
| @@ -0,0 +1,137 @@ | |||
| /* | |||
| * Carla Bridge OSC | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #ifndef CARLA_BRIDGE_OSC_HPP | |||
| #define CARLA_BRIDGE_OSC_HPP | |||
| #include "carla_bridge.hpp" | |||
| #include "carla_osc_utils.hpp" | |||
| #define CARLA_BRIDGE_OSC_HANDLE_ARGS const int argc, const lo_arg* const* const argv, const char* const types | |||
| #define CARLA_BRIDGE_OSC_CHECK_OSC_TYPES(/* argc, types, */ argcToCompare, typesToCompare) \ | |||
| /* check argument count */ \ | |||
| if (argc != argcToCompare) \ | |||
| { \ | |||
| qCritical("CarlaBridgeOsc::%s() - argument count mismatch: %i != %i", __FUNCTION__, argc, argcToCompare); \ | |||
| return 1; \ | |||
| } \ | |||
| if (argc > 0) \ | |||
| { \ | |||
| /* check for nullness */ \ | |||
| if (! (types && typesToCompare)) \ | |||
| { \ | |||
| qCritical("CarlaBridgeOsc::%s() - argument types are null", __FUNCTION__); \ | |||
| return 1; \ | |||
| } \ | |||
| /* check argument types */ \ | |||
| if (strcmp(types, typesToCompare) != 0) \ | |||
| { \ | |||
| qCritical("CarlaBridgeOsc::%s() - argument types mismatch: '%s' != '%s'", __FUNCTION__, types, typesToCompare); \ | |||
| return 1; \ | |||
| } \ | |||
| } | |||
| CARLA_BRIDGE_START_NAMESPACE | |||
| #if 0 | |||
| } // Fix editor indentation | |||
| #endif | |||
| class CarlaBridgeOsc | |||
| { | |||
| public: | |||
| CarlaBridgeOsc(CarlaBridgeClient* const client); | |||
| ~CarlaBridgeOsc(); | |||
| bool init(const char* const url); | |||
| void idle(); | |||
| void close(); | |||
| // ------------------------------------------------------------------- | |||
| bool isControlRegistered() const | |||
| { | |||
| return bool(m_controlData.target); | |||
| } | |||
| const CarlaOscData* getControlData() const | |||
| { | |||
| return &m_controlData; | |||
| } | |||
| const char* getServerPath() const | |||
| { | |||
| return m_serverPath; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| private: | |||
| CarlaBridgeClient* const client; | |||
| char* m_name; | |||
| size_t m_nameSize; | |||
| lo_server m_server; | |||
| char* m_serverPath; | |||
| CarlaOscData m_controlData; | |||
| // ------------------------------------------------------------------- | |||
| int handleMessage(const char* const path, const int argc, const lo_arg* const* const argv, const char* const types, const lo_message msg); | |||
| int handleMsgConfigure(CARLA_BRIDGE_OSC_HANDLE_ARGS); | |||
| int handleMsgControl(CARLA_BRIDGE_OSC_HANDLE_ARGS); | |||
| int handleMsgProgram(CARLA_BRIDGE_OSC_HANDLE_ARGS); | |||
| int handleMsgMidiProgram(CARLA_BRIDGE_OSC_HANDLE_ARGS); | |||
| int handleMsgMidi(CARLA_BRIDGE_OSC_HANDLE_ARGS); | |||
| int handleMsgShow(); | |||
| int handleMsgHide(); | |||
| int handleMsgQuit(); | |||
| #ifdef BRIDGE_LV2 | |||
| int handleMsgLv2TransferAtom(CARLA_BRIDGE_OSC_HANDLE_ARGS); | |||
| int handleMsgLv2TransferEvent(CARLA_BRIDGE_OSC_HANDLE_ARGS); | |||
| #endif | |||
| #ifdef BUILD_BRIDGE_PLUGIN | |||
| int handleMsgPluginSaveNow(); | |||
| int handleMsgPluginSetChunk(CARLA_BRIDGE_OSC_HANDLE_ARGS); | |||
| int handleMsgPluginSetCustomData(CARLA_BRIDGE_OSC_HANDLE_ARGS); | |||
| #endif | |||
| // ------------------------------------------------------------------- | |||
| static void osc_error_handler(const int num, const char* const msg, const char* const path) | |||
| { | |||
| qWarning("CarlaBridgeOsc::osc_error_handler(%i, \"%s\", \"%s\")", num, msg, path); | |||
| } | |||
| static int osc_message_handler(const char* const path, const char* const types, lo_arg** const argv, const int argc, const lo_message msg, void* const user_data) | |||
| { | |||
| CARLA_ASSERT(user_data); | |||
| if (CarlaBridgeOsc* const _this_ = (CarlaBridgeOsc*)user_data) | |||
| return _this_->handleMessage(path, argc, argv, types, msg); | |||
| return 1; | |||
| } | |||
| }; | |||
| CARLA_BRIDGE_END_NAMESPACE | |||
| #endif // CARLA_BRIDGE_OSC_HPP | |||
| @@ -0,0 +1,245 @@ | |||
| /* | |||
| * Carla UI bridge code | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #include "carla_bridge_client.hpp" | |||
| #include "carla_bridge_toolkit.hpp" | |||
| #if defined(BRIDGE_COCOA) || defined(BRIDGE_HWND) || defined(BRIDGE_X11) | |||
| # error Embed UI uses Qt | |||
| #endif | |||
| #include <gtk/gtk.h> | |||
| #include <QtCore/QSettings> | |||
| CARLA_BRIDGE_START_NAMESPACE | |||
| // ------------------------------------------------------------------------- | |||
| #if defined(BRIDGE_GTK2) | |||
| static const char* const appName = "Carla-Gtk2UIs"; | |||
| #elif defined(BRIDGE_GTK3) | |||
| static const char* const appName = "Carla-Gtk3UIs"; | |||
| #else | |||
| static const char* const appName = "Carla-UIs"; | |||
| #endif | |||
| static int gargc = 0; | |||
| static char** gargv = {}; | |||
| // ------------------------------------------------------------------------- | |||
| class CarlaToolkitGtk : public CarlaBridgeToolkit | |||
| { | |||
| public: | |||
| CarlaToolkitGtk(CarlaBridgeClient* const client, const char* const uiTitle) | |||
| : CarlaBridgeToolkit(client, uiTitle), | |||
| settings("Cadence", appName) | |||
| { | |||
| qDebug("CarlaToolkitGtk::CarlaToolkitGtk(%p, \"%s\")", client, uiTitle); | |||
| window = nullptr; | |||
| lastX = 0; | |||
| lastY = 0; | |||
| lastWidth = 0; | |||
| lastHeight = 0; | |||
| } | |||
| ~CarlaToolkitGtk() | |||
| { | |||
| qDebug("CarlaToolkitGtk::~CarlaToolkitGtk()"); | |||
| if (window) | |||
| gtk_widget_destroy(window); | |||
| } | |||
| void init() | |||
| { | |||
| qDebug("CarlaToolkitGtk::init()"); | |||
| CARLA_ASSERT(! window); | |||
| gtk_init(&gargc, &gargv); | |||
| window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |||
| gtk_window_resize(GTK_WINDOW(window), 30, 30); | |||
| gtk_widget_hide(window); | |||
| } | |||
| void exec(const bool showGui) | |||
| { | |||
| qDebug("CarlaToolkitGtk::exec(%s)", bool2str(showGui)); | |||
| CARLA_ASSERT(window); | |||
| CARLA_ASSERT(client); | |||
| GtkWidget* const widget = (GtkWidget*)client->getWidget(); | |||
| gtk_container_add(GTK_CONTAINER(window), widget); | |||
| gtk_window_set_resizable(GTK_WINDOW(window), client->isResizable()); | |||
| gtk_window_set_title(GTK_WINDOW(window), uiTitle); | |||
| if (settings.contains(QString("%1/pos_x").arg(uiTitle))) | |||
| { | |||
| gtk_window_get_position(GTK_WINDOW(window), &lastX, &lastY); | |||
| bool hasX, hasY; | |||
| lastX = settings.value(QString("%1/pos_x").arg(uiTitle), lastX).toInt(&hasX); | |||
| lastY = settings.value(QString("%1/pos_y").arg(uiTitle), lastY).toInt(&hasY); | |||
| if (hasX && hasY) | |||
| gtk_window_move(GTK_WINDOW(window), lastX, lastY); | |||
| if (client->isResizable()) | |||
| { | |||
| gtk_window_get_size(GTK_WINDOW(window), &lastWidth, &lastHeight); | |||
| bool hasWidth, hasHeight; | |||
| lastWidth = settings.value(QString("%1/width").arg(uiTitle), lastWidth).toInt(&hasWidth); | |||
| lastHeight = settings.value(QString("%1/height").arg(uiTitle), lastHeight).toInt(&hasHeight); | |||
| if (hasWidth && hasHeight) | |||
| gtk_window_resize(GTK_WINDOW(window), lastWidth, lastHeight); | |||
| } | |||
| } | |||
| if (showGui) | |||
| show(); | |||
| else | |||
| client->sendOscUpdate(); | |||
| // Timer | |||
| g_timeout_add(50, gtk_ui_timeout, this); | |||
| g_signal_connect(window, "destroy", G_CALLBACK(gtk_ui_destroy), this); | |||
| // First idle | |||
| handleTimeout(); | |||
| // Main loop | |||
| gtk_main(); | |||
| } | |||
| void quit() | |||
| { | |||
| qDebug("CarlaToolkitGtk::quit()"); | |||
| if (window) | |||
| { | |||
| gtk_widget_destroy(window); | |||
| gtk_main_quit(); | |||
| window = nullptr; | |||
| } | |||
| } | |||
| void show() | |||
| { | |||
| qDebug("CarlaToolkitGtk::show()"); | |||
| CARLA_ASSERT(window); | |||
| if (window) | |||
| gtk_widget_show_all(window); | |||
| } | |||
| void hide() | |||
| { | |||
| qDebug("CarlaToolkitGtk::hide()"); | |||
| CARLA_ASSERT(window); | |||
| if (window) | |||
| { | |||
| #ifdef BRIDGE_GTK2 | |||
| gtk_widget_hide_all(window); | |||
| #else | |||
| gtk_widget_hide(window); | |||
| #endif | |||
| } | |||
| } | |||
| void resize(int width, int height) | |||
| { | |||
| qDebug("CarlaToolkitGtk::resize(%i, %i)", width, height); | |||
| CARLA_ASSERT(window); | |||
| if (window) | |||
| gtk_window_resize(GTK_WINDOW(window), width, height); | |||
| } | |||
| // --------------------------------------------------------------------- | |||
| protected: | |||
| GtkWidget* window; | |||
| QSettings settings; | |||
| gint lastX, lastY, lastWidth, lastHeight; | |||
| void handleDestroy() | |||
| { | |||
| qDebug("CarlaToolkitGtk::handleDestroy()"); | |||
| window = nullptr; | |||
| settings.setValue(QString("%1/pos_x").arg(uiTitle), lastX); | |||
| settings.setValue(QString("%1/pos_y").arg(uiTitle), lastY); | |||
| settings.setValue(QString("%1/width").arg(uiTitle), lastWidth); | |||
| settings.setValue(QString("%1/height").arg(uiTitle), lastHeight); | |||
| settings.sync(); | |||
| } | |||
| gboolean handleTimeout() | |||
| { | |||
| if (window) | |||
| { | |||
| gtk_window_get_position(GTK_WINDOW(window), &lastX, &lastY); | |||
| gtk_window_get_size(GTK_WINDOW(window), &lastWidth, &lastHeight); | |||
| } | |||
| // FIXME? | |||
| return client->isOscControlRegistered() ? client->oscIdle() : false; | |||
| } | |||
| // --------------------------------------------------------------------- | |||
| private: | |||
| static void gtk_ui_destroy(GtkWidget*, gpointer data) | |||
| { | |||
| CARLA_ASSERT(data); | |||
| if (CarlaToolkitGtk* const _this_ = (CarlaToolkitGtk*)data) | |||
| _this_->handleDestroy(); | |||
| gtk_main_quit(); | |||
| } | |||
| static gboolean gtk_ui_timeout(gpointer data) | |||
| { | |||
| CARLA_ASSERT(data); | |||
| if (CarlaToolkitGtk* const _this_ = (CarlaToolkitGtk*)data) | |||
| return _this_->handleTimeout(); | |||
| return false; | |||
| } | |||
| }; | |||
| // ------------------------------------------------------------------------- | |||
| CarlaBridgeToolkit* CarlaBridgeToolkit::createNew(CarlaBridgeClient* const client, const char* const uiTitle) | |||
| { | |||
| return new CarlaToolkitGtk(client, uiTitle); | |||
| } | |||
| CARLA_BRIDGE_END_NAMESPACE | |||
| @@ -0,0 +1,155 @@ | |||
| /* | |||
| * Carla Plugin bridge code | |||
| * Copyright (C) 2012 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 COPYING file | |||
| */ | |||
| #include "carla_bridge_client.hpp" | |||
| #include "carla_bridge_toolkit.hpp" | |||
| #include "carla_plugin.hpp" | |||
| CARLA_BRIDGE_START_NAMESPACE | |||
| static int qargc = 0; | |||
| static char* qargv[0] = {}; | |||
| // ------------------------------------------------------------------------- | |||
| class CarlaBridgeToolkitPlugin : public CarlaBridgeToolkit, | |||
| public CarlaBackend::CarlaPluginGUI::Callback | |||
| { | |||
| public: | |||
| CarlaBridgeToolkitPlugin(CarlaBridgeClient* const client, const char* const uiTitle) | |||
| : CarlaBridgeToolkit(client, uiTitle) | |||
| { | |||
| qDebug("CarlaBridgeToolkitPlugin::CarlaBridgeToolkitPlugin(%p, \"%s\")", client, uiTitle); | |||
| app = nullptr; | |||
| gui = nullptr; | |||
| m_uiQuit = false; | |||
| init(); | |||
| } | |||
| ~CarlaBridgeToolkitPlugin() | |||
| { | |||
| qDebug("CarlaBridgeToolkitPlugin::~CarlaBridgeToolkitPlugin()"); | |||
| CARLA_ASSERT(! app); | |||
| CARLA_ASSERT(! gui); | |||
| } | |||
| void init() | |||
| { | |||
| qDebug("CarlaBridgeToolkitPlugin::init()"); | |||
| CARLA_ASSERT(! app); | |||
| CARLA_ASSERT(! gui); | |||
| app = new QApplication(qargc, qargv); | |||
| gui = new CarlaBackend::CarlaPluginGUI(nullptr, this); | |||
| } | |||
| void exec(const bool showGui) | |||
| { | |||
| qDebug("CarlaBridgeToolkitPlugin::exec(%s)", bool2str(showGui)); | |||
| CARLA_ASSERT(app); | |||
| CARLA_ASSERT(gui); | |||
| CARLA_ASSERT(client); | |||
| if (showGui) | |||
| { | |||
| show(); | |||
| } | |||
| else | |||
| { | |||
| app->setQuitOnLastWindowClosed(false); | |||
| client->sendOscUpdate(); | |||
| client->sendOscBridgeUpdate(); | |||
| } | |||
| m_uiQuit = showGui; | |||
| // Main loop | |||
| app->exec(); | |||
| } | |||
| void quit() | |||
| { | |||
| qDebug("CarlaBridgeToolkitPlugin::quit()"); | |||
| CARLA_ASSERT(app); | |||
| if (gui) | |||
| { | |||
| gui->close(); | |||
| delete gui; | |||
| gui = nullptr; | |||
| } | |||
| if (app) | |||
| { | |||
| if (! app->closingDown()) | |||
| app->quit(); | |||
| delete app; | |||
| app = nullptr; | |||
| } | |||
| } | |||
| void show() | |||
| { | |||
| qDebug("CarlaBridgeToolkitPlugin::show()"); | |||
| CARLA_ASSERT(gui); | |||
| if (gui && m_uiShow) | |||
| gui->setVisible(true); | |||
| } | |||
| void hide() | |||
| { | |||
| qDebug("CarlaBridgeToolkitPlugin::hide()"); | |||
| CARLA_ASSERT(gui); | |||
| if (gui && m_uiShow) | |||
| gui->setVisible(false); | |||
| } | |||
| void resize(const int width, const int height) | |||
| { | |||
| qDebug("CarlaBridgeToolkitPlugin::resize(%i, %i)", width, height); | |||
| CARLA_ASSERT(gui); | |||
| if (gui) | |||
| gui->setNewSize(width, height); | |||
| } | |||
| protected: | |||
| QApplication* app; | |||
| CarlaBackend::CarlaPluginGUI* gui; | |||
| void guiClosedCallback(); | |||
| private: | |||
| bool m_uiQuit; | |||
| }; | |||
| // ------------------------------------------------------------------------- | |||
| CarlaBridgeToolkit* CarlaBridgeToolkit::createNew(CarlaBridgeClient* const client, const char* const uiTitle) | |||
| { | |||
| return new CarlaBridgeToolkitPlugin(client, uiTitle); | |||
| } | |||
| CARLA_BRIDGE_END_NAMESPACE | |||
| @@ -0,0 +1,334 @@ | |||
| /* | |||
| * Carla Bridge Toolkit, Qt version | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #include "carla_bridge_client.hpp" | |||
| #include "carla_bridge_toolkit.hpp" | |||
| #include <QtCore/QSettings> | |||
| #include <QtCore/QThread> | |||
| #include <QtCore/QTimerEvent> | |||
| #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) | |||
| # include <QtWidgets/QApplication> | |||
| # include <QtWidgets/QMainWindow> | |||
| # ifdef Q_WS_X11 | |||
| # undef Q_WS_X11 | |||
| # endif | |||
| #else | |||
| # include <QtGui/QApplication> | |||
| # include <QtGui/QMainWindow> | |||
| # ifdef Q_WS_X11 | |||
| # include <QtGui/QX11EmbedContainer> | |||
| # endif | |||
| #endif | |||
| #if defined(BRIDGE_COCOA) || defined(BRIDGE_HWND) || defined(BRIDGE_X11) | |||
| # define BRIDGE_CONTAINER | |||
| # ifdef Q_WS_X11 | |||
| typedef QX11EmbedContainer QEmbedContainer; | |||
| # else | |||
| typedef QWidget QEmbedContainer; | |||
| # endif | |||
| #endif | |||
| CARLA_BRIDGE_START_NAMESPACE | |||
| // ------------------------------------------------------------------------- | |||
| #if defined(BRIDGE_QT4) | |||
| static const char* const appName = "Carla-Qt4UIs"; | |||
| #elif defined(BRIDGE_QT5) | |||
| static const char* const appName = "Carla-Qt5UIs"; | |||
| #elif defined(BRIDGE_COCOA) | |||
| static const char* const appName = "Carla-CocoaUIs"; | |||
| #elif defined(BRIDGE_HWND) | |||
| static const char* const appName = "Carla-HWNDUIs"; | |||
| #elif defined(BRIDGE_X11) | |||
| static const char* const appName = "Carla-X11UIs"; | |||
| #else | |||
| static const char* const appName = "Carla-UIs"; | |||
| #endif | |||
| static int qargc = 0; | |||
| static char* qargv[0] = {}; | |||
| // ------------------------------------------------------------------------- | |||
| class CarlaBridgeToolkitQt: public CarlaBridgeToolkit, | |||
| public QObject | |||
| { | |||
| public: | |||
| CarlaBridgeToolkitQt(CarlaBridgeClient* const client, const char* const uiTitle) | |||
| : CarlaBridgeToolkit(client, uiTitle), | |||
| QObject(nullptr), | |||
| settings("Cadence", appName) | |||
| { | |||
| qDebug("CarlaBridgeToolkitQt::CarlaBridgeToolkitQt(%p, \"%s\")", client, uiTitle); | |||
| app = nullptr; | |||
| window = nullptr; | |||
| msgTimer = 0; | |||
| needsResize = false; | |||
| nextWidth = 0; | |||
| nextHeight = 0; | |||
| #ifdef BRIDGE_CONTAINER | |||
| embedContainer = nullptr; | |||
| #endif | |||
| } | |||
| ~CarlaBridgeToolkitQt() | |||
| { | |||
| qDebug("CarlaBridgeToolkitQt::~CarlaBridgeToolkitQt()"); | |||
| CARLA_ASSERT(! app); | |||
| CARLA_ASSERT(! window); | |||
| CARLA_ASSERT(! msgTimer); | |||
| } | |||
| void init() | |||
| { | |||
| qDebug("CarlaBridgeToolkitQt::init()"); | |||
| CARLA_ASSERT(! app); | |||
| CARLA_ASSERT(! window); | |||
| CARLA_ASSERT(! msgTimer); | |||
| app = new QApplication(qargc, qargv); | |||
| window = new QMainWindow(nullptr); | |||
| window->resize(30, 30); | |||
| window->hide(); | |||
| } | |||
| void exec(const bool showGui) | |||
| { | |||
| qDebug("CarlaBridgeToolkitQt::exec(%s)", bool2str(showGui)); | |||
| CARLA_ASSERT(app); | |||
| CARLA_ASSERT(window); | |||
| CARLA_ASSERT(client); | |||
| #if defined(BRIDGE_QT4) || defined(BRIDGE_QT5) | |||
| QWidget* const widget = (QWidget*)client->getWidget(); | |||
| window->setCentralWidget(widget); | |||
| window->adjustSize(); | |||
| widget->setParent(window); | |||
| widget->show(); | |||
| #endif | |||
| if (! client->isResizable()) | |||
| { | |||
| window->setFixedSize(window->width(), window->height()); | |||
| #ifdef Q_OS_WIN | |||
| window->setWindowFlags(window->windowFlags() | Qt::MSWindowsFixedSizeDialogHint); | |||
| #endif | |||
| } | |||
| window->setWindowTitle(uiTitle); | |||
| if (settings.contains(QString("%1/pos_x").arg(uiTitle))) | |||
| { | |||
| bool hasX, hasY; | |||
| int posX = settings.value(QString("%1/pos_x").arg(uiTitle), window->x()).toInt(&hasX); | |||
| int posY = settings.value(QString("%1/pos_y").arg(uiTitle), window->y()).toInt(&hasY); | |||
| if (hasX && hasY) | |||
| window->move(posX, posY); | |||
| if (client->isResizable()) | |||
| { | |||
| bool hasWidth, hasHeight; | |||
| int width = settings.value(QString("%1/width").arg(uiTitle), window->width()).toInt(&hasWidth); | |||
| int height = settings.value(QString("%1/height").arg(uiTitle), window->height()).toInt(&hasHeight); | |||
| if (hasWidth && hasHeight) | |||
| window->resize(width, height); | |||
| } | |||
| } | |||
| if (showGui) | |||
| show(); | |||
| else | |||
| client->sendOscUpdate(); | |||
| // Timer | |||
| msgTimer = startTimer(50); | |||
| // First idle | |||
| handleTimeout(); | |||
| // Main loop | |||
| app->exec(); | |||
| } | |||
| void quit() | |||
| { | |||
| qDebug("CarlaBridgeToolkitQt::quit()"); | |||
| CARLA_ASSERT(app); | |||
| if (msgTimer != 0) | |||
| { | |||
| killTimer(msgTimer); | |||
| msgTimer = 0; | |||
| } | |||
| if (window) | |||
| { | |||
| settings.setValue(QString("%1/pos_x").arg(uiTitle), window->x()); | |||
| settings.setValue(QString("%1/pos_y").arg(uiTitle), window->y()); | |||
| settings.setValue(QString("%1/width").arg(uiTitle), window->width()); | |||
| settings.setValue(QString("%1/height").arg(uiTitle), window->height()); | |||
| settings.sync(); | |||
| window->close(); | |||
| delete window; | |||
| window = nullptr; | |||
| } | |||
| #ifdef BRIDGE_CONTAINER | |||
| if (embedContainer) | |||
| { | |||
| embedContainer->close(); | |||
| delete embedContainer; | |||
| embedContainer = nullptr; | |||
| } | |||
| #endif | |||
| if (app) | |||
| { | |||
| if (! app->closingDown()) | |||
| app->quit(); | |||
| delete app; | |||
| app = nullptr; | |||
| } | |||
| } | |||
| void show() | |||
| { | |||
| qDebug("CarlaBridgeToolkitQt::show()"); | |||
| CARLA_ASSERT(window); | |||
| if (window) | |||
| window->show(); | |||
| } | |||
| void hide() | |||
| { | |||
| qDebug("CarlaBridgeToolkitQt::hide()"); | |||
| CARLA_ASSERT(window); | |||
| if (window) | |||
| window->hide(); | |||
| } | |||
| void resize(const int width, const int height) | |||
| { | |||
| qDebug("CarlaBridgeToolkitQt::resize(%i, %i)", width, height); | |||
| CARLA_ASSERT(window); | |||
| if (app->thread() != QThread::currentThread()) | |||
| { | |||
| nextWidth = width; | |||
| nextHeight = height; | |||
| needsResize = true; | |||
| return; | |||
| } | |||
| if (window) | |||
| window->setFixedSize(width, height); | |||
| #ifdef BRIDGE_CONTAINER | |||
| if (embedContainer) | |||
| embedContainer->setFixedSize(width, height); | |||
| #endif | |||
| } | |||
| #ifdef BRIDGE_CONTAINER | |||
| void* getContainerId() | |||
| { | |||
| qDebug("CarlaBridgeToolkitQt::getContainerId()"); | |||
| CARLA_ASSERT(window); | |||
| if (! embedContainer) | |||
| { | |||
| embedContainer = new QEmbedContainer(window); | |||
| window->setCentralWidget(embedContainer); | |||
| window->adjustSize(); | |||
| embedContainer->setParent(window); | |||
| embedContainer->show(); | |||
| } | |||
| return (void*)embedContainer->winId(); | |||
| } | |||
| #endif | |||
| protected: | |||
| QApplication* app; | |||
| QMainWindow* window; | |||
| QSettings settings; | |||
| int msgTimer; | |||
| bool needsResize; | |||
| int nextWidth, nextHeight; | |||
| #ifdef BRIDGE_CONTAINER | |||
| QEmbedContainer* embedContainer; | |||
| #endif | |||
| void handleTimeout() | |||
| { | |||
| if (! client) | |||
| return; | |||
| if (needsResize) | |||
| { | |||
| client->toolkitResize(nextWidth, nextHeight); | |||
| needsResize = false; | |||
| } | |||
| if (client->isOscControlRegistered() && ! client->oscIdle()) | |||
| { | |||
| killTimer(msgTimer); | |||
| msgTimer = 0; | |||
| } | |||
| } | |||
| private: | |||
| void timerEvent(QTimerEvent* const event) | |||
| { | |||
| if (event->timerId() == msgTimer) | |||
| handleTimeout(); | |||
| QObject::timerEvent(event); | |||
| } | |||
| }; | |||
| // ------------------------------------------------------------------------- | |||
| CarlaBridgeToolkit* CarlaBridgeToolkit::createNew(CarlaBridgeClient* const client, const char* const uiTitle) | |||
| { | |||
| return new CarlaBridgeToolkitQt(client, uiTitle); | |||
| } | |||
| CARLA_BRIDGE_END_NAMESPACE | |||
| @@ -0,0 +1,49 @@ | |||
| /* | |||
| * Carla Bridge Toolkit | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #include "carla_bridge_toolkit.hpp" | |||
| #include "carla_utils.hpp" | |||
| #include <cstdlib> | |||
| #include <cstring> | |||
| CARLA_BRIDGE_START_NAMESPACE | |||
| CarlaBridgeToolkit::CarlaBridgeToolkit(CarlaBridgeClient* const client_, const char* const newTitle) | |||
| : client(client_) | |||
| { | |||
| qDebug("CarlaBridgeToolkit::CarlaBridgeToolkit(%p, \"%s\")", client, newTitle); | |||
| CARLA_ASSERT(client); | |||
| CARLA_ASSERT(newTitle); | |||
| uiTitle = strdup(newTitle ? newTitle : "(null)"); | |||
| } | |||
| CarlaBridgeToolkit::~CarlaBridgeToolkit() | |||
| { | |||
| qDebug("CarlaBridgeToolkit::~CarlaBridgeToolkit()"); | |||
| free(uiTitle); | |||
| } | |||
| void* CarlaBridgeToolkit::getContainerId() | |||
| { | |||
| qDebug("CarlaBridgeToolkit::getContainerId()"); | |||
| return nullptr; | |||
| } | |||
| CARLA_BRIDGE_END_NAMESPACE | |||
| @@ -0,0 +1,63 @@ | |||
| /* | |||
| * Carla Bridge Toolkit | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #ifndef CARLA_BRIDGE_TOOLKIT_HPP | |||
| #define CARLA_BRIDGE_TOOLKIT_HPP | |||
| #include "carla_bridge.hpp" | |||
| CARLA_BRIDGE_START_NAMESPACE | |||
| #if 0 | |||
| } // Fix editor indentation | |||
| #endif | |||
| /*! | |||
| * @defgroup CarlaBridgeToolkit Carla Bridge Toolkit | |||
| * | |||
| * The Carla Bridge Toolkit. | |||
| * @{ | |||
| */ | |||
| class CarlaBridgeToolkit | |||
| { | |||
| public: | |||
| CarlaBridgeToolkit(CarlaBridgeClient* const client, const char* const uiTitle); | |||
| virtual ~CarlaBridgeToolkit(); | |||
| virtual void init() = 0; | |||
| virtual void exec(const bool showGui) = 0; | |||
| virtual void quit() = 0; | |||
| virtual void show() = 0; | |||
| virtual void hide() = 0; | |||
| virtual void resize(const int width, const int height) = 0; | |||
| virtual void* getContainerId(); | |||
| static CarlaBridgeToolkit* createNew(CarlaBridgeClient* const client, const char* const uiTitle); | |||
| protected: | |||
| CarlaBridgeClient* const client; | |||
| char* uiTitle; | |||
| }; | |||
| /**@}*/ | |||
| CARLA_BRIDGE_END_NAMESPACE | |||
| #endif // CARLA_BRIDGE_TOOLKIT_HPP | |||
| @@ -0,0 +1,595 @@ | |||
| /* | |||
| * Carla Bridge UI, VST version | |||
| * Copyright (C) 2011-2012 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 COPYING file | |||
| */ | |||
| #ifdef BRIDGE_VST | |||
| #include "carla_bridge_client.hpp" | |||
| #include "carla_bridge_toolkit.hpp" | |||
| #include "carla_vst_utils.hpp" | |||
| #include "carla_midi.h" | |||
| #include <QtCore/QObject> | |||
| #include <QtCore/QTimerEvent> | |||
| #if defined(Q_WS_X11) && (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) | |||
| # include <QtGui/QX11Info> | |||
| #endif | |||
| CARLA_BRIDGE_START_NAMESPACE | |||
| // ------------------------------------------------------------------------- | |||
| // fake values | |||
| uint32_t bufferSize = 512; | |||
| double sampleRate = 44100.0; | |||
| class CarlaVstClient : public CarlaBridgeClient, | |||
| public QObject | |||
| { | |||
| public: | |||
| CarlaVstClient(const char* const uiTitle) | |||
| : CarlaBridgeClient(uiTitle), | |||
| QObject(nullptr) | |||
| { | |||
| effect = nullptr; | |||
| idleTimer = 0; | |||
| needIdle = false; | |||
| // make client valid | |||
| srand(uiTitle[0]); | |||
| unique1 = unique2 = rand(); | |||
| } | |||
| ~CarlaVstClient() | |||
| { | |||
| // make client invalid | |||
| unique2 += 1; | |||
| } | |||
| // --------------------------------------------------------------------- | |||
| // ui initialization | |||
| bool init(const char* binary, const char*) | |||
| { | |||
| // ----------------------------------------------------------------- | |||
| // init | |||
| CarlaBridgeClient::init(binary, nullptr); | |||
| // ----------------------------------------------------------------- | |||
| // open DLL | |||
| if (! uiLibOpen(binary)) | |||
| { | |||
| qWarning("%s", uiLibError()); | |||
| return false; | |||
| } | |||
| // ----------------------------------------------------------------- | |||
| // get DLL main entry | |||
| VST_Function vstFn = (VST_Function)uiLibSymbol("VSTPluginMain"); | |||
| if (! vstFn) | |||
| vstFn = (VST_Function)uiLibSymbol("main"); | |||
| if (! vstFn) | |||
| return false; | |||
| // ----------------------------------------------------------------- | |||
| // initialize plugin | |||
| lastVstPlugin = this; | |||
| effect = vstFn(hostCallback); | |||
| lastVstPlugin = nullptr; | |||
| if (! (effect && effect->magic == kEffectMagic)) | |||
| return false; | |||
| // ----------------------------------------------------------------- | |||
| // initialize VST stuff | |||
| #ifdef VESTIGE_HEADER | |||
| effect->ptr1 = this; | |||
| #else | |||
| effect->resvd1 = ToVstPtr(this); | |||
| #endif | |||
| int32_t value = 0; | |||
| #if defined(Q_WS_X11) && (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) | |||
| value = (int64_t)QX11Info::display(); | |||
| #endif | |||
| effect->dispatcher(effect, effOpen, 0, 0, nullptr, 0.0f); | |||
| effect->dispatcher(effect, effMainsChanged, 0, 0, nullptr, 0.0f); | |||
| #if ! VST_FORCE_DEPRECATED | |||
| effect->dispatcher(effect, effSetBlockSizeAndSampleRate, 0, bufferSize, nullptr, sampleRate); | |||
| #endif | |||
| effect->dispatcher(effect, effSetSampleRate, 0, 0, nullptr, sampleRate); | |||
| effect->dispatcher(effect, effSetBlockSize, 0, bufferSize, nullptr, 0.0f); | |||
| effect->dispatcher(effect, effSetProcessPrecision, 0, kVstProcessPrecision32, nullptr, 0.0f); | |||
| if (effect->dispatcher(effect, effEditOpen, 0, value, getContainerId(), 0.0f) != 1) | |||
| { | |||
| //effect->dispatcher(effect, effClose, 0, 0, nullptr, 0.0f); | |||
| //return false; | |||
| qWarning("VST UI failed to open, trying to init anyway..."); | |||
| } | |||
| // ----------------------------------------------------------------- | |||
| // initialize gui stuff | |||
| ERect* vstRect = nullptr; | |||
| effect->dispatcher(effect, effEditGetRect, 0, 0, &vstRect, 0.0f); | |||
| if (vstRect) | |||
| { | |||
| int width = vstRect->right - vstRect->left; | |||
| int height = vstRect->bottom - vstRect->top; | |||
| if (width > 0 && height > 0) | |||
| toolkitResize(width, height); | |||
| } | |||
| idleTimer = startTimer(50); | |||
| return true; | |||
| } | |||
| void close() | |||
| { | |||
| CarlaBridgeClient::close(); | |||
| if (effect) | |||
| { | |||
| effect->dispatcher(effect, effEditClose, 0, 0, nullptr, 0.0f); | |||
| effect->dispatcher(effect, effClose, 0, 0, nullptr, 0.0f); | |||
| } | |||
| } | |||
| // --------------------------------------------------------------------- | |||
| // ui management | |||
| void* getWidget() const | |||
| { | |||
| return nullptr; // VST always uses reparent | |||
| } | |||
| bool isResizable() const | |||
| { | |||
| return false; | |||
| } | |||
| bool needsReparent() const | |||
| { | |||
| return true; | |||
| } | |||
| // --------------------------------------------------------------------- | |||
| // processing | |||
| void setParameter(const int32_t rindex, const double value) | |||
| { | |||
| if (effect) | |||
| effect->setParameter(effect, rindex, value); | |||
| } | |||
| void setProgram(const uint32_t index) | |||
| { | |||
| if (effect) | |||
| effect->dispatcher(effect, effSetProgram, 0, index, nullptr, 0.0f); | |||
| } | |||
| void setMidiProgram(const uint32_t, const uint32_t) | |||
| { | |||
| } | |||
| void noteOn(const uint8_t, const uint8_t, const uint8_t) | |||
| { | |||
| } | |||
| void noteOff(const uint8_t, const uint8_t) | |||
| { | |||
| } | |||
| // --------------------------------------------------------------------- | |||
| void handleAudioMasterAutomate(const uint32_t index, const float value) | |||
| { | |||
| effect->setParameter(effect, index, value); | |||
| if (isOscControlRegistered()) | |||
| sendOscControl(index, value); | |||
| } | |||
| intptr_t handleAudioMasterGetCurrentProcessLevel() | |||
| { | |||
| return kVstProcessLevelUser; | |||
| } | |||
| intptr_t handleAudioMasterGetBlockSize() | |||
| { | |||
| return bufferSize; | |||
| } | |||
| intptr_t handleAudioMasterGetSampleRate() | |||
| { | |||
| return sampleRate; | |||
| } | |||
| intptr_t handleAudioMasterGetTime() | |||
| { | |||
| memset(&vstTimeInfo, 0, sizeof(VstTimeInfo_R)); | |||
| vstTimeInfo.sampleRate = sampleRate; | |||
| // Tempo | |||
| vstTimeInfo.tempo = 120.0; | |||
| vstTimeInfo.flags |= kVstTempoValid; | |||
| // Time Signature | |||
| vstTimeInfo.timeSigNumerator = 4; | |||
| vstTimeInfo.timeSigDenominator = 4; | |||
| vstTimeInfo.flags |= kVstTimeSigValid; | |||
| return (intptr_t)&vstTimeInfo; | |||
| } | |||
| void handleAudioMasterNeedIdle() | |||
| { | |||
| needIdle = true; | |||
| } | |||
| intptr_t handleAudioMasterProcessEvents(const VstEvents* const vstEvents) | |||
| { | |||
| if (isOscControlRegistered()) | |||
| return 1; | |||
| for (int32_t i=0; i < vstEvents->numEvents; i++) | |||
| { | |||
| if (! vstEvents->events[i]) | |||
| break; | |||
| const VstMidiEvent* const vstMidiEvent = (const VstMidiEvent*)vstEvents->events[i]; | |||
| if (vstMidiEvent->type != kVstMidiType) | |||
| { | |||
| uint8_t status = vstMidiEvent->midiData[0]; | |||
| // Fix bad note-off | |||
| if (MIDI_IS_STATUS_NOTE_ON(status) && vstMidiEvent->midiData[2] == 0) | |||
| status -= 0x10; | |||
| uint8_t midiBuf[4] = { 0, status, (uint8_t)vstMidiEvent->midiData[1], (uint8_t)vstMidiEvent->midiData[2] }; | |||
| sendOscMidi(midiBuf); | |||
| } | |||
| } | |||
| return 1; | |||
| } | |||
| intptr_t handleAdioMasterSizeWindow(int32_t width, int32_t height) | |||
| { | |||
| toolkitResize(width, height); | |||
| return 1; | |||
| } | |||
| void handleAudioMasterUpdateDisplay() | |||
| { | |||
| if (isOscControlRegistered()) | |||
| sendOscConfigure("reloadprograms", ""); | |||
| } | |||
| // --------------------------------------------------------------------- | |||
| static intptr_t hostCanDo(const char* const feature) | |||
| { | |||
| qDebug("CarlaVstClient::hostCanDo(\"%s\")", feature); | |||
| if (strcmp(feature, "supplyIdle") == 0) | |||
| return 1; | |||
| if (strcmp(feature, "sendVstEvents") == 0) | |||
| return 1; | |||
| if (strcmp(feature, "sendVstMidiEvent") == 0) | |||
| return 1; | |||
| if (strcmp(feature, "sendVstMidiEventFlagIsRealtime") == 0) | |||
| return -1; | |||
| if (strcmp(feature, "sendVstTimeInfo") == 0) | |||
| return 1; | |||
| if (strcmp(feature, "receiveVstEvents") == 0) | |||
| return 1; | |||
| if (strcmp(feature, "receiveVstMidiEvent") == 0) | |||
| return 1; | |||
| if (strcmp(feature, "receiveVstTimeInfo") == 0) | |||
| return -1; | |||
| if (strcmp(feature, "reportConnectionChanges") == 0) | |||
| return -1; | |||
| if (strcmp(feature, "acceptIOChanges") == 0) | |||
| return 1; | |||
| if (strcmp(feature, "sizeWindow") == 0) | |||
| return 1; | |||
| if (strcmp(feature, "offline") == 0) | |||
| return -1; | |||
| if (strcmp(feature, "openFileSelector") == 0) | |||
| return -1; | |||
| if (strcmp(feature, "closeFileSelector") == 0) | |||
| return -1; | |||
| if (strcmp(feature, "startStopProcess") == 0) | |||
| return 1; | |||
| if (strcmp(feature, "supportShell") == 0) | |||
| return -1; | |||
| if (strcmp(feature, "shellCategory") == 0) | |||
| return -1; | |||
| // unimplemented | |||
| qWarning("CarlaVstClient::hostCanDo(\"%s\") - unknown feature", feature); | |||
| return 0; | |||
| } | |||
| static intptr_t VSTCALLBACK hostCallback(AEffect* const effect, const int32_t opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt) | |||
| { | |||
| #if DEBUG | |||
| qDebug("CarlaVstClient::hostCallback(%p, %s, %i, " P_INTPTR ", %p, %f", effect, vstMasterOpcode2str(opcode), index, value, ptr, opt); | |||
| #endif | |||
| // Check if 'resvd1' points to this client, or register ourselfs if possible | |||
| CarlaVstClient* self = nullptr; | |||
| if (effect) | |||
| { | |||
| #ifdef VESTIGE_HEADER | |||
| if (effect && effect->ptr1) | |||
| { | |||
| self = (CarlaVstClient*)effect->ptr1; | |||
| #else | |||
| if (effect && effect->resvd1) | |||
| { | |||
| self = FromVstPtr<CarlaVstClient>(effect->resvd1); | |||
| #endif | |||
| if (self->unique1 != self->unique2) | |||
| self = nullptr; | |||
| } | |||
| if (self) | |||
| { | |||
| if (! self->effect) | |||
| self->effect = effect; | |||
| CARLA_ASSERT(self->effect == effect); | |||
| if (self->effect != effect) | |||
| { | |||
| qWarning("CarlaVstClient::hostCallback() - host pointer mismatch: %p != %p", self->effect, effect); | |||
| self = nullptr; | |||
| } | |||
| } | |||
| else if (lastVstPlugin) | |||
| { | |||
| #ifdef VESTIGE_HEADER | |||
| effect->ptr1 = lastVstPlugin; | |||
| #else | |||
| effect->resvd1 = ToVstPtr(lastVstPlugin); | |||
| #endif | |||
| self = lastVstPlugin; | |||
| } | |||
| } | |||
| intptr_t ret = 0; | |||
| switch (opcode) | |||
| { | |||
| case audioMasterAutomate: | |||
| if (self) | |||
| self->handleAudioMasterAutomate(index, opt); | |||
| break; | |||
| case audioMasterVersion: | |||
| ret = kVstVersion; | |||
| break; | |||
| case audioMasterCurrentId: | |||
| // TODO | |||
| break; | |||
| case audioMasterIdle: | |||
| //if (effect) | |||
| // effect->dispatcher(effect, effEditIdle, 0, 0, nullptr, 0.0f); | |||
| break; | |||
| case audioMasterGetTime: | |||
| static VstTimeInfo_R timeInfo; | |||
| memset(&timeInfo, 0, sizeof(VstTimeInfo_R)); | |||
| timeInfo.sampleRate = sampleRate; | |||
| // Tempo | |||
| timeInfo.tempo = 120.0; | |||
| timeInfo.flags |= kVstTempoValid; | |||
| // Time Signature | |||
| timeInfo.timeSigNumerator = 4; | |||
| timeInfo.timeSigDenominator = 4; | |||
| timeInfo.flags |= kVstTimeSigValid; | |||
| ret = (intptr_t)&timeInfo; | |||
| break; | |||
| case audioMasterProcessEvents: | |||
| if (self && ptr) | |||
| ret = self->handleAudioMasterProcessEvents((const VstEvents*)ptr); | |||
| break; | |||
| #if ! VST_FORCE_DEPRECATED | |||
| case audioMasterTempoAt: | |||
| // Deprecated in VST SDK 2.4 | |||
| ret = 120 * 10000; | |||
| break; | |||
| #endif | |||
| case audioMasterSizeWindow: | |||
| if (self && index > 0 && value > 0) | |||
| ret = self->handleAdioMasterSizeWindow(index, value); | |||
| break; | |||
| case audioMasterGetSampleRate: | |||
| ret = sampleRate; | |||
| break; | |||
| case audioMasterGetBlockSize: | |||
| ret = bufferSize; | |||
| break; | |||
| case audioMasterGetCurrentProcessLevel: | |||
| ret = kVstProcessLevelUser; | |||
| break; | |||
| case audioMasterGetAutomationState: | |||
| ret = kVstAutomationReadWrite; | |||
| break; | |||
| case audioMasterGetVendorString: | |||
| if (ptr) | |||
| strcpy((char*)ptr, "Cadence"); | |||
| break; | |||
| case audioMasterGetProductString: | |||
| if (ptr) | |||
| strcpy((char*)ptr, "Carla-Bridge"); | |||
| break; | |||
| case audioMasterGetVendorVersion: | |||
| ret = 0x050; // 0.5.0 | |||
| break; | |||
| case audioMasterCanDo: | |||
| if (ptr) | |||
| ret = hostCanDo((const char*)ptr); | |||
| break; | |||
| case audioMasterGetLanguage: | |||
| ret = kVstLangEnglish; | |||
| break; | |||
| case audioMasterUpdateDisplay: | |||
| if (self) | |||
| self->handleAudioMasterUpdateDisplay(); | |||
| break; | |||
| default: | |||
| #ifdef DEBUG | |||
| qDebug("CarlaVstClient::hostCallback(%p, %s, %i, " P_INTPTR ", %p, %f", effect, vstMasterOpcode2str(opcode), index, value, ptr, opt); | |||
| #endif | |||
| break; | |||
| } | |||
| return ret; | |||
| } | |||
| protected: | |||
| void timerEvent(QTimerEvent* const event) | |||
| { | |||
| if (event->timerId() == idleTimer && effect) | |||
| { | |||
| if (needIdle) | |||
| effect->dispatcher(effect, effIdle, 0, 0, nullptr, 0.0f); | |||
| effect->dispatcher(effect, effEditIdle, 0, 0, nullptr, 0.0f); | |||
| } | |||
| QObject::timerEvent(event); | |||
| } | |||
| private: | |||
| int unique1; | |||
| AEffect* effect; | |||
| VstTimeInfo_R vstTimeInfo; | |||
| int idleTimer; | |||
| bool needIdle; | |||
| static CarlaVstClient* lastVstPlugin; | |||
| int unique2; | |||
| }; | |||
| CarlaVstClient* CarlaVstClient::lastVstPlugin = nullptr; | |||
| // ------------------------------------------------------------------------- | |||
| CARLA_BRIDGE_END_NAMESPACE | |||
| int main(int argc, char* argv[]) | |||
| { | |||
| CARLA_BRIDGE_USE_NAMESPACE | |||
| if (argc != 4) | |||
| { | |||
| qWarning("usage: %s <osc-url|\"null\"> <binary> <ui-title>", argv[0]); | |||
| return 1; | |||
| } | |||
| const char* oscUrl = argv[1]; | |||
| const char* binary = argv[2]; | |||
| const char* uiTitle = argv[3]; | |||
| const bool useOsc = strcmp(oscUrl, "null"); | |||
| // try to get sampleRate value | |||
| const char* const sampleRateStr = getenv("CARLA_SAMPLE_RATE"); | |||
| if (sampleRateStr) | |||
| sampleRate = atof(sampleRateStr); | |||
| // Init VST client | |||
| CarlaVstClient client(uiTitle); | |||
| // Init OSC | |||
| if (useOsc && ! client.oscInit(oscUrl)) | |||
| { | |||
| return -1; | |||
| } | |||
| // Load UI | |||
| int ret; | |||
| if (client.init(binary, nullptr)) | |||
| { | |||
| client.toolkitExec(!useOsc); | |||
| ret = 0; | |||
| } | |||
| else | |||
| { | |||
| qCritical("Failed to load VST UI"); | |||
| ret = 1; | |||
| } | |||
| // Close OSC | |||
| if (useOsc) | |||
| { | |||
| client.oscClose(); | |||
| } | |||
| // Close VST client | |||
| client.close(); | |||
| return ret; | |||
| } | |||
| #endif // BRIDGE_VST | |||
| @@ -0,0 +1,43 @@ | |||
| # QtCreator project file | |||
| QT = core | |||
| CONFIG = debug link_pkgconfig qt warn_on | |||
| PKGCONFIG = liblo gtk+-2.0 | |||
| TARGET = carla-bridge-lv2-gtk2 | |||
| TEMPLATE = app | |||
| VERSION = 0.5.0 | |||
| SOURCES = \ | |||
| ../carla_bridge_client.cpp \ | |||
| ../carla_bridge_osc.cpp \ | |||
| ../carla_bridge_toolkit.cpp \ | |||
| ../carla_bridge_toolkit-gtk.cpp \ | |||
| ../carla_bridge_ui-lv2.cpp | |||
| HEADERS = \ | |||
| ../carla_bridge.hpp \ | |||
| ../carla_bridge_client.hpp \ | |||
| ../carla_bridge_osc.hpp \ | |||
| ../carla_bridge_toolkit.hpp \ | |||
| ../../carla-includes/carla_defines.hpp \ | |||
| ../../carla-includes/carla_midi.h \ | |||
| ../../carla-includes/lv2_rdf.hpp \ | |||
| ../../carla-utils/carla_lib_utils.hpp \ | |||
| ../../carla-utils/carla_osc_utils.hpp \ | |||
| ../../carla-utils/carla_lv2_utils.hpp | |||
| INCLUDEPATH = .. \ | |||
| ../../carla-includes \ | |||
| ../../carla-utils | |||
| LIBS = \ | |||
| ../../carla-lilv/carla_lilv.a \ | |||
| ../../carla-rtmempool/carla_rtmempool.a | |||
| DEFINES = QTCREATOR_TEST | |||
| DEFINES += DEBUG | |||
| DEFINES += BUILD_BRIDGE BUILD_BRIDGE_UI BRIDGE_LV2 BRIDGE_GTK2 BRIDGE_LV2_GTK2 | |||
| QMAKE_CXXFLAGS *= -std=c++0x | |||
| @@ -0,0 +1,43 @@ | |||
| # QtCreator project file | |||
| QT = core | |||
| CONFIG = debug link_pkgconfig qt warn_on | |||
| PKGCONFIG = liblo gtk+-3.0 | |||
| TARGET = carla-bridge-lv2-gtk3 | |||
| TEMPLATE = app | |||
| VERSION = 0.5.0 | |||
| SOURCES = \ | |||
| ../carla_bridge_client.cpp \ | |||
| ../carla_bridge_osc.cpp \ | |||
| ../carla_bridge_toolkit.cpp \ | |||
| ../carla_bridge_toolkit-gtk.cpp \ | |||
| ../carla_bridge_ui-lv2.cpp | |||
| HEADERS = \ | |||
| ../carla_bridge.hpp \ | |||
| ../carla_bridge_client.hpp \ | |||
| ../carla_bridge_osc.hpp \ | |||
| ../carla_bridge_toolkit.hpp \ | |||
| ../../carla-includes/carla_defines.hpp \ | |||
| ../../carla-includes/carla_midi.h \ | |||
| ../../carla-includes/lv2_rdf.hpp \ | |||
| ../../carla-utils/carla_lib_utils.hpp \ | |||
| ../../carla-utils/carla_osc_utils.hpp \ | |||
| ../../carla-utils/carla_lv2_utils.hpp | |||
| INCLUDEPATH = .. \ | |||
| ../../carla-includes \ | |||
| ../../carla-utils | |||
| LIBS = \ | |||
| ../../carla-lilv/carla_lilv.a \ | |||
| ../../carla-rtmempool/carla_rtmempool.a | |||
| DEFINES = QTCREATOR_TEST | |||
| DEFINES += DEBUG | |||
| DEFINES += BUILD_BRIDGE BUILD_BRIDGE_UI BRIDGE_LV2 BRIDGE_GTK3 BRIDGE_LV2_GTK3 | |||
| QMAKE_CXXFLAGS *= -std=c++0x | |||
| @@ -0,0 +1,43 @@ | |||
| # QtCreator project file | |||
| QT = core gui | |||
| CONFIG = debug link_pkgconfig qt warn_on | |||
| PKGCONFIG = liblo | |||
| TARGET = carla-bridge-lv2-qt4 | |||
| TEMPLATE = app | |||
| VERSION = 0.5.0 | |||
| SOURCES = \ | |||
| ../carla_bridge_client.cpp \ | |||
| ../carla_bridge_osc.cpp \ | |||
| ../carla_bridge_toolkit.cpp \ | |||
| ../carla_bridge_toolkit-qt.cpp \ | |||
| ../carla_bridge_ui-lv2.cpp | |||
| HEADERS = \ | |||
| ../carla_bridge.hpp \ | |||
| ../carla_bridge_client.hpp \ | |||
| ../carla_bridge_osc.hpp \ | |||
| ../carla_bridge_toolkit.hpp \ | |||
| ../../carla-includes/carla_defines.hpp \ | |||
| ../../carla-includes/carla_midi.h \ | |||
| ../../carla-includes/lv2_rdf.hpp \ | |||
| ../../carla-utils/carla_lib_utils.hpp \ | |||
| ../../carla-utils/carla_osc_utils.hpp \ | |||
| ../../carla-utils/carla_lv2_utils.hpp | |||
| INCLUDEPATH = .. \ | |||
| ../../carla-includes \ | |||
| ../../carla-utils | |||
| LIBS = \ | |||
| ../../carla-lilv/carla_lilv.a \ | |||
| ../../carla-rtmempool/carla_rtmempool.a | |||
| DEFINES = QTCREATOR_TEST | |||
| DEFINES += DEBUG | |||
| DEFINES += BUILD_BRIDGE BUILD_BRIDGE_UI BRIDGE_LV2 BRIDGE_QT4 BRIDGE_LV2_QT4 | |||
| QMAKE_CXXFLAGS *= -std=c++0x | |||
| @@ -0,0 +1,43 @@ | |||
| # QtCreator project file | |||
| QT = core widgets | |||
| CONFIG = debug link_pkgconfig qt warn_on | |||
| PKGCONFIG = liblo | |||
| TARGET = carla-bridge-lv2-qt5 | |||
| TEMPLATE = app | |||
| VERSION = 0.5.0 | |||
| SOURCES = \ | |||
| ../carla_bridge_client.cpp \ | |||
| ../carla_bridge_osc.cpp \ | |||
| ../carla_bridge_toolkit.cpp \ | |||
| ../carla_bridge_toolkit-qt.cpp \ | |||
| ../carla_bridge_ui-lv2.cpp | |||
| HEADERS = \ | |||
| ../carla_bridge.hpp \ | |||
| ../carla_bridge_client.hpp \ | |||
| ../carla_bridge_osc.hpp \ | |||
| ../carla_bridge_toolkit.hpp \ | |||
| ../../carla-includes/carla_defines.hpp \ | |||
| ../../carla-includes/carla_midi.h \ | |||
| ../../carla-includes/lv2_rdf.hpp \ | |||
| ../../carla-utils/carla_lib_utils.hpp \ | |||
| ../../carla-utils/carla_osc_utils.hpp \ | |||
| ../../carla-utils/carla_lv2_utils.hpp | |||
| INCLUDEPATH = .. \ | |||
| ../../carla-includes \ | |||
| ../../carla-utils | |||
| LIBS = \ | |||
| ../../carla-lilv/carla_lilv.a \ | |||
| ../../carla-rtmempool/carla_rtmempool.a | |||
| DEFINES = QTCREATOR_TEST | |||
| DEFINES += DEBUG | |||
| DEFINES += BUILD_BRIDGE BUILD_BRIDGE_UI BRIDGE_LV2 BRIDGE_QT5 BRIDGE_LV2_QT5 | |||
| QMAKE_CXXFLAGS *= -std=c++0x | |||
| @@ -0,0 +1,47 @@ | |||
| # QtCreator project file | |||
| contains(QT_VERSION, ^5.*) { | |||
| QT = core widgets | |||
| } else { | |||
| QT = core gui | |||
| } | |||
| CONFIG = debug link_pkgconfig qt warn_on | |||
| PKGCONFIG = liblo | |||
| TARGET = carla-bridge-lv2-x11 | |||
| TEMPLATE = app | |||
| VERSION = 0.5.0 | |||
| SOURCES = \ | |||
| ../carla_bridge_client.cpp \ | |||
| ../carla_bridge_osc.cpp \ | |||
| ../carla_bridge_toolkit.cpp \ | |||
| ../carla_bridge_toolkit-qt.cpp \ | |||
| ../carla_bridge_ui-lv2.cpp | |||
| HEADERS = \ | |||
| ../carla_bridge.hpp \ | |||
| ../carla_bridge_client.hpp \ | |||
| ../carla_bridge_osc.hpp \ | |||
| ../carla_bridge_toolkit.hpp \ | |||
| ../../carla-includes/carla_defines.hpp \ | |||
| ../../carla-includes/carla_midi.h \ | |||
| ../../carla-includes/lv2_rdf.hpp \ | |||
| ../../carla-utils/carla_lib_utils.hpp \ | |||
| ../../carla-utils/carla_osc_utils.hpp \ | |||
| ../../carla-utils/carla_lv2_utils.hpp | |||
| INCLUDEPATH = .. \ | |||
| ../../carla-includes \ | |||
| ../../carla-utils | |||
| LIBS = \ | |||
| ../../carla-lilv/carla_lilv.a \ | |||
| ../../carla-rtmempool/carla_rtmempool.a | |||
| DEFINES = QTCREATOR_TEST | |||
| DEFINES += DEBUG | |||
| DEFINES += BUILD_BRIDGE BUILD_BRIDGE_UI BRIDGE_LV2 BRIDGE_X11 BRIDGE_LV2_X11 | |||
| QMAKE_CXXFLAGS *= -std=c++0x | |||
| @@ -0,0 +1,100 @@ | |||
| # QtCreator project file | |||
| contains(QT_VERSION, ^5.*) { | |||
| QT = core widgets xml | |||
| } else { | |||
| QT = core gui xml | |||
| } | |||
| CONFIG = debug link_pkgconfig qt warn_on | |||
| PKGCONFIG = jack liblo | |||
| TARGET = carla-bridge-qtcreator | |||
| TEMPLATE = app | |||
| VERSION = 0.5.0 | |||
| SOURCES = \ | |||
| ../carla_bridge_client.cpp \ | |||
| ../carla_bridge_osc.cpp \ | |||
| ../carla_bridge_toolkit.cpp \ | |||
| ../carla_bridge_plugin.cpp | |||
| HEADERS = \ | |||
| ../carla_bridge.hpp \ | |||
| ../carla_bridge_client.hpp \ | |||
| ../carla_bridge_osc.hpp \ | |||
| ../carla_bridge_toolkit.hpp \ | |||
| # carla | |||
| SOURCES += \ | |||
| ../../carla/Shared.cpp | |||
| HEADERS += \ | |||
| ../../carla/Shared.hpp | |||
| # carla-engine | |||
| SOURCES += \ | |||
| ../../carla-engine/carla_engine.cpp \ | |||
| ../../carla-engine/carla_engine_osc.cpp \ | |||
| ../../carla-engine/carla_engine_thread.cpp \ | |||
| ../../carla-engine/jack.cpp | |||
| HEADERS += \ | |||
| ../../carla-engine/carla_engine.hpp \ | |||
| ../../carla-engine/carla_engine_osc.hpp \ | |||
| ../../carla-engine/carla_engine_thread.hpp \ | |||
| # carla-plugin | |||
| SOURCES += \ | |||
| ../../carla-plugin/carla_plugin.cpp \ | |||
| ../../carla-plugin/carla_plugin_thread.cpp \ | |||
| ../../carla-plugin/ladspa.cpp \ | |||
| ../../carla-plugin/dssi.cpp \ | |||
| ../../carla-plugin/lv2.cpp \ | |||
| ../../carla-plugin/vst.cpp | |||
| HEADERS += \ | |||
| ../../carla-plugin/carla_plugin.hpp \ | |||
| ../../carla-plugin/carla_plugin_thread.hpp | |||
| # carla-backend | |||
| HEADERS += \ | |||
| ../../carla-backend/carla_backend.hpp \ | |||
| ../../carla-backend/carla_backend_utils.hpp | |||
| # carla-includes | |||
| HEADERS += \ | |||
| ../../carla-includes/carla_defines.hpp \ | |||
| ../../carla-includes/carla_midi.h \ | |||
| ../../carla-includes/ladspa_rdf.hpp \ | |||
| ../../carla-includes/lv2_rdf.hpp | |||
| # carla-utils | |||
| HEADERS += \ | |||
| ../../carla-utils/carla_lib_utils.hpp \ | |||
| ../../carla-utils/carla_osc_utils.hpp \ | |||
| ../../carla-utils/carla_ladspa_utils.hpp \ | |||
| ../../carla-utils/carla_lv2_utils.hpp \ | |||
| ../../carla-utils/carla_vst_utils.hpp | |||
| INCLUDEPATH = .. \ | |||
| ../../carla-backend \ | |||
| ../../carla-engine \ | |||
| ../../carla-includes \ | |||
| ../../carla-jackbridge \ | |||
| ../../carla-plugin \ | |||
| ../../carla-utils | |||
| DEFINES = QTCREATOR_TEST | |||
| DEFINES += DEBUG | |||
| #DEFINES += VESTIGE_HEADER | |||
| DEFINES += BUILD_BRIDGE BUILD_BRIDGE_PLUGIN BRIDGE_PLUGIN | |||
| DEFINES += CARLA_ENGINE_JACK | |||
| DEFINES += WANT_LADSPA WANT_DSSI WANT_LV2 WANT_VST | |||
| LIBS = -ldl \ | |||
| ../../carla-lilv/carla_lilv.a \ | |||
| ../../carla-rtmempool/carla_rtmempool.a | |||
| QMAKE_CXXFLAGS *= -std=c++0x | |||
| @@ -0,0 +1,42 @@ | |||
| # QtCreator project file | |||
| contains(QT_VERSION, ^5.*) { | |||
| QT = core widgets | |||
| } else { | |||
| QT = core gui | |||
| } | |||
| CONFIG = debug link_pkgconfig qt warn_on | |||
| PKGCONFIG = liblo | |||
| TARGET = carla-bridge-vst-x11 | |||
| TEMPLATE = app | |||
| VERSION = 0.5.0 | |||
| SOURCES = \ | |||
| ../carla_bridge_client.cpp \ | |||
| ../carla_bridge_osc.cpp \ | |||
| ../carla_bridge_toolkit.cpp \ | |||
| ../carla_bridge_toolkit-qt.cpp \ | |||
| ../carla_bridge_ui-vst.cpp | |||
| HEADERS = \ | |||
| ../carla_bridge.hpp \ | |||
| ../carla_bridge_client.hpp \ | |||
| ../carla_bridge_osc.hpp \ | |||
| ../carla_bridge_toolkit.hpp \ | |||
| ../../carla-includes/carla_defines.hpp \ | |||
| ../../carla-includes/carla_midi.h \ | |||
| ../../carla-utils/carla_lib_utils.hpp \ | |||
| ../../carla-utils/carla_osc_utils.hpp \ | |||
| ../../carla-utils/carla_vst_utils.hpp | |||
| INCLUDEPATH = .. \ | |||
| ../../carla-includes \ | |||
| ../../carla-utils | |||
| DEFINES = QTCREATOR_TEST | |||
| DEFINES += DEBUG | |||
| DEFINES += BUILD_BRIDGE BUILD_BRIDGE_UI BRIDGE_VST BRIDGE_X11 BRIDGE_VST_X11 | |||
| QMAKE_CXXFLAGS *= -std=c++0x | |||
| @@ -0,0 +1,897 @@ | |||
| #!/usr/bin/env python3 | |||
| # -*- coding: utf-8 -*- | |||
| # Carla Backend code | |||
| # Copyright (C) 2011-2012 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 COPYING file | |||
| # Imports (Global) | |||
| from PyQt4.QtGui import QApplication, QMainWindow | |||
| from liblo import make_method, Address, ServerError, ServerThread | |||
| from liblo import send as lo_send | |||
| from liblo import TCP as LO_TCP | |||
| # Imports (Custom) | |||
| import ui_carla_control | |||
| from shared_carla import * | |||
| global lo_target, lo_targetName | |||
| lo_target = None | |||
| lo_targetName = "" | |||
| Carla.isControl = True | |||
| # Python Object dicts compatible to carla-backend struct ctypes | |||
| MidiProgramData = { | |||
| 'bank': 0, | |||
| 'program': 0, | |||
| 'label': None | |||
| } | |||
| ParameterData = { | |||
| 'type': PARAMETER_NULL, | |||
| 'index': 0, | |||
| 'rindex': -1, | |||
| 'hints': 0x0, | |||
| 'midiChannel': 0, | |||
| 'midiCC': -1 | |||
| } | |||
| ParameterRanges = { | |||
| 'def': 0.0, | |||
| 'min': 0.0, | |||
| 'max': 1.0, | |||
| 'step': 0.0, | |||
| 'stepSmall': 0.0, | |||
| 'stepLarge': 0.0 | |||
| } | |||
| CustomData = { | |||
| 'type': CUSTOM_DATA_INVALID, | |||
| 'key': None, | |||
| 'value': None | |||
| } | |||
| PluginInfo = { | |||
| 'type': PLUGIN_NONE, | |||
| 'category': PLUGIN_CATEGORY_NONE, | |||
| 'hints': 0x0, | |||
| 'binary': None, | |||
| 'name': None, | |||
| 'label': None, | |||
| 'maker': None, | |||
| 'copyright': None, | |||
| 'uniqueId': 0 | |||
| } | |||
| PortCountInfo = { | |||
| 'ins': 0, | |||
| 'outs': 0, | |||
| 'total': 0 | |||
| } | |||
| ParameterInfo = { | |||
| 'name': None, | |||
| 'symbol': None, | |||
| 'unit': None, | |||
| 'scalePointCount': 0, | |||
| } | |||
| ScalePointInfo = { | |||
| 'value': 0.0, | |||
| 'label': None | |||
| } | |||
| GuiInfo = { | |||
| 'type': GUI_NONE, | |||
| 'resizable': False | |||
| } | |||
| class ControlPluginInfo(object): | |||
| __slots__ = [ | |||
| 'pluginInfo', | |||
| 'pluginRealName', | |||
| 'audioCountInfo', | |||
| 'midiCountInfo', | |||
| 'parameterCountInfo', | |||
| 'parameterInfoS', | |||
| 'parameterDataS', | |||
| 'parameterRangeS', | |||
| 'parameterValueS', | |||
| 'programCount', | |||
| 'programCurrent', | |||
| 'programNameS', | |||
| 'midiProgramCount', | |||
| 'midiProgramCurrent', | |||
| 'midiProgramDataS', | |||
| 'inPeak', | |||
| 'outPeak' | |||
| ] | |||
| # Python Object class compatible to 'Host' on the Carla Backend code | |||
| class Host(object): | |||
| def __init__(self): | |||
| object.__init__(self) | |||
| self.pluginInfo = [] | |||
| for i in range(MAX_PLUGINS): | |||
| self.pluginInfo.append(ControlPluginInfo()) | |||
| self._clear(i) | |||
| def _clear(self, index): | |||
| self.pluginInfo[index].pluginInfo = PluginInfo | |||
| self.pluginInfo[index].pluginRealName = None | |||
| self.pluginInfo[index].audioCountInfo = PortCountInfo | |||
| self.pluginInfo[index].midiCountInfo = PortCountInfo | |||
| self.pluginInfo[index].parameterCountInfo = PortCountInfo | |||
| self.pluginInfo[index].parameterInfoS = [] | |||
| self.pluginInfo[index].parameterDataS = [] | |||
| self.pluginInfo[index].parameterRangeS = [] | |||
| self.pluginInfo[index].parameterValueS = [] | |||
| self.pluginInfo[index].programCount = 0 | |||
| self.pluginInfo[index].programCurrent = -1 | |||
| self.pluginInfo[index].programNameS = [] | |||
| self.pluginInfo[index].midiProgramCount = 0 | |||
| self.pluginInfo[index].midiProgramCurrent = -1 | |||
| self.pluginInfo[index].midiProgramDataS = [] | |||
| self.pluginInfo[index].inPeak = [0.0, 0.0] | |||
| self.pluginInfo[index].outPeak = [0.0, 0.0] | |||
| def _set_pluginInfo(self, index, info): | |||
| self.pluginInfo[index].pluginInfo = info | |||
| def _set_pluginRealName(self, index, realName): | |||
| self.pluginInfo[index].pluginRealName = realName | |||
| def _set_audioCountInfo(self, index, info): | |||
| self.pluginInfo[index].audioCountInfo = info | |||
| def _set_midiCountInfo(self, index, info): | |||
| self.pluginInfo[index].midiCountInfo = info | |||
| def _set_parameterCountInfo(self, index, info): | |||
| self.pluginInfo[index].parameterCountInfo = info | |||
| # clear | |||
| self.pluginInfo[index].parameterInfoS = [] | |||
| self.pluginInfo[index].parameterDataS = [] | |||
| self.pluginInfo[index].parameterRangeS = [] | |||
| self.pluginInfo[index].parameterValueS = [] | |||
| # add placeholders | |||
| for x in range(info['total']): | |||
| self.pluginInfo[index].parameterInfoS.append(ParameterInfo) | |||
| self.pluginInfo[index].parameterDataS.append(ParameterData) | |||
| self.pluginInfo[index].parameterRangeS.append(ParameterRanges) | |||
| self.pluginInfo[index].parameterValueS.append(0.0) | |||
| def _set_programCount(self, index, count): | |||
| self.pluginInfo[index].programCount = count | |||
| # clear | |||
| self.pluginInfo[index].programNameS = [] | |||
| # add placeholders | |||
| for x in range(count): | |||
| self.pluginInfo[index].programNameS.append(None) | |||
| def _set_midiProgramCount(self, index, count): | |||
| self.pluginInfo[index].midiProgramCount = count | |||
| # clear | |||
| self.pluginInfo[index].midiProgramDataS = [] | |||
| # add placeholders | |||
| for x in range(count): | |||
| self.pluginInfo[index].midiProgramDataS.append(MidiProgramData) | |||
| def _set_parameterInfoS(self, index, paramIndex, data): | |||
| if paramIndex < self.pluginInfo[index].parameterCountInfo['total']: | |||
| self.pluginInfo[index].parameterInfoS[paramIndex] = data | |||
| def _set_parameterDataS(self, index, paramIndex, data): | |||
| if paramIndex < self.pluginInfo[index].parameterCountInfo['total']: | |||
| self.pluginInfo[index].parameterDataS[paramIndex] = data | |||
| def _set_parameterRangeS(self, index, paramIndex, data): | |||
| if paramIndex < self.pluginInfo[index].parameterCountInfo['total']: | |||
| self.pluginInfo[index].parameterRangeS[paramIndex] = data | |||
| def _set_parameterValueS(self, index, paramIndex, value): | |||
| if paramIndex < self.pluginInfo[index].parameterCountInfo['total']: | |||
| self.pluginInfo[index].parameterValueS[paramIndex] = value | |||
| def _set_parameterDefaultValue(self, index, paramIndex, value): | |||
| if paramIndex < self.pluginInfo[index].parameterCountInfo['total']: | |||
| self.pluginInfo[index].parameterRangeS[paramIndex]['def'] = value | |||
| def _set_parameterMidiCC(self, index, paramIndex, cc): | |||
| if paramIndex < self.pluginInfo[index].parameterCountInfo['total']: | |||
| self.pluginInfo[index].parameterDataS[paramIndex]['midiCC'] = cc | |||
| def _set_parameterMidiChannel(self, index, paramIndex, channel): | |||
| if paramIndex < self.pluginInfo[index].parameterCountInfo['total']: | |||
| self.pluginInfo[index].parameterDataS[paramIndex]['midiChannel'] = channel | |||
| def _set_currentProgram(self, index, pIndex): | |||
| self.pluginInfo[index].programCurrent = pIndex | |||
| def _set_currentMidiProgram(self, index, mpIndex): | |||
| self.pluginInfo[index].midiProgramCurrent = mpIndex | |||
| def _set_programNameS(self, index, pIndex, data): | |||
| if pIndex < self.pluginInfo[index].programCount: | |||
| self.pluginInfo[index].programNameS[pIndex] = data | |||
| def _set_midiProgramDataS(self, index, mpIndex, data): | |||
| if mpIndex < self.pluginInfo[index].midiProgramCount: | |||
| self.pluginInfo[index].midiProgramDataS[mpIndex] = data | |||
| def _set_inPeak(self, index, port, value): | |||
| self.pluginInfo[index].inPeak[port] = value | |||
| def _set_outPeak(self, index, port, value): | |||
| self.pluginInfo[index].outPeak[port] = value | |||
| def get_plugin_info(self, plugin_id): | |||
| return self.pluginInfo[plugin_id].pluginInfo | |||
| def get_audio_port_count_info(self, plugin_id): | |||
| return self.pluginInfo[plugin_id].audioCountInfo | |||
| def get_midi_port_count_info(self, plugin_id): | |||
| return self.pluginInfo[plugin_id].midiCountInfo | |||
| def get_parameter_count_info(self, plugin_id): | |||
| return self.pluginInfo[plugin_id].parameterCountInfo | |||
| def get_parameter_info(self, plugin_id, parameter_id): | |||
| return self.pluginInfo[plugin_id].parameterInfoS[parameter_id] | |||
| def get_parameter_scalepoint_info(self, plugin_id, parameter_id, scalepoint_id): | |||
| return ScalePointInfo | |||
| def get_gui_info(self, plugin_id): | |||
| return GuiInfo | |||
| def get_parameter_data(self, plugin_id, parameter_id): | |||
| return self.pluginInfo[plugin_id].parameterDataS[parameter_id] | |||
| def get_parameter_ranges(self, plugin_id, parameter_id): | |||
| return self.pluginInfo[plugin_id].parameterRangeS[parameter_id] | |||
| def get_midi_program_data(self, plugin_id, midi_program_id): | |||
| return self.pluginInfo[plugin_id].midiProgramDataS[midi_program_id] | |||
| def get_custom_data(self, plugin_id, custom_data_id): | |||
| return CustomData | |||
| def get_chunk_data(self, plugin_id): | |||
| return None | |||
| def get_parameter_count(self, plugin_id): | |||
| return self.pluginInfo[plugin_id].parameterCountInfo['total'] | |||
| def get_program_count(self, plugin_id): | |||
| return self.pluginInfo[plugin_id].programCount | |||
| def get_midi_program_count(self, plugin_id): | |||
| return self.pluginInfo[plugin_id].midiProgramCount | |||
| def get_custom_data_count(self, plugin_id): | |||
| return 0 | |||
| def get_parameter_text(self, plugin_id, program_id): | |||
| return None | |||
| def get_program_name(self, plugin_id, program_id): | |||
| return self.pluginInfo[plugin_id].programNameS[program_id] | |||
| def get_midi_program_name(self, plugin_id, midi_program_id): | |||
| return self.pluginInfo[plugin_id].midiProgramDataS[midi_program_id]['label'] | |||
| def get_real_plugin_name(self, plugin_id): | |||
| return self.pluginInfo[plugin_id].pluginRealName | |||
| def get_current_program_index(self, plugin_id): | |||
| return self.pluginInfo[plugin_id].programCurrent | |||
| def get_current_midi_program_index(self, plugin_id): | |||
| return self.pluginInfo[plugin_id].midiProgramCurrent | |||
| def get_default_parameter_value(self, plugin_id, parameter_id): | |||
| return self.pluginInfo[plugin_id].parameterRangeS[parameter_id]['def'] | |||
| def get_current_parameter_value(self, plugin_id, parameter_id): | |||
| return self.pluginInfo[plugin_id].parameterValueS[parameter_id] | |||
| def get_input_peak_value(self, plugin_id, port_id): | |||
| return self.pluginInfo[plugin_id].inPeak[port_id-1] | |||
| def get_output_peak_value(self, plugin_id, port_id): | |||
| return self.pluginInfo[plugin_id].outPeak[port_id-1] | |||
| def set_active(self, plugin_id, onoff): | |||
| global to_target, lo_targetName | |||
| lo_path = "/%s/%i/set_active" % (lo_targetName, plugin_id) | |||
| lo_send(lo_target, lo_path, 1 if onoff else 0) | |||
| def set_drywet(self, plugin_id, value): | |||
| global to_target, lo_targetName | |||
| lo_path = "/%s/%i/set_drywet" % (lo_targetName, plugin_id) | |||
| lo_send(lo_target, lo_path, value) | |||
| def set_volume(self, plugin_id, value): | |||
| global to_target, lo_targetName | |||
| lo_path = "/%s/%i/set_volume" % (lo_targetName, plugin_id) | |||
| lo_send(lo_target, lo_path, value) | |||
| def set_balance_left(self, plugin_id, value): | |||
| global to_target, lo_targetName | |||
| lo_path = "/%s/%i/set_balance_left" % (lo_targetName, plugin_id) | |||
| lo_send(lo_target, lo_path, value) | |||
| def set_balance_right(self, plugin_id, value): | |||
| global to_target, lo_targetName | |||
| lo_path = "/%s/%i/set_balance_right" % (lo_targetName, plugin_id) | |||
| lo_send(lo_target, lo_path, value) | |||
| def set_parameter_value(self, plugin_id, parameter_id, value): | |||
| global to_target, lo_targetName | |||
| lo_path = "/%s/%i/set_parameter_value" % (lo_targetName, plugin_id) | |||
| lo_send(lo_target, lo_path, parameter_id, value) | |||
| def set_parameter_midi_channel(self, plugin_id, parameter_id, channel): | |||
| global to_target, lo_targetName | |||
| lo_path = "/%s/%i/set_parameter_midi_channel" % (lo_targetName, plugin_id) | |||
| lo_send(lo_target, lo_path, parameter_id, channel) | |||
| def set_parameter_midi_cc(self, plugin_id, parameter_id, midi_cc): | |||
| global to_target, lo_targetName | |||
| lo_path = "/%s/%i/set_parameter_midi_cc" % (lo_targetName, plugin_id) | |||
| lo_send(lo_target, lo_path, parameter_id, midi_cc) | |||
| def set_program(self, plugin_id, program_id): | |||
| global to_target, lo_targetName | |||
| lo_path = "/%s/%i/set_program" % (lo_targetName, plugin_id) | |||
| lo_send(lo_target, lo_path, program_id) | |||
| def set_midi_program(self, plugin_id, midi_program_id): | |||
| global to_target, lo_targetName | |||
| lo_path = "/%s/%i/set_midi_program" % (lo_targetName, plugin_id) | |||
| lo_send(lo_target, lo_path, midi_program_id) | |||
| def send_midi_note(self, plugin_id, channel, note, velocity): | |||
| global to_target, lo_targetName | |||
| if velocity: | |||
| lo_path = "/%s/%i/note_on" % (lo_targetName, plugin_id) | |||
| lo_send(lo_target, lo_path, channel, note, velocity) | |||
| else: | |||
| lo_path = "/%s/%i/note_off" % (lo_targetName, plugin_id) | |||
| lo_send(lo_target, lo_path, channel, note) | |||
| # OSC Control server | |||
| class ControlServer(ServerThread): | |||
| def __init__(self, parent): | |||
| ServerThread.__init__(self, 8087, LO_TCP) | |||
| self.parent = parent | |||
| def getFullURL(self): | |||
| return "%scarla-control" % self.get_url() | |||
| @make_method('/carla-control/add_plugin_start', 'is') | |||
| def add_plugin_start_callback(self, path, args): | |||
| pluginId, pluginName = args | |||
| self.parent.emit(SIGNAL("AddPluginStart(int, QString)"), pluginId, pluginName) | |||
| @make_method('/carla-control/add_plugin_end', 'i') | |||
| def add_plugin_end_callback(self, path, args): | |||
| pluginId, = args | |||
| self.parent.emit(SIGNAL("AddPluginEnd(int)"), pluginId) | |||
| @make_method('/carla-control/remove_plugin', 'i') | |||
| def remove_plugin_callback(self, path, args): | |||
| pluginId, = args | |||
| self.parent.emit(SIGNAL("RemovePlugin(int)"), pluginId) | |||
| @make_method('/carla-control/set_plugin_data', 'iiiissssh') | |||
| def set_plugin_data_callback(self, path, args): | |||
| pluginId, ptype, category, hints, realName, label, maker, copyright_, uniqueId = args | |||
| self.parent.emit(SIGNAL("SetPluginData(int, int, int, int, QString, QString, QString, QString, int)"), pluginId, ptype, category, hints, realName, label, maker, copyright_, uniqueId) | |||
| @make_method('/carla-control/set_plugin_ports', 'iiiiiiii') | |||
| def set_plugin_ports_callback(self, path, args): | |||
| pluginId, audioIns, audioOuts, midiIns, midiOuts, cIns, cOuts, cTotals = args | |||
| self.parent.emit(SIGNAL("SetPluginPorts(int, int, int, int, int, int, int, int)"), pluginId, audioIns, audioOuts, midiIns, midiOuts, cIns, cOuts, cTotals) | |||
| @make_method('/carla-control/set_parameter_data', 'iiiissd') | |||
| def set_parameter_data_callback(self, path, args): | |||
| pluginId, index, type_, hints, name, label, current = args | |||
| self.parent.emit(SIGNAL("SetParameterData(int, int, int, int, QString, QString, double)"), pluginId, index, type_, hints, name, label, current) | |||
| @make_method('/carla-control/set_parameter_ranges', 'iidddddd') | |||
| def set_parameter_ranges_callback(self, path, args): | |||
| pluginId, index, min_, max_, def_, step, stepSmall, stepLarge = args | |||
| self.parent.emit(SIGNAL("SetParameterRanges(int, int, double, double, double, double, double, double)"), pluginId, index, min_, max_, def_, step, stepSmall, stepLarge) | |||
| @make_method('/carla-control/set_parameter_midi_cc', 'iii') | |||
| def set_parameter_midi_cc_callback(self, path, args): | |||
| pluginId, index, cc = args | |||
| self.parent.emit(SIGNAL("SetParameterMidiCC(int, int, int)"), pluginId, index, cc) | |||
| @make_method('/carla-control/set_parameter_midi_channel', 'iii') | |||
| def set_parameter_midi_channel_callback(self, path, args): | |||
| pluginId, index, channel = args | |||
| self.parent.emit(SIGNAL("SetParameterMidiChannel(int, int, int)"), pluginId, index, channel) | |||
| @make_method('/carla-control/set_parameter_value', 'iid') | |||
| def set_parameter_value_callback(self, path, args): | |||
| pluginId, index, value = args | |||
| self.parent.emit(SIGNAL("SetParameterValue(int, int, double)"), pluginId, index, value) | |||
| @make_method('/carla-control/set_default_value', 'iid') | |||
| def set_default_value_callback(self, path, args): | |||
| pluginId, index, value = args | |||
| self.parent.emit(SIGNAL("SetDefaultValue(int, int, double)"), pluginId, index, value) | |||
| @make_method('/carla-control/set_program', 'ii') | |||
| def set_program_callback(self, path, args): | |||
| pluginId, index = args | |||
| self.parent.emit(SIGNAL("SetProgram(int, int)"), pluginId, index) | |||
| @make_method('/carla-control/set_program_count', 'ii') | |||
| def set_program_count_callback(self, path, args): | |||
| pluginId, count = args | |||
| self.parent.emit(SIGNAL("SetProgramCount(int, int)"), pluginId, count) | |||
| @make_method('/carla-control/set_program_name', 'iis') | |||
| def set_program_name_callback(self, path, args): | |||
| pluginId, index, name = args | |||
| self.parent.emit(SIGNAL("SetProgramName(int, int, QString)"), pluginId, index, name) | |||
| @make_method('/carla-control/set_midi_program', 'ii') | |||
| def set_midi_program_callback(self, path, args): | |||
| pluginId, index = args | |||
| self.parent.emit(SIGNAL("SetMidiProgram(int, int)"), pluginId, index) | |||
| @make_method('/carla-control/set_midi_program_count', 'ii') | |||
| def set_midi_program_count_callback(self, path, args): | |||
| pluginId, count = args | |||
| self.parent.emit(SIGNAL("SetMidiProgramCount(int, int)"), pluginId, count) | |||
| @make_method('/carla-control/set_midi_program_data', 'iiiis') | |||
| def set_midi_program_data_callback(self, path, args): | |||
| pluginId, index, bank, program, name = args | |||
| self.parent.emit(SIGNAL("SetMidiProgramData(int, int, int, int, QString)"), pluginId, index, bank, program, name) | |||
| @make_method('/carla-control/set_input_peak_value', 'iid') | |||
| def set_input_peak_value_callback(self, path, args): | |||
| pluginId, portId, value = args | |||
| self.parent.emit(SIGNAL("SetInputPeakValue(int, int, double)"), pluginId, portId, value) | |||
| @make_method('/carla-control/set_output_peak_value', 'iid') | |||
| def set_output_peak_value_callback(self, path, args): | |||
| pluginId, portId, value = args | |||
| self.parent.emit(SIGNAL("SetOutputPeakValue(int, int, double)"), pluginId, portId, value) | |||
| @make_method('/carla-control/note_on', 'iiii') | |||
| def note_on_callback(self, path, args): | |||
| pluginId, channel, note, velo = args | |||
| self.parent.emit(SIGNAL("NoteOn(int, int, int, int)"), pluginId, channel, note, velo) | |||
| @make_method('/carla-control/note_off', 'iii') | |||
| def note_off_callback(self, path, args): | |||
| pluginId, channel, note = args | |||
| self.parent.emit(SIGNAL("NoteOff(int, int, int)"), pluginId, channel, note) | |||
| @make_method('/carla-control/exit', '') | |||
| def exit_callback(self, path, args): | |||
| self.parent.emit(SIGNAL("Exit()")) | |||
| @make_method(None, None) | |||
| def fallback(self, path, args): | |||
| print("ControlServer::fallback(\"%s\") - unknown message, args =" % path, args) | |||
| # Main Window | |||
| class CarlaControlW(QMainWindow, ui_carla_control.Ui_CarlaControlW): | |||
| def __init__(self, parent=None): | |||
| QMainWindow.__init__(self, parent) | |||
| self.setupUi(self) | |||
| # ------------------------------------------------------------- | |||
| # Load Settings | |||
| self.settings = QSettings("Cadence", "Carla-Control") | |||
| self.loadSettings() | |||
| self.setStyleSheet(""" | |||
| QWidget#centralwidget { | |||
| background-color: qlineargradient(spread:pad, | |||
| x1:0.0, y1:0.0, | |||
| x2:0.2, y2:1.0, | |||
| stop:0 rgb( 7, 7, 7), | |||
| stop:1 rgb(28, 28, 28) | |||
| ); | |||
| } | |||
| """) | |||
| # ------------------------------------------------------------- | |||
| # Internal stuff | |||
| self.lo_address = "" | |||
| self.lo_server = None | |||
| self.m_lastPluginName = None | |||
| self.m_plugin_list = [] | |||
| for x in range(MAX_PLUGINS): | |||
| self.m_plugin_list.append(None) | |||
| # ------------------------------------------------------------- | |||
| # Set-up GUI stuff | |||
| self.act_file_refresh.setEnabled(False) | |||
| self.resize(self.width(), 0) | |||
| # ------------------------------------------------------------- | |||
| # Connect actions to functions | |||
| self.connect(self.act_file_connect, SIGNAL("triggered()"), SLOT("slot_doConnect()")) | |||
| self.connect(self.act_file_refresh, SIGNAL("triggered()"), SLOT("slot_doRefresh()")) | |||
| self.connect(self.act_help_about, SIGNAL("triggered()"), SLOT("slot_aboutCarlaControl()")) | |||
| self.connect(self.act_help_about_qt, SIGNAL("triggered()"), app, SLOT("aboutQt()")) | |||
| self.connect(self, SIGNAL("AddPluginStart(int, QString)"), SLOT("slot_handleAddPluginStart(int, QString)")) | |||
| self.connect(self, SIGNAL("AddPluginEnd(int)"), SLOT("slot_handleAddPluginEnd(int)")) | |||
| self.connect(self, SIGNAL("RemovePlugin(int)"), SLOT("slot_handleRemovePlugin(int)")) | |||
| self.connect(self, SIGNAL("SetPluginData(int, int, int, int, QString, QString, QString, QString, int)"), SLOT("slot_handleSetPluginData(int, int, int, int, QString, QString, QString, QString, int)")) | |||
| self.connect(self, SIGNAL("SetPluginPorts(int, int, int, int, int, int, int, int)"), SLOT("slot_handleSetPluginPorts(int, int, int, int, int, int, int, int)")) | |||
| self.connect(self, SIGNAL("SetParameterData(int, int, int, int, QString, QString, double)"), SLOT("slot_handleSetParameterData(int, int, int, int, QString, QString, double)")) | |||
| self.connect(self, SIGNAL("SetParameterRanges(int, int, double, double, double, double, double, double)"), SLOT("slot_handleSetParameterRanges(int, int, double, double, double, double, double, double)")) | |||
| self.connect(self, SIGNAL("SetParameterMidiCC(int, int, int)"), SLOT("slot_handleSetParameterMidiCC(int, int, int)")) | |||
| self.connect(self, SIGNAL("SetParameterMidiChannel(int, int, int)"), SLOT("slot_handleSetParameterMidiChannel(int, int, int)")) | |||
| self.connect(self, SIGNAL("SetParameterValue(int, int, double)"), SLOT("slot_handleSetParameterValue(int, int, double)")) | |||
| self.connect(self, SIGNAL("SetDefaultValue(int, int, double)"), SLOT("slot_handleSetDefaultValue(int, int, double)")) | |||
| self.connect(self, SIGNAL("SetProgram(int, int)"), SLOT("slot_handleSetProgram(int, int)")) | |||
| self.connect(self, SIGNAL("SetProgramCount(int, int)"), SLOT("slot_handleSetProgramCount(int, int)")) | |||
| self.connect(self, SIGNAL("SetProgramName(int, int, QString)"), SLOT("slot_handleSetProgramName(int, int, QString)")) | |||
| self.connect(self, SIGNAL("SetMidiProgram(int, int)"), SLOT("slot_handleSetMidiProgram(int, int)")) | |||
| self.connect(self, SIGNAL("SetMidiProgramCount(int, int)"), SLOT("slot_handleSetMidiProgramCount(int, int)")) | |||
| self.connect(self, SIGNAL("SetMidiProgramData(int, int, int, int, QString)"), SLOT("slot_handleSetMidiProgramData(int, int, int, int, QString)")) | |||
| self.connect(self, SIGNAL("SetInputPeakValue(int, int, double)"), SLOT("slot_handleSetInputPeakValue(int, int, double)")) | |||
| self.connect(self, SIGNAL("SetOutputPeakValue(int, int, double)"), SLOT("slot_handleSetOutputPeakValue(int, int, double)")) | |||
| self.connect(self, SIGNAL("NoteOn(int, int, int, int)"), SLOT("slot_handleNoteOn(int, int, int, int)")) | |||
| self.connect(self, SIGNAL("NoteOff(int, int, int)"), SLOT("slot_handleNoteOff(int, int, int)")) | |||
| self.connect(self, SIGNAL("Exit()"), SLOT("slot_handleExit()")) | |||
| self.TIMER_GUI_STUFF = self.startTimer(50) # Peaks | |||
| self.TIMER_GUI_STUFF2 = self.startTimer(50 * 2) # LEDs and edit dialog | |||
| def remove_all(self): | |||
| for i in range(MAX_PLUGINS): | |||
| if self.m_plugin_list[i]: | |||
| self.slot_handleRemovePlugin(i) | |||
| @pyqtSlot() | |||
| def slot_doConnect(self): | |||
| global lo_target, lo_targetName | |||
| if lo_target and self.lo_server: | |||
| urlText = self.lo_address | |||
| else: | |||
| urlText = "osc.tcp://127.0.0.1:19000/Carla" | |||
| askValue = QInputDialog.getText(self, self.tr("Carla Control - Connect"), self.tr("Address"), text=urlText) | |||
| if not askValue[1]: | |||
| return | |||
| self.slot_handleExit() | |||
| self.lo_address = askValue[0] | |||
| lo_target = Address(self.lo_address) | |||
| lo_targetName = self.lo_address.rsplit("/", 1)[-1] | |||
| print("Connecting to \"%s\" as '%s'..." % (self.lo_address, lo_targetName)) | |||
| try: | |||
| self.lo_server = ControlServer(self) | |||
| except: # ServerError, err: | |||
| print("Connecting error!") | |||
| #print str(err) | |||
| QMessageBox.critical(self, self.tr("Error"), self.tr("Failed to connect, operation failed.")) | |||
| if self.lo_server: | |||
| self.lo_server.start() | |||
| self.act_file_refresh.setEnabled(True) | |||
| lo_send(lo_target, "/register", self.lo_server.getFullURL()) | |||
| @pyqtSlot() | |||
| def slot_doRefresh(self): | |||
| global lo_target | |||
| if lo_target and self.lo_server: | |||
| self.remove_all() | |||
| lo_send(lo_target, "/unregister") | |||
| lo_send(lo_target, "/register", self.lo_server.getFullURL()) | |||
| @pyqtSlot() | |||
| def slot_aboutCarlaControl(self): | |||
| CarlaAboutW(self).exec_() | |||
| @pyqtSlot(int, str) | |||
| def slot_handleAddPluginStart(self, pluginId, pluginName): | |||
| self.m_lastPluginName = pluginName | |||
| @pyqtSlot(int) | |||
| def slot_handleAddPluginEnd(self, pluginId): | |||
| pwidget = PluginWidget(self, pluginId) | |||
| self.w_plugins.layout().addWidget(pwidget) | |||
| self.m_plugin_list[pluginId] = pwidget | |||
| @pyqtSlot(int) | |||
| def slot_handleRemovePlugin(self, pluginId): | |||
| pwidget = self.m_plugin_list[pluginId] | |||
| if pwidget: | |||
| pwidget.edit_dialog.close() | |||
| pwidget.close() | |||
| pwidget.deleteLater() | |||
| self.w_plugins.layout().removeWidget(pwidget) | |||
| self.m_plugin_list[pluginId] = None | |||
| @pyqtSlot(int, int, int, int, str, str, str, str, int) | |||
| def slot_handleSetPluginData(self, pluginId, type_, category, hints, realName, label, maker, copyright, uniqueId): | |||
| info = deepcopy(PluginInfo) | |||
| info['type'] = type_ | |||
| info['category'] = category | |||
| info['hints'] = hints | |||
| info['name'] = self.m_lastPluginName | |||
| info['label'] = label | |||
| info['maker'] = maker | |||
| info['copyright'] = copyright | |||
| info['uniqueId'] = uniqueId | |||
| Carla.host._set_pluginInfo(pluginId, info) | |||
| Carla.host._set_pluginRealName(pluginId, realName) | |||
| @pyqtSlot(int, int, int, int, int, int, int, int) | |||
| def slot_handleSetPluginPorts(self, pluginId, audioIns, audioOuts, midiIns, midiOuts, cIns, cOuts, cTotals): | |||
| audioInfo = deepcopy(PortCountInfo) | |||
| midiInfo = deepcopy(PortCountInfo) | |||
| paramInfo = deepcopy(PortCountInfo) | |||
| audioInfo['ins'] = audioIns | |||
| audioInfo['outs'] = audioOuts | |||
| audioInfo['total'] = audioIns + audioOuts | |||
| midiInfo['ins'] = midiIns | |||
| midiInfo['outs'] = midiOuts | |||
| midiInfo['total'] = midiIns + midiOuts | |||
| paramInfo['ins'] = cIns | |||
| paramInfo['outs'] = cOuts | |||
| paramInfo['total'] = cTotals | |||
| Carla.host._set_audioCountInfo(pluginId, audioInfo) | |||
| Carla.host._set_midiCountInfo(pluginId, midiInfo) | |||
| Carla.host._set_parameterCountInfo(pluginId, paramInfo) | |||
| @pyqtSlot(int, int, int, int, str, str, float) | |||
| def slot_handleSetParameterData(self, pluginId, index, type_, hints, name, label, current): | |||
| # remove hints not possible in bridge mode | |||
| hints &= ~(PARAMETER_USES_SCALEPOINTS | PARAMETER_USES_CUSTOM_TEXT) | |||
| data = deepcopy(ParameterData) | |||
| data['type'] = type_ | |||
| data['index'] = index | |||
| data['rindex'] = index | |||
| data['hints'] = hints | |||
| info = deepcopy(ParameterInfo) | |||
| info['name'] = name | |||
| info['label'] = label | |||
| Carla.host._set_parameterDataS(pluginId, index, data) | |||
| Carla.host._set_parameterInfoS(pluginId, index, info) | |||
| Carla.host._set_parameterValueS(pluginId, index, current) | |||
| @pyqtSlot(int, int, float, float, float, float, float, float) | |||
| def slot_handleSetParameterRanges(self, pluginId, index, min_, max_, def_, step, stepSmall, stepLarge): | |||
| ranges = deepcopy(ParameterRanges) | |||
| ranges['min'] = min_ | |||
| ranges['max'] = max_ | |||
| ranges['def'] = def_ | |||
| ranges['step'] = step | |||
| ranges['stepSmall'] = stepSmall | |||
| ranges['stepLarge'] = stepLarge | |||
| Carla.host._set_parameterRangeS(pluginId, index, ranges) | |||
| @pyqtSlot(int, int, int) | |||
| def slot_handleSetParameterMidiCC(self, pluginId, index, cc): | |||
| Carla.host._set_parameterMidiCC(pluginId, index, cc) | |||
| pwidget = self.m_plugin_list[pluginId] | |||
| if pwidget: | |||
| pwidget.edit_dialog.set_parameter_midi_cc(index, cc, True) | |||
| @pyqtSlot(int, int, int) | |||
| def slot_handleSetParameterMidiChannel(self, pluginId, index, channel): | |||
| Carla.host._set_parameterMidiChannel(pluginId, index, channel) | |||
| pwidget = self.m_plugin_list[pluginId] | |||
| if pwidget: | |||
| pwidget.edit_dialog.set_parameter_midi_channel(index, channel, True) | |||
| @pyqtSlot(int, int, float) | |||
| def slot_handleSetParameterValue(self, pluginId, parameterId, value): | |||
| if parameterId >= 0: | |||
| Carla.host._set_parameterValueS(pluginId, parameterId, value) | |||
| pwidget = self.m_plugin_list[pluginId] | |||
| if pwidget: | |||
| if parameterId < PARAMETER_NULL: | |||
| pwidget.m_parameterIconTimer = ICON_STATE_ON | |||
| else: | |||
| for param in pwidget.edit_dialog.m_parameterList: | |||
| if param[1] == parameterId: | |||
| if param[0] == PARAMETER_INPUT: | |||
| pwidget.m_parameterIconTimer = ICON_STATE_ON | |||
| break | |||
| if parameterId == PARAMETER_ACTIVE: | |||
| pwidget.set_active((value > 0.0), True, False) | |||
| elif parameterId == PARAMETER_DRYWET: | |||
| pwidget.set_drywet(value * 1000, True, False) | |||
| elif parameterId == PARAMETER_VOLUME: | |||
| pwidget.set_volume(value * 1000, True, False) | |||
| elif parameterId == PARAMETER_BALANCE_LEFT: | |||
| pwidget.set_balance_left(value * 1000, True, False) | |||
| elif parameterId == PARAMETER_BALANCE_RIGHT: | |||
| pwidget.set_balance_right(value * 1000, True, False) | |||
| elif parameterId >= 0: | |||
| pwidget.edit_dialog.set_parameter_to_update(parameterId) | |||
| @pyqtSlot(int, int, float) | |||
| def slot_handleSetDefaultValue(self, pluginId, parameterId, value): | |||
| Carla.host._set_parameterDefaultValue(pluginId, parameterId, value) | |||
| #pwidget = self.m_plugin_list[pluginId] | |||
| #if pwidget: | |||
| #pwidget.edit_dialog.set_parameter_default_value(parameterId, value) | |||
| @pyqtSlot(int, int) | |||
| def slot_handleSetProgram(self, pluginId, index): | |||
| Carla.host._set_currentProgram(pluginId, index) | |||
| pwidget = self.m_plugin_list[pluginId] | |||
| if pwidget: | |||
| pwidget.edit_dialog.set_program(index) | |||
| pwidget.m_parameterIconTimer = ICON_STATE_ON | |||
| @pyqtSlot(int, int) | |||
| def slot_handleSetProgramCount(self, pluginId, count): | |||
| Carla.host._set_programCount(pluginId, count) | |||
| @pyqtSlot(int, int, str) | |||
| def slot_handleSetProgramName(self, pluginId, index, name): | |||
| Carla.host._set_programNameS(pluginId, index, name) | |||
| @pyqtSlot(int, int) | |||
| def slot_handleSetMidiProgram(self, pluginId, index): | |||
| Carla.host._set_currentMidiProgram(pluginId, index) | |||
| pwidget = self.m_plugin_list[pluginId] | |||
| if pwidget: | |||
| pwidget.edit_dialog.set_midi_program(index) | |||
| pwidget.m_parameterIconTimer = ICON_STATE_ON | |||
| @pyqtSlot(int, int) | |||
| def slot_handleSetMidiProgramCount(self, pluginId, count): | |||
| Carla.host._set_midiProgramCount(pluginId, count) | |||
| @pyqtSlot(int, int, int, int, str) | |||
| def slot_handleSetMidiProgramData(self, pluginId, index, bank, program, name): | |||
| data = deepcopy(MidiProgramData) | |||
| data['bank'] = bank | |||
| data['program'] = program | |||
| data['label'] = name | |||
| Carla.host._set_midiProgramDataS(pluginId, index, data) | |||
| @pyqtSlot(int, int, float) | |||
| def slot_handleSetInputPeakValue(self, pluginId, portId, value): | |||
| Carla.host._set_inPeak(pluginId, portId-1, value) | |||
| @pyqtSlot(int, int, float) | |||
| def slot_handleSetOutputPeakValue(self, pluginId, portId, value): | |||
| Carla.host._set_outPeak(pluginId, portId-1, value) | |||
| @pyqtSlot(int, int, int, int) | |||
| def slot_handleNoteOn(self, pluginId, channel, note, velo): | |||
| pwidget = self.m_plugin_list[pluginId] | |||
| if pwidget: | |||
| pwidget.edit_dialog.keyboard.sendNoteOn(note, False) | |||
| @pyqtSlot(int, int, int) | |||
| def slot_handleNoteOff(self, pluginId, channel, note): | |||
| pwidget = self.m_plugin_list[pluginId] | |||
| if pwidget: | |||
| pwidget.edit_dialog.keyboard.sendNoteOff(note, False) | |||
| @pyqtSlot() | |||
| def slot_handleExit(self): | |||
| self.remove_all() | |||
| if self.lo_server: | |||
| self.lo_server.stop() | |||
| self.lo_server = None | |||
| self.act_file_refresh.setEnabled(False) | |||
| global lo_target, lo_targetName | |||
| lo_target = None | |||
| lo_targetName = "" | |||
| self.lo_address = "" | |||
| def saveSettings(self): | |||
| self.settings.setValue("Geometry", self.saveGeometry()) | |||
| #self.settings.setValue("ShowToolbar", self.toolBar.isVisible()) | |||
| def loadSettings(self): | |||
| self.restoreGeometry(self.settings.value("Geometry", "")) | |||
| #show_toolbar = self.settings.value("ShowToolbar", True, type=bool) | |||
| #self.act_settings_show_toolbar.setChecked(show_toolbar) | |||
| #self.toolBar.setVisible(show_toolbar) | |||
| def timerEvent(self, event): | |||
| if event.timerId() == self.TIMER_GUI_STUFF: | |||
| for pwidget in self.m_plugin_list: | |||
| if pwidget: pwidget.check_gui_stuff() | |||
| elif event.timerId() == self.TIMER_GUI_STUFF2: | |||
| for pwidget in self.m_plugin_list: | |||
| if pwidget: pwidget.check_gui_stuff2() | |||
| QMainWindow.timerEvent(self, event) | |||
| def closeEvent(self, event): | |||
| self.saveSettings() | |||
| global lo_target | |||
| if lo_target and self.lo_server: | |||
| lo_send(lo_target, "/unregister") | |||
| QMainWindow.closeEvent(self, event) | |||
| #--------------- main ------------------ | |||
| if __name__ == '__main__': | |||
| # App initialization | |||
| app = QApplication(sys.argv) | |||
| app.setApplicationName("Carla-Control") | |||
| app.setApplicationVersion(VERSION) | |||
| app.setOrganizationName("Cadence") | |||
| app.setWindowIcon(QIcon(":/scalable/carla-control.svg")) | |||
| Carla.host = Host() | |||
| # Create GUI | |||
| Carla.gui = CarlaControlW() | |||
| # Set-up custom signal handling | |||
| setUpSignals(Carla.gui) | |||
| # Show GUI | |||
| Carla.gui.show() | |||
| # App-Loop | |||
| sys.exit(app.exec_()) | |||