diff --git a/source/modules/Makefile b/source/modules/Makefile index 9e0dad8ff..9aae55435 100644 --- a/source/modules/Makefile +++ b/source/modules/Makefile @@ -77,10 +77,12 @@ jackbridge-wine64: clean: rm -f *.a *.def *.dll *.dylib *.so + $(MAKE) clean -C carla_native $(MAKE) clean -C distrho/dgl - $(MAKE) clean -C lilv $(MAKE) clean -C jackbridge + $(MAKE) clean -C juce_audio_basics $(MAKE) clean -C juce_core + $(MAKE) clean -C lilv $(MAKE) clean -C rtmempool $(MAKE) clean -C theme $(MAKE) clean -C widgets diff --git a/source/modules/carla_native/Makefile b/source/modules/carla_native/Makefile index a6fbb9e84..6f702d61f 100644 --- a/source/modules/carla_native/Makefile +++ b/source/modules/carla_native/Makefile @@ -9,13 +9,12 @@ include ../../Makefile.mk # -------------------------------------------------------------- BUILD_C_FLAGS += -I. -I../../includes -BUILD_CXX_FLAGS += -I. -I.. -I../../includes -I../../utils +BUILD_CXX_FLAGS += -I. -I.. -I../distrho -I../../includes -I../../utils # -------------------------------------------------------------- ifeq ($(HAVE_OPENGL),true) GL_CXX_FLAGS = $(BUILD_CXX_FLAGS) -GL_CXX_FLAGS += -I../distrho GL_CXX_FLAGS += $(shell pkg-config --cflags gl) endif @@ -83,9 +82,9 @@ OBJS += \ # distrho-stereoenhancer.cpp.o endif -# # DISTRHO plugins (Qt) -# OBJS += \ -# distrho-notes.cpp.o +# DISTRHO plugins (PyQt) +OBJS += \ + distrho-notes.cpp.o ifeq ($(HAVE_ZYN_DEPS),true) # ZynAddSubFX @@ -175,19 +174,19 @@ audio_decoder/%.c.o: audio_decoder/%.c audio-file.cpp.o: audio-file.cpp audio-base.hpp $(CXXDEPS) $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ -distrho-3bandeq.cpp.o: distrho-3bandeq.cpp 3bandeq/*.cpp 3bandeq/*.h 3bandeq/*.hpp distrho/DistrhoPluginCarla.cpp $(CXXDEPS) +distrho-3bandeq.cpp.o: distrho-3bandeq.cpp #3bandeq/*.cpp 3bandeq/*.h 3bandeq/*.hpp distrho/DistrhoPluginCarla.cpp $(CXXDEPS) $(CXX) $< $(GL_CXX_FLAGS) -I3bandeq -DDISTRHO_NAMESPACE=DISTRHO_3BandEQ -c -o $@ -distrho-3bandsplitter.cpp.o: distrho-3bandsplitter.cpp 3bandsplitter/*.cpp 3bandsplitter/*.h 3bandsplitter/*.hpp distrho/DistrhoPluginCarla.cpp $(CXXDEPS) +distrho-3bandsplitter.cpp.o: distrho-3bandsplitter.cpp #3bandsplitter/*.cpp 3bandsplitter/*.h 3bandsplitter/*.hpp distrho/DistrhoPluginCarla.cpp $(CXXDEPS) $(CXX) $< $(GL_CXX_FLAGS) -I3bandsplitter -DDISTRHO_NAMESPACE=DISTRHO_3BandSplitter -c -o $@ -distrho-nekobi.cpp.o: distrho-nekobi.cpp nekobi/*.cpp nekobi/*.h nekobi/*.hpp nekobi/nekobee-src/*.c nekobi/nekobee-src/*.h distrho/DistrhoPluginCarla.cpp $(CXXDEPS) +distrho-nekobi.cpp.o: distrho-nekobi.cpp nekobi/*.cpp #nekobi/*.h nekobi/*.hpp nekobi/nekobee-src/*.c nekobi/nekobee-src/*.h distrho/DistrhoPluginCarla.cpp $(CXXDEPS) $(CXX) $< $(GL_CXX_FLAGS) -Inekobi -DDISTRHO_NAMESPACE=DISTRHO_Nekobi -c -o $@ -distrho-pingpongpan.cpp.o: distrho-pingpongpan.cpp pingpongpan/*.cpp pingpongpan/*.h pingpongpan/*.hpp distrho/DistrhoPluginCarla.cpp $(CXXDEPS) +distrho-pingpongpan.cpp.o: distrho-pingpongpan.cpp #pingpongpan/*.cpp pingpongpan/*.h pingpongpan/*.hpp distrho/DistrhoPluginCarla.cpp $(CXXDEPS) $(CXX) $< $(GL_CXX_FLAGS) -Ipingpongpan -DDISTRHO_NAMESPACE=DISTRHO_PingPongPan -c -o $@ -distrho-stereoenhancer.cpp.o: distrho-stereoenhancer.cpp stereoenhancer/*.cpp stereoenhancer/*.h stereoenhancer/*.hpp distrho/DistrhoPluginCarla.cpp $(CXXDEPS) +distrho-stereoenhancer.cpp.o: distrho-stereoenhancer.cpp #stereoenhancer/*.cpp stereoenhancer/*.h stereoenhancer/*.hpp distrho/DistrhoPluginCarla.cpp $(CXXDEPS) $(CXX) $< $(GL_CXX_FLAGS) -Istereoenhancer -DDISTRHO_NAMESPACE=DISTRHO_StereoEnhancer -c -o $@ distrho-notes.cpp.o: distrho-notes.cpp notes/*.cpp notes/*.h notes/*.hpp distrho/DistrhoPluginCarla.cpp $(CXXDEPS) diff --git a/source/modules/carla_native/distrho-notes.cpp b/source/modules/carla_native/distrho-notes.cpp index ce0d922f5..3e00ebe3c 100644 --- a/source/modules/carla_native/distrho-notes.cpp +++ b/source/modules/carla_native/distrho-notes.cpp @@ -30,7 +30,7 @@ START_NAMESPACE_DISTRHO static const PluginDescriptor notesDesc = { /* category */ PLUGIN_CATEGORY_UTILITY, - /* hints */ static_cast(PLUGIN_IS_RTSAFE|PLUGIN_HAS_GUI|PLUGIN_USES_SINGLE_THREAD), + /* hints */ static_cast(PLUGIN_IS_RTSAFE|PLUGIN_HAS_GUI), /* supports */ static_cast(0x0), /* audioIns */ DISTRHO_PLUGIN_NUM_INPUTS, /* audioOuts */ DISTRHO_PLUGIN_NUM_OUTPUTS, diff --git a/source/modules/carla_native/distrho/DistrhoPluginCarla.cpp b/source/modules/carla_native/distrho/DistrhoPluginCarla.cpp index 05a6bfa0c..3cb6b3c1a 100644 --- a/source/modules/carla_native/distrho/DistrhoPluginCarla.cpp +++ b/source/modules/carla_native/distrho/DistrhoPluginCarla.cpp @@ -14,27 +14,39 @@ * For a full copy of the license see the LGPL.txt file */ -#include "../CarlaNative.hpp" - -#include "DistrhoPluginMain.cpp" - #ifdef DISTRHO_UI_QT # error We do not want Qt in the engine code! #endif -#ifdef DISTRHO_UI_EXTERNAL -# error Not implemented yet -#endif + +#include "../CarlaNative.hpp" +#include "CarlaString.hpp" + +#include "DistrhoPluginMain.cpp" #define DISTRHO_PLUGIN_HAS_UI 1 #if DISTRHO_PLUGIN_HAS_UI # include "DistrhoUIMain.cpp" -# include "dgl/App.hpp" -# include "dgl/Window.hpp" +# ifdef DISTRHO_UI_OPENGL +# include "dgl/App.hpp" +# include "dgl/Window.hpp" +# endif #endif -using namespace DGL; +#define WAIT_START_TIMEOUT 3000 /* ms */ +#define WAIT_ZOMBIE_TIMEOUT 3000 /* ms */ +#define WAIT_STEP 100 /* ms */ + +#include +//#include +#include + +using juce::ChildProcess; +using juce::NamedPipe; +using juce::Random; using juce::ScopedPointer; +using juce::String; +using juce::StringArray; // ----------------------------------------------------------------------- @@ -51,82 +63,463 @@ public: : fHost(host), fPlugin(plugin), fUi(this, 0, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, uiResizeCallback), +#ifdef DISTRHO_UI_OPENGL glApp(fUi.getApp()), glWindow(fUi.getWindow()) +#else + pipeRecv(-1), + pipeSend(-1), + pid(-1) +#endif { +#ifdef DISTRHO_UI_OPENGL glWindow.setSize(fUi.getWidth(), fUi.getHeight()); glWindow.setWindowTitle(host->uiName); +#else + + const char* argv[6]; + + //------------------------------------------ + // argv[0] => filename + + CarlaString filename; + filename += fHost->resourceDir; +#ifdef CARLA_OS_WIN + filename += "\\resources\\"; +#else + filename += "/resources/"; +#endif + filename += fUi.getExternalFilename(); + + argv[0] = (const char*)filename; + + //------------------------------------------ + // argv[1] => sample rate + + char sampleRateStr[12+1]; + std::snprintf(sampleRateStr, 12, "%g", host->get_sample_rate(host->handle)); + sampleRateStr[12] = '\0'; + + argv[1] = sampleRateStr; + + //------------------------------------------ + // argv[2-3] => pipes + + int pipe1[2]; /* written by host process, read by plugin UI process */ + int pipe2[2]; /* written by plugin UI process, read by host process */ + + if (pipe(pipe1) != 0) + { + fail("pipe1 creation failed"); + return; + } + + if (pipe(pipe2) != 0) + { + fail("pipe2 creation failed"); + return; + } + + char uiPipeRecv[100+1]; + char uiPipeSend[100+1]; + + std::snprintf(uiPipeRecv, 100, "%d", pipe1[0]); /* [0] means reading end */ + std::snprintf(uiPipeSend, 100, "%d", pipe2[1]); /* [1] means writting end */ + + uiPipeRecv[100] = '\0'; + uiPipeSend[100] = '\0'; + + argv[2] = uiPipeRecv; /* reading end */ + argv[3] = uiPipeSend; /* writting end */ + + //------------------------------------------ + // argv[4] => UI Name + + argv[4] = host->uiName; + + //------------------------------------------ + // argv[5] => NULL + + argv[5] = nullptr; + + //------------------------------------------ + // fork + + int ret = -1; + + if ((! fork_exec(argv, &ret)) || ret == -1) + { + close(pipe1[0]); + close(pipe1[1]); + close(pipe2[0]); + close(pipe2[1]); + fail("fork_exec() failed"); + return; + } + + pid = ret; + + /* fork duplicated the handles, close pipe ends that are used by the child process */ + close(pipe1[0]); + close(pipe2[1]); + + pipeSend = pipe1[1]; /* [1] means writting end */ + pipeRecv = pipe2[0]; /* [0] means reading end */ + + fcntl(pipeRecv, F_SETFL, fcntl(pipeRecv, F_GETFL) | O_NONBLOCK); + + //------------------------------------------ + // wait a while for child process to confirm it is alive + + char ch; + + for (int i=0; ;) + { + ret = read(pipeRecv, &ch, 1); + + switch (ret) + { + case -1: + if (errno == EAGAIN) + { + if (i < WAIT_START_TIMEOUT / WAIT_STEP) + { + carla_msleep(WAIT_STEP); + i++; + continue; + } + + carla_stderr("we have waited for child with pid %d to appear for %.1f seconds and we are giving up", (int)pid, (float)WAIT_START_TIMEOUT / 1000.0f); + } + else + carla_stderr("read() failed: %s", strerror(errno)); + break; + + case 1: + if (ch == '\n') + // success + return; + + carla_stderr("read() wrong first char '%c'", ch); + break; + + default: + carla_stderr("read() returned %d", ret); + break; + } + + break; + } + + carla_stderr("force killing misbehaved child %d (start)", (int)pid); + + if (kill(pid, SIGKILL) == -1) + { + carla_stderr("kill() failed: %s (start)\n", strerror(errno)); + } + + /* wait a while child to exit, we dont like zombie processes */ + wait_child(pid); +#endif + } + + void fail(const char* const error) + { + carla_stderr2(error); + fHost->dispatcher(fHost->handle, HOST_OPCODE_UI_UNAVAILABLE, 0, 0, nullptr, 0.0f); + } + + static bool fork_exec(const char* const argv[6], int* const retp) + { + int ret = *retp = vfork(); + + switch (ret) + { + case 0: /* child process */ + execvp(argv[0], (char* const*)argv); + carla_stderr2("exec of UI failed: %s", strerror(errno)); + return false; + case -1: + carla_stderr2("fork() failed to create new process for plugin UI"); + return false; + } + + return true; + } + + static bool wait_child(pid_t pid) + { + pid_t ret; + int i; + + if (pid == -1) + { + carla_stderr2("Can't wait for pid -1"); + return false; + } + + for (i = 0; i < WAIT_ZOMBIE_TIMEOUT / WAIT_STEP; ++i) + { + ret = waitpid(pid, NULL, WNOHANG); + + if (ret != 0) + { + if (ret == pid) + { + //printf("child zombie with pid %d was consumed.\n", (int)pid); + return true; + } + + if (ret == -1) + { + carla_stderr2("waitpid(%d) failed: %s", (int)pid, strerror(errno)); + return false; + } + + carla_stderr2("we have waited for child pid %d to exit but we got pid %d instead", (int)pid, (int)ret); + + return false; + } + + carla_msleep(WAIT_STEP); /* wait 100 ms */ + } + + carla_stderr2("we have waited for child with pid %d to exit for %.1f seconds and we are giving up", (int)pid, (float)WAIT_START_TIMEOUT / 1000.0f); + return false; + } + + char* read_line() const + { + char ch; + ssize_t ret; + + char buf[0xff]; + char* ptr = buf; + + for (int i=0; i < 0xff; ++i) + { + ret = read(pipeRecv, &ch, 1); + + if (ret == 1 && ch != '\n') + { + if (ch == '\r') + ch = '\n'; + + *ptr++ = ch; + continue; + } + + if (ptr != buf) + { + *ptr = '\0'; + return strdup(buf); + } + + break; + } + + return nullptr; + } + + ~UICarla() + { + printf("UI CARLA HERE 00END\n"); +#ifdef DISTRHO_UI_EXTERNAL + write(pipeSend, "quit\n", 5); + + /* for a while wait child to exit, we dont like zombie processes */ + if (! wait_child(pid)) + { + carla_stderr2("force killing misbehaved child %d (exit)", (int)pid); + + if (kill(pid, SIGKILL) == -1) + carla_stderr2("kill() failed: %s (exit)", strerror(errno)); + else + wait_child(pid); + } +#endif } // --------------------------------------------- void carla_show(const bool yesNo) { +#ifdef DISTRHO_UI_OPENGL glWindow.setVisible(yesNo); +#else + if (yesNo) + write(pipeSend, "show\n", 5); + else + write(pipeSend, "hide\n", 5); +#endif } void carla_idle() { fUi.idle(); + +#if 1//def DISTRHO_UI_EXTERNAL + char* locale = strdup(setlocale(LC_NUMERIC, nullptr)); + setlocale(LC_NUMERIC, "POSIX"); + + for (;;) + { + char* const msg = read_line(); + + if (msg == nullptr) + break; + + if (std::strcmp(msg, "control") == 0) + { + int index; + float value; + char* indexStr = read_line(); + char* valueStr = read_line(); + + index = atoi(indexStr); + + if (sscanf(valueStr, "%f", &value) == 1) + fHost->ui_parameter_changed(fHost->handle, index, value); + else + fprintf(stderr, "failed to convert \"%s\" to float\n", valueStr); + + carla_stdout("PARAM CHANGE, %i %f", index, value); + + std::free(indexStr); + std::free(valueStr); + } + else if (std::strcmp(msg, "configure") == 0) + { + char* const key = read_line(); + char* const value = read_line(); + + fHost->ui_custom_data_changed(fHost->handle, key, value); + + carla_stdout("STATE CHANGE, \"%s\" \"%s\"", key, value); + + std::free(key); + std::free(value); + } + else if (std::strcmp(msg, "exiting") == 0) + { + /* for a while wait child to exit, we dont like zombie processes */ + if (! wait_child(pid)) + { + fprintf(stderr, "force killing misbehaved child %d (exit)\n", (int)pid); + + if (kill(pid, SIGKILL) == -1) + fprintf(stderr, "kill() failed: %s (exit)\n", strerror(errno)); + else + wait_child(pid); + } + + fHost->ui_closed(fHost->handle); + } + else + { + carla_stderr("unknown message HOST: \"%s\"", msg); + } + + std::free(msg); + } + + setlocale(LC_NUMERIC, locale); + std::free(locale); +#endif } void carla_setParameterValue(const uint32_t index, const float value) { +#ifdef DISTRHO_UI_OPENGL fUi.parameterChanged(index, value); +#else + char msgParamIndex[0xff+1]; + char msgParamValue[0xff+1]; + + std::snprintf(msgParamIndex, 0xff, "%d", index); + std::snprintf(msgParamValue, 0xff, "%f", value); + + msgParamIndex[0xff] = '\0'; + msgParamValue[0xff] = '\0'; + + write(pipeSend, "control\n", 8); + write(pipeSend, msgParamIndex, std::strlen(msgParamIndex)); + write(pipeSend, msgParamValue, std::strlen(msgParamValue)); +#endif } +#if DISTRHO_PLUGIN_WANT_PROGRAMS void carla_setMidiProgram(const uint32_t realProgram) { -#if DISTRHO_PLUGIN_WANT_PROGRAMS + #ifdef DISTRHO_UI_OPENGL fUi.programChanged(realProgram); -#else - return; - // unused - (void)realProgram; -#endif + #else + char msgProgram[0xff+1]; + + std::snprintf(msgProgram, 0xff, "%d", realProgram); + + msgProgram[0xff] = '\0'; + + write(pipeSend, "program\n", 8); + write(pipeSend, msgProgram, std::strlen(msgProgram)); + #endif } +#endif +#if DISTRHO_PLUGIN_WANT_STATE void carla_setCustomData(const char* const key, const char* const value) { -#if DISTRHO_PLUGIN_WANT_STATE + #ifdef DISTRHO_UI_OPENGL fUi.stateChanged(key, value); -#else - return; - // unused - (void)key; - (void)value; -#endif + #else + CarlaString skey(key), svalue(value); + skey.replace('\n', '\r'); + svalue.replace('\n', '\r'); + + write(pipeSend, "configure\n", 10); + write(pipeSend, (const char*)skey, skey.length()); + write(pipeSend, (const char*)svalue, svalue.length()); + #endif } +#endif - void carla_setUiTitle(const char* const uiName) + void carla_setUiTitle(const char* const uiTitle) { +#ifdef DISTRHO_UI_OPENGL glWindow.setWindowTitle(uiName); +#else + CarlaString stitle(uiTitle); + stitle.replace('\n', '\r'); + + write(pipeSend, "uiTitle\n", 8); + write(pipeSend, (const char*)stitle, stitle.length()); +#endif } // --------------------------------------------- protected: - void editParameter(uint32_t, bool) + void handleEditParameter(uint32_t, bool) { // TODO } - void setParameterValue(uint32_t rindex, float value) + void handleSetParameterValue(uint32_t rindex, float value) { fHost->ui_parameter_changed(fHost->handle, rindex, value); } - void setState(const char* key, const char* value) + void handleSetState(const char* key, const char* value) { fHost->ui_custom_data_changed(fHost->handle, key, value); } - void sendNote(bool, uint8_t, uint8_t, uint8_t) + void handleSendNote(bool, uint8_t, uint8_t, uint8_t) { // TODO } - void uiResize(unsigned int /*width*/, unsigned int /*height*/) + void handleUiResize(unsigned int /*width*/, unsigned int /*height*/) { // TODO } @@ -141,41 +534,63 @@ private: // UI UIInternal fUi; +#ifdef DISTRHO_UI_OPENGL // OpenGL stuff App& glApp; Window& glWindow; +#else + int pipeRecv; /* the pipe end that is used for receiving messages from UI */ + int pipeSend; /* the pipe end that is used for sending messages to UI */ + pid_t pid; +#endif // --------------------------------------------- // Callbacks +#ifdef DISTRHO_UI_OPENGL #define handlePtr ((UICarla*)ptr) static void editParameterCallback(void* ptr, uint32_t index, bool started) { - handlePtr->editParameter(index, started); + handlePtr->handleEditParameter(index, started); } static void setParameterCallback(void* ptr, uint32_t rindex, float value) { - handlePtr->setParameterValue(rindex, value); + handlePtr->handleSetParameterValue(rindex, value); } + #if DISTRHO_PLUGIN_WANT_STATE static void setStateCallback(void* ptr, const char* key, const char* value) { - handlePtr->setState(key, value); + handlePtr->handleSetState(key, value); } + #else + static constexpr setStateFunc setStateCallback = nullptr; + #endif + #if DISTRHO_PLUGIN_IS_SYNTH static void sendNoteCallback(void* ptr, bool onOff, uint8_t channel, uint8_t note, uint8_t velocity) { - handlePtr->sendNote(onOff, channel, note, velocity); + handlePtr->handleSendNote(onOff, channel, note, velocity); } + #else + static constexpr sendNoteFunc sendNoteCallback = nullptr; + #endif static void uiResizeCallback(void* ptr, unsigned int width, unsigned int height) { - handlePtr->uiResize(width, height); + handlePtr->handleUiResize(width, height); } #undef handlePtr +#else + static constexpr editParamFunc editParameterCallback = nullptr; + static constexpr setParamFunc setParameterCallback = nullptr; + static constexpr setStateFunc setStateCallback = nullptr; + static constexpr sendNoteFunc sendNoteCallback = nullptr; + static constexpr uiResizeFunc uiResizeCallback = nullptr; +#endif CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UICarla) }; diff --git a/source/modules/carla_native/nekofilter/ui.c b/source/modules/carla_native/nekofilter/ui.c index 1bfe83880..163da34b5 100644 --- a/source/modules/carla_native/nekofilter/ui.c +++ b/source/modules/carla_native/nekofilter/ui.c @@ -405,11 +405,13 @@ nekoui_instantiate( if (pipe(pipe1) != 0) { fprintf(stderr, "pipe1 creation failed.\n"); + goto fail_free_control; } if (pipe(pipe2) != 0) { fprintf(stderr, "pipe2 creation failed.\n"); + goto fail_free_control; } snprintf(ui_recv_pipe, sizeof(ui_recv_pipe), "%d", pipe1[0]); /* [0] means reading end */ diff --git a/source/modules/carla_native/notes/DistrhoPluginNotes.cpp b/source/modules/carla_native/notes/DistrhoPluginNotes.cpp new file mode 100644 index 000000000..00d1eeb1d --- /dev/null +++ b/source/modules/carla_native/notes/DistrhoPluginNotes.cpp @@ -0,0 +1,116 @@ +/* + * DISTRHO Notes Plugin + * Copyright (C) 2012-2013 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the doc/GPL.txt file. + */ + +#include "DistrhoPluginNotes.hpp" + +#include + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- + +DistrhoPluginNotes::DistrhoPluginNotes() + : Plugin(1, 0, 103) // 1 parameter, 0 programs, 103 states +{ + fCurPage = 0; +} + +DistrhoPluginNotes::~DistrhoPluginNotes() +{ +} + +// ----------------------------------------------------------------------- +// Init + +void DistrhoPluginNotes::d_initParameter(uint32_t index, Parameter& parameter) +{ + if (index != 0) + return; + + parameter.hints = PARAMETER_IS_AUTOMABLE | PARAMETER_IS_INTEGER; + parameter.name = "Page"; + parameter.symbol = "page"; + parameter.ranges.def = 1; + parameter.ranges.min = 1; + parameter.ranges.max = 100; + parameter.ranges.step = 1; + parameter.ranges.stepSmall = 1; + parameter.ranges.stepLarge = 10; +} + +void DistrhoPluginNotes::d_initStateKey(uint32_t index, d_string& stateKey) +{ + switch (index) + { + case 0: + stateKey = "readOnly"; + break; + case 1 ... 100: + stateKey = "pageText #" + d_string(index); + break; + case 101: + stateKey = "guiWidth"; + break; + case 102: + stateKey = "guiHeight"; + break; + } +} + +// ----------------------------------------------------------------------- +// Internal data + +float DistrhoPluginNotes::d_getParameterValue(uint32_t index) const +{ + if (index != 0) + return 0.0f; + + return fCurPage; +} + +void DistrhoPluginNotes::d_setParameterValue(uint32_t index, float value) +{ + if (index != 0) + return; + + fCurPage = int(value); +} + +void DistrhoPluginNotes::d_setState(const char*, const char*) +{ + // do nothing, used only for UI state +} + +// ----------------------------------------------------------------------- +// Process + +void DistrhoPluginNotes::d_run(float** inputs, float** outputs, uint32_t frames, const MidiEvent*, uint32_t) +{ + std::memcpy(outputs[0], inputs[0], sizeof(float)*frames); + std::memcpy(outputs[1], inputs[1], sizeof(float)*frames); +} + +// ----------------------------------------------------------------------- + +Plugin* createPlugin() +{ + return new DistrhoPluginNotes(); +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO diff --git a/source/modules/carla_native/notes/DistrhoPluginNotes.hpp b/source/modules/carla_native/notes/DistrhoPluginNotes.hpp new file mode 100644 index 000000000..e3ce0e17b --- /dev/null +++ b/source/modules/carla_native/notes/DistrhoPluginNotes.hpp @@ -0,0 +1,90 @@ +/* + * DISTRHO Notes Plugin + * Copyright (C) 2012-2013 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the doc/GPL.txt file. + */ + +#ifndef DISTRHO_PLUGIN_NOTES_HPP_INCLUDED +#define DISTRHO_PLUGIN_NOTES_HPP_INCLUDED + +#include "DistrhoPlugin.hpp" + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- + +class DistrhoPluginNotes : public Plugin +{ +public: + DistrhoPluginNotes(); + ~DistrhoPluginNotes() override; + +protected: + // ------------------------------------------------------------------- + // Information + + const char* d_getLabel() const noexcept override + { + return "Notes"; + } + + const char* d_getMaker() const noexcept override + { + return "DISTRHO"; + } + + const char* d_getLicense() const noexcept override + { + return "GPL v2+"; + } + + uint32_t d_getVersion() const noexcept override + { + return 0x1000; + } + + long d_getUniqueId() const noexcept override + { + return d_cconst('D', 'N', 'o', 't'); + } + + // ------------------------------------------------------------------- + // Init + + void d_initParameter(uint32_t index, Parameter& parameter) override; + void d_initStateKey(uint32_t index, d_string& stateKeyName) override; + + // ------------------------------------------------------------------- + // Internal data + + float d_getParameterValue(uint32_t index) const override; + void d_setParameterValue(uint32_t index, float value) override; + void d_setState(const char* key, const char* value) override; + + // ------------------------------------------------------------------- + // Process + + void d_run(float** inputs, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount) override; + + // ------------------------------------------------------------------- + +private: + int fCurPage; +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_PLUGIN_NOTES_HPP_INCLUDED diff --git a/source/modules/carla_native/notes/DistrhoUINotes.cpp b/source/modules/carla_native/notes/DistrhoUINotes.cpp new file mode 100644 index 000000000..92f9dadb1 --- /dev/null +++ b/source/modules/carla_native/notes/DistrhoUINotes.cpp @@ -0,0 +1,38 @@ +/* + * DISTRHO Notes Plugin + * Copyright (C) 2012-2013 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the doc/GPL.txt file. + */ + +#include "DistrhoUINotes.hpp" + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- + +DistrhoUINotes::DistrhoUINotes() + : ExternalUI() +{ +} + +// ----------------------------------------------------------------------- + +UI* createUI() +{ + return new DistrhoUINotes(); +} + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO diff --git a/source/modules/carla_native/notes/DistrhoUINotes.cpp.old b/source/modules/carla_native/notes/DistrhoUINotes.cpp.old new file mode 100644 index 000000000..e69de29bb diff --git a/source/modules/carla_native/notes/DistrhoUINotes.hpp b/source/modules/carla_native/notes/DistrhoUINotes.hpp new file mode 100644 index 000000000..40dbb89c0 --- /dev/null +++ b/source/modules/carla_native/notes/DistrhoUINotes.hpp @@ -0,0 +1,48 @@ +/* + * DISTRHO Notes Plugin + * Copyright (C) 2012-2013 Filipe Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For a full copy of the GNU General Public License see the doc/GPL.txt file. + */ + +#ifndef DISTRHO_UI_NOTES_HPP_INCLUDED +#define DISTRHO_UI_NOTES_HPP_INCLUDED + +#include "DistrhoUIExternal.hpp" + +START_NAMESPACE_DISTRHO + +// ----------------------------------------------------------------------- + +class DistrhoUINotes : public ExternalUI +{ +public: + DistrhoUINotes(); + +protected: + // ------------------------------------------------------------------- + // Information (External) + + const char* d_getExternalFilename() const override + { + return "notes-ui"; + } + + // ------------------------------------------------------------------- +}; + +// ----------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +#endif // DISTRHO_UI_NOTES_HPP_INCLUDED diff --git a/source/modules/carla_native/resources/externalui.py b/source/modules/carla_native/resources/externalui.py new file mode 100755 index 000000000..5874b55f0 --- /dev/null +++ b/source/modules/carla_native/resources/externalui.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# DISTRHO Plugin Toolkit (DPT) +# Copyright (C) 2012-2013 Filipe Coelho +# +# Permission to use, copy, modify, and/or distribute this software for any purpose with +# or without fee is hereby granted, provided that the above copyright notice and this +# permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD +# TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN +# NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# ----------------------------------------------------------------------- +# Imports + +from os import fdopen, O_NONBLOCK +from fcntl import fcntl, F_GETFL, F_SETFL +from sys import argv + +# ----------------------------------------------------------------------- +# External UI + +class ExternalUI(object): + def __init__(self): + object.__init__(self) + + self.fPipeRecv = None + self.fPipeSend = None + self.fQuitReceived = False + + self.fSampleRate = float(argv[1]) + self.fPipeRecvFd = int(argv[2]) + self.fPipeSendFd = int(argv[3]) + self.fUiName = argv[4] + + fcntl(self.fPipeRecvFd, F_SETFL, fcntl(self.fPipeRecvFd, F_GETFL) | O_NONBLOCK) + + self.fPipeRecv = fdopen(self.fPipeRecvFd, 'r') + self.fPipeSend = fdopen(self.fPipeSendFd, 'w') + + # send empty line (just newline char) + self.send([""]) + + # ------------------------------------------------------------------- + # Host DSP State + + def d_getSampleRate(self): + return self.fSampleRate + + def d_editParameter(self, index, started): + self.send(["editParam", index, started]) + + def d_setParameterValue(self, index, value): + self.send(["control", index, value]) + + def d_setState(self, key, value): + self.send(["configure", key, value]) + + def d_sendNote(self, onOff, channel, note, velocity): + self.send(["note", onOff, channel, note, velocity]) + + # ------------------------------------------------------------------- + # DSP Callbacks + + def d_parameterChanged(self, index, value): + return + + def d_programChanged(self, index): + return + + def d_stateChanged(self, key, value): + return + + def d_noteReceived(self, onOff, channel, note, velocity): + return + + # ------------------------------------------------------------------- + # ExternalUI Callbacks + + def d_uiShow(self): + return + + def d_uiHide(self): + return + + def d_uiQuit(self): + return + + def d_uiTitleChanged(self, uiTitle): + return + + # ------------------------------------------------------------------- + # Public methods + + def closeExternalUI(self): + if not self.fQuitReceived: + self.send(["exiting"]) + + if self.fPipeRecv is not None: + self.fPipeRecv.close() + self.fPipeRecv = None + + if self.fPipeSend is not None: + self.fPipeSend.close() + self.fPipeSend = None + + def idleExternalUI(self): + if self.fPipeRecv is None: + return False + + try: + msg = self.fPipeRecv.readline().strip() + except IOError: + return False + + if msg == "": + return True + + if msg == "control": + index = int(self.fPipeRecv.readline()) + value = float(self.fPipeRecv.readline()) + self.d_parameterChanged(index, value) + + elif msg == "program": + index = int(self.fPipeRecv.readline()) + self.d_programChanged(index) + + elif msg == "configure": + key = self.fPipeRecv.readline().strip().replace("\r", "\n") + value = self.fPipeRecv.readline().strip().replace("\r", "\n") + self.d_stateChanged(key, value) + + elif msg == "note": + onOff = bool(self.fPipeRecv.readline().strip() == "true") + channel = int(self.fPipeRecv.readline()) + note = int(self.fPipeRecv.readline()) + velocity = int(self.fPipeRecv.readline()) + self.d_noteReceived(onOff, channel, note, velocity) + + elif msg == "show": + self.d_uiShow() + + elif msg == "hide": + self.d_uiHide() + + elif msg == "quit": + self.fQuitReceived = True + self.d_uiQuit() + + elif msg == "uiTitle": + uiTitle = self.fPipeRecv.readline().strip().replace("\r", "\n") + self.d_uiTitleChanged(uiTitle) + + else: + print("unknown message: \"" + msg + "\"") + + return True + + # ------------------------------------------------------------------- + # Internal stuff + + def send(self, lines): + if self.fPipeSend is None: + return + + for line in lines: + if isinstance(line, str): + line2 = line.replace("\n", "\r") + else: + if isinstance(line, bool): + line2 = "true" if line else "false" + elif isinstance(line, int): + line2 = "%i" % line + elif isinstance(line, float): + line2 = "%.10f" % line + else: + return + + self.fPipeSend.write(line2 + "\n") + self.fPipeSend.flush() diff --git a/source/backend/resources/nekofilter-ui b/source/modules/carla_native/resources/nekofilter-ui similarity index 100% rename from source/backend/resources/nekofilter-ui rename to source/modules/carla_native/resources/nekofilter-ui diff --git a/source/backend/resources/nekofilter/lv2logo.png b/source/modules/carla_native/resources/nekofilter/lv2logo.png similarity index 100% rename from source/backend/resources/nekofilter/lv2logo.png rename to source/modules/carla_native/resources/nekofilter/lv2logo.png diff --git a/source/modules/carla_native/resources/notes-ui b/source/modules/carla_native/resources/notes-ui new file mode 100755 index 000000000..24e703d49 --- /dev/null +++ b/source/modules/carla_native/resources/notes-ui @@ -0,0 +1,233 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# DISTRHO Notes Plugin +# Copyright (C) 2012-2013 Filipe Coelho +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# For a full copy of the GNU General Public License see the GPL.txt file + +# ------------------------------------------------------------------------------------------------------------ +# Imports (Global) + +from numpy import rint +from sys import argv, exit + +try: + from PyQt5.QtCore import pyqtSlot + from PyQt5.QtWidgets import QApplication, QGridLayout, QLabel, QPushButton, QTextEdit, QWidget +except: + from PyQt4.QtCore import pyqtSlot + from PyQt4.QtGui import QApplication, QGridLayout, QLabel, QPushButton, QTextEdit, QWidget + +# ----------------------------------------------------------------------- +# Imports (ExternalUI) + +from externalui import ExternalUI +from paramspinbox import ParamSpinBox + +# ----------------------------------------------------------------------- +# External UI + +class DistrhoUINotes(QWidget, ExternalUI): + def __init__(self): + QWidget.__init__(self, None) + ExternalUI.__init__(self) + + self.fCurPage = 1 + self.fSaveSizeNowChecker = -1 + self.fSaveTextNowChecker = -1 + self.fNotes = ["" for x in range(100)] + + self.fTextEdit = QTextEdit(self) + self.fButton = QPushButton(self) + self.fProgressBar = ParamSpinBox(self) + self.fSpacer = QLabel(self) + self.fGridLayout = QGridLayout(self) + + self.fButton.setCheckable(True) + self.fButton.setChecked(True) + self.fButton.setText("Edit") + self.fButton.setFixedSize(self.fButton.minimumSizeHint()) + + self.fProgressBar.setMinimum(1.0) + self.fProgressBar.setMaximum(100.0) + self.fProgressBar.setValue(1.0) + self.fProgressBar.setStep(1.0) + self.fProgressBar.setStepSmall(1.0) + self.fProgressBar.setStepLarge(10.0) + + self.fSpacer.setText("") + self.fSpacer.setFixedSize(5, 5) + + self.fTextEdit.setReadOnly(False) + + self.setLayout(self.fGridLayout) + self.fGridLayout.addWidget(self.fTextEdit, 0, 0, 1, 3) + self.fGridLayout.addWidget(self.fButton, 1, 0, 1, 1) + self.fGridLayout.addWidget(self.fProgressBar, 1, 1, 1, 1) + self.fGridLayout.addWidget(self.fSpacer, 1, 2, 1, 1) + self.fGridLayout.setContentsMargins(0, 0, 0, 0) + + self.resize(300, 200) + self.setWindowTitle(self.fUiName) + + self.fButton.clicked.connect(self.slot_buttonClicked) + self.fProgressBar.valueChanged.connect(self.slot_progressBarValueChanged) + self.fTextEdit.textChanged.connect(self.slot_textChanged) + + self.fIdleTimer = self.startTimer(50) + + def saveCurrentTextState(self): + pageKey = "pageText %i" % self.fCurPage + pageValue = self.fTextEdit.toPlainText() + + if pageValue != self.fNotes[self.fCurPage-1]: + self.fNotes[self.fCurPage-1] = pageValue + self.d_setState(pageKey, pageValue) + + # ------------------------------------------------------------------- + + @pyqtSlot(bool) + def slot_buttonClicked(self, click): + readOnly = not click + self.fTextEdit.setReadOnly(readOnly) + self.d_setState("readOnly", "yes" if readOnly else "no") + + @pyqtSlot(float) + def slot_progressBarValueChanged(self, value): + value = rint(value) + + if self.fCurPage == int(value): + return + + # maybe save current text before changing page + if self.fSaveTextNowChecker >= 0 and value >= 1.0 and value <= 100.0: + self.saveCurrentTextState() + self.fSaveTextNowChecker = -1 + + # change current page + self.d_parameterChanged(0, value) + + # tell host about this change + self.d_setParameterValue(0, value) + + @pyqtSlot() + def slot_textChanged(self): + self.fSaveTextNowChecker = 0 + + # ------------------------------------------------------------------- + # DSP Callbacks + + def d_parameterChanged(self, index, value): + if index != 0: + return + + nextCurPage = int(value) + + if nextCurPage != self.fCurPage and nextCurPage >= 1 and nextCurPage <= 100: + self.saveCurrentTextState() + self.fCurPage = nextCurPage + + self.fTextEdit.setPlainText(self.fNotes[self.fCurPage-1]) + self.fProgressBar.setValue(self.fCurPage) + self.fProgressBar.update() + + def d_stateChanged(self, key, value): + if key == "guiWidth": + try: + width = int(value) + except: + width = 0 + + if width > 0: + self.resize(width, self.height()) + + elif key == "guiHeight": + try: + height = int(value) + except: + height = 0 + + if height > 0: + self.resize(self.width(), height) + + elif key.startswith("pageText #"): + try: + pageIndex = int(key.replace("pageText #","")) + except: + pageIndex = 0 + + if pageIndex >= 1 and pageIndex <= 100: + self.fNotes[pageIndex-1] = value + + if pageIndex == self.fCurPage: + self.fTextEdit.setPlainText(self.fNotes[pageIndex-1]) + + elif key == "readOnly": + readOnly = (value == "yes") + self.fButton.setChecked(not readOnly) + self.fTextEdit.setReadOnly(readOnly) + + # ------------------------------------------------------------------- + # ExternalUI Callbacks + + def d_uiShow(self): + self.show() + + def d_uiHide(self): + self.hide() + + def d_uiQuit(self): + self.close() + app.quit() + + def d_uiTitleChanged(self, uiTitle): + self.setWindowTitle(uiTitle) + + # ------------------------------------------------------------------- + # Qt events + + def resizeEvent(self, event): + self.fSaveSizeNowChecker = 0 + QWidget.resizeEvent(self, event) + + def timerEvent(self, event): + if event.timerId() == self.fIdleTimer: + if self.fSaveSizeNowChecker == 11: + self.d_setState("guiWidth", str(self.width())) + self.d_setState("guiHeight", str(self.height())) + self.fSaveSizeNowChecker = -1 + elif self.fSaveSizeNowChecker >= 0: + self.fSaveSizeNowChecker += 1 + + if self.fSaveTextNowChecker == 11: + self.saveCurrentTextState() + self.fSaveTextNowChecker = -1 + elif self.fSaveTextNowChecker >= 0: + self.fSaveTextNowChecker += 1 + + if not self.idleExternalUI(): + self.d_uiQuit() + + QWidget.timerEvent(self, event) + + def closeEvent(self, event): + self.closeExternalUI() + QWidget.closeEvent(self, event) + +#--------------- main ------------------ +if __name__ == '__main__': + app = QApplication(argv) + #app... + gui = DistrhoUINotes() + exit(app.exec_()) diff --git a/source/modules/carla_native/resources/paramspinbox.py b/source/modules/carla_native/resources/paramspinbox.py new file mode 120000 index 000000000..6476a49f3 --- /dev/null +++ b/source/modules/carla_native/resources/paramspinbox.py @@ -0,0 +1 @@ +/home/falktx/Personal/FOSS/GIT/Carla/source/widgets/paramspinbox.py \ No newline at end of file diff --git a/source/modules/carla_native/resources/pluginpipe.py b/source/modules/carla_native/resources/pluginpipe.py new file mode 100644 index 000000000..8d1c8b69c --- /dev/null +++ b/source/modules/carla_native/resources/pluginpipe.py @@ -0,0 +1 @@ + diff --git a/source/backend/resources/zynaddsubfx/black_key.png b/source/modules/carla_native/resources/zynaddsubfx/black_key.png similarity index 100% rename from source/backend/resources/zynaddsubfx/black_key.png rename to source/modules/carla_native/resources/zynaddsubfx/black_key.png diff --git a/source/backend/resources/zynaddsubfx/black_key_pressed.png b/source/modules/carla_native/resources/zynaddsubfx/black_key_pressed.png similarity index 100% rename from source/backend/resources/zynaddsubfx/black_key_pressed.png rename to source/modules/carla_native/resources/zynaddsubfx/black_key_pressed.png diff --git a/source/backend/resources/zynaddsubfx/knob.png b/source/modules/carla_native/resources/zynaddsubfx/knob.png similarity index 100% rename from source/backend/resources/zynaddsubfx/knob.png rename to source/modules/carla_native/resources/zynaddsubfx/knob.png diff --git a/source/backend/resources/zynaddsubfx/module_backdrop.png b/source/modules/carla_native/resources/zynaddsubfx/module_backdrop.png similarity index 100% rename from source/backend/resources/zynaddsubfx/module_backdrop.png rename to source/modules/carla_native/resources/zynaddsubfx/module_backdrop.png diff --git a/source/backend/resources/zynaddsubfx/white_key.png b/source/modules/carla_native/resources/zynaddsubfx/white_key.png similarity index 100% rename from source/backend/resources/zynaddsubfx/white_key.png rename to source/modules/carla_native/resources/zynaddsubfx/white_key.png diff --git a/source/backend/resources/zynaddsubfx/white_key_pressed.png b/source/modules/carla_native/resources/zynaddsubfx/white_key_pressed.png similarity index 100% rename from source/backend/resources/zynaddsubfx/white_key_pressed.png rename to source/modules/carla_native/resources/zynaddsubfx/white_key_pressed.png diff --git a/source/backend/resources/zynaddsubfx/window_backdrop.png b/source/modules/carla_native/resources/zynaddsubfx/window_backdrop.png similarity index 100% rename from source/backend/resources/zynaddsubfx/window_backdrop.png rename to source/modules/carla_native/resources/zynaddsubfx/window_backdrop.png diff --git a/source/modules/distrho/src/DistrhoUIInternal.hpp b/source/modules/distrho/src/DistrhoUIInternal.hpp index d5c7c6994..b56a52a1a 100644 --- a/source/modules/distrho/src/DistrhoUIInternal.hpp +++ b/source/modules/distrho/src/DistrhoUIInternal.hpp @@ -17,7 +17,7 @@ #ifndef DISTRHO_UI_INTERNAL_HPP_INCLUDED #define DISTRHO_UI_INTERNAL_HPP_INCLUDED -# include "../DistrhoUI.hpp" +#include "../DistrhoUI.hpp" #if defined(DISTRHO_UI_EXTERNAL) # include "../DistrhoUIExternal.hpp" @@ -25,8 +25,10 @@ # include "../DistrhoUIOpenGL.hpp" # include "../dgl/App.hpp" # include "../dgl/Window.hpp" -#else +#elif defined(DISTRHO_UI_QT) # include "../DistrhoUIQt.hpp" +#else +# error Invalid UI type #endif #include @@ -129,19 +131,20 @@ public: if (fUi == nullptr) return; -#ifdef DISTRHO_UI_QT - assert(winId == 0); - - if (winId != 0) - return; -#endif - fData->ptr = ptr; fData->editParamCallbackFunc = editParamCall; fData->setParamCallbackFunc = setParamCall; fData->setStateCallbackFunc = setStateCall; fData->sendNoteCallbackFunc = sendNoteCall; fData->uiResizeCallbackFunc = uiResizeCall; + +#ifndef DISTRHO_UI_OPENGL + assert(winId == 0); + return; + + // unused + (void)winId; +#endif } ~UIInternal() @@ -225,7 +228,10 @@ public: } #if defined(DISTRHO_UI_EXTERNAL) - // not needed + const char* getExternalFilename() const + { + return ((ExternalUI*)fUi)->d_getExternalFilename(); + } #elif defined(DISTRHO_UI_OPENGL) DGL::App& getApp() { @@ -237,21 +243,21 @@ public: return glWindow; } - intptr_t getWindowId() const + /*intptr_t getWindowId() const { return glWindow.getWindowId(); - } + }*/ - void fixWindowSize() + /*void fixWindowSize() { assert(fUi != nullptr); glWindow.setSize(fUi->d_getWidth(), fUi->d_getHeight()); - } + }*/ #elif defined(DISTRHO_UI_QT) - QtUI* getQtUI() const + /*QtUI* getQtUI() const { return (QtUI*)fUi; - } + }*/ bool isResizable() const { diff --git a/source/plugin/Makefile b/source/plugin/Makefile index 84b80a4c8..e861ac240 100644 --- a/source/plugin/Makefile +++ b/source/plugin/Makefile @@ -131,7 +131,7 @@ carla-native-plugin.cpp.o: carla-native-plugin.cpp carla-native-base.cpp ../modu carla-native-export: carla-native-export.cpp.o $(LIBS) $(CXX) $^ $(LINK_FLAGS) -o $@ - ./carla-native-export +# ./carla-native-export carla-native-export.exe: carla-native-export.cpp.o $(LIBS) $(CXX) $^ $(LINK_FLAGS) -o $@ diff --git a/source/plugin/carla-native-base.cpp b/source/plugin/carla-native-base.cpp index 62de5f1cc..988694d5d 100644 --- a/source/plugin/carla-native-base.cpp +++ b/source/plugin/carla-native-base.cpp @@ -56,8 +56,8 @@ void carla_register_native_plugin_PingPongPan(); // void carla_register_native_plugin_StereoEnhancer(); #endif -// DISTRHO plugins (Qt) -// void carla_register_native_plugin_Notes(); +// DISTRHO plugins (PyQt) +void carla_register_native_plugin_Notes(); #ifdef WANT_ZYNADDSUBFX // ZynAddSubFX @@ -102,8 +102,8 @@ struct PluginListManager { //carla_register_native_plugin_StereoEnhancer(); // unfinished #endif - // DISTRHO plugins (Qt) - //carla_register_native_plugin_Notes(); // unfinished + // DISTRHO plugins (PyQt) + carla_register_native_plugin_Notes(); // unfinished #ifdef WANT_ZYNADDSUBFX // ZynAddSubFX diff --git a/source/plugin/carla-native-export.cpp b/source/plugin/carla-native-export.cpp index beb5294ca..e1a88f822 100644 --- a/source/plugin/carla-native-export.cpp +++ b/source/plugin/carla-native-export.cpp @@ -164,12 +164,6 @@ void writeManifestFile() text += " ui:binary ;\n"; text += " lv2:extensionData <" LV2_PROGRAMS__UIInterface "> ;\n"; text += " lv2:requiredFeature <" LV2_INSTANCE_ACCESS_URI "> .\n"; - text += "\n"; - text += "\n"; - text += " a <" LV2_EXTERNAL_UI_DEPRECATED_URI "> ;\n"; - text += " ui:binary ;\n"; - text += " lv2:extensionData <" LV2_PROGRAMS__UIInterface "> ;\n"; - text += " lv2:requiredFeature <" LV2_INSTANCE_ACCESS_URI "> .\n"; // ------------------------------------------------------------------- // Write file now @@ -291,8 +285,7 @@ void writePluginFile(const PluginDescriptor* const pluginDesc) if (pluginDesc->hints & PLUGIN_HAS_GUI) { - text += " ui:ui ,\n"; - text += " ;\n"; + text += " ui:ui ;\n"; text += "\n"; } diff --git a/source/plugin/carla-native-plugin.cpp b/source/plugin/carla-native-plugin.cpp index 8c438afec..348c6ab64 100644 --- a/source/plugin/carla-native-plugin.cpp +++ b/source/plugin/carla-native-plugin.cpp @@ -1321,23 +1321,7 @@ const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) /* extension_data */ lv2ui_extension_data }; - static const LV2UI_Descriptor lv2UiDescOld = { - /* URI */ "http://kxstudio.sf.net/carla#UIold", - /* instantiate */ lv2ui_instantiate, - /* cleanup */ lv2ui_cleanup, - /* port_event */ lv2ui_port_event, - /* extension_data */ lv2ui_extension_data - }; - - switch (index) - { - case 0: - return &lv2UiDesc; - case 1: - return &lv2UiDescOld; - default: - return nullptr; - } + return (index == 0) ? &lv2UiDesc : nullptr; } // ----------------------------------------------------------------------- diff --git a/source/plugin/carla-native.lv2/resources b/source/plugin/carla-native.lv2/resources index 0a4fbdbe9..3ee81d7f8 120000 --- a/source/plugin/carla-native.lv2/resources +++ b/source/plugin/carla-native.lv2/resources @@ -1 +1 @@ -../../backend/resources/ \ No newline at end of file +../../modules/carla_native/resources \ No newline at end of file diff --git a/source/widgets/paramspinbox.py b/source/widgets/paramspinbox.py index 85854f873..8fa0302dd 100644 --- a/source/widgets/paramspinbox.py +++ b/source/widgets/paramspinbox.py @@ -93,6 +93,7 @@ class ParamProgressBar(QProgressBar): QProgressBar.__init__(self, parent) self.fLeftClickDown = False + self.fIsInteger = False self.fMinimum = 0.0 self.fMaximum = 1.0 @@ -163,6 +164,8 @@ class ParamProgressBar(QProgressBar): def paintEvent(self, event): if self.fTextCall is not None: self.setFormat("%s %s %s" % (self.fPreLabel, self.fTextCall(), self.fLabel)) + elif self.fIsInteger: + self.setFormat("%s %i %s" % (self.fPreLabel, int(self.fRealValue), self.fLabel)) else: self.setFormat("%s %f %s" % (self.fPreLabel, self.fRealValue, self.fLabel)) @@ -245,6 +248,8 @@ class ParamSpinBox(QAbstractSpinBox): if self.fStepLarge < value: self.fStepLarge = value + self.fBar.fIsInteger = bool(self.fStepSmall == 1.0) + def setStepSmall(self, value): if value == 0.0: self.fStepSmall = 0.0001 @@ -253,6 +258,8 @@ class ParamSpinBox(QAbstractSpinBox): else: self.fStepSmall = value + self.fBar.fIsInteger = bool(self.fStepSmall == 1.0) + def setStepLarge(self, value): if value == 0.0: self.fStepLarge = 0.1