diff options
author | falkTX <falktx@gmail.com> | 2018-12-30 17:51:24 +0100 |
---|---|---|
committer | falkTX <falktx@gmail.com> | 2018-12-30 17:51:24 +0100 |
commit | 209c6c2be6bb45cb34e2f8ef833bc55a4be25a92 (patch) | |
tree | d60dfbb0eca74995dac6dc6e3b6751a69c3be1eb /examples | |
parent | cf980eb2a07555dc8ef9637a57932c8131a8310d (diff) |
Adjust things for better external ui support, add example plugin
Signed-off-by: falkTX <falktx@gmail.com>
Diffstat (limited to 'examples')
-rw-r--r-- | examples/ExternalUI/DistrhoPluginInfo.h | 36 | ||||
-rw-r--r-- | examples/ExternalUI/ExternalExamplePlugin.cpp | 188 | ||||
-rw-r--r-- | examples/ExternalUI/ExternalExampleUI.cpp | 187 | ||||
-rwxr-xr-x | examples/ExternalUI/ExternalLauncher.sh | 40 | ||||
-rw-r--r-- | examples/ExternalUI/Makefile | 40 | ||||
-rw-r--r-- | examples/ExternalUI/README.md | 11 |
6 files changed, 502 insertions, 0 deletions
diff --git a/examples/ExternalUI/DistrhoPluginInfo.h b/examples/ExternalUI/DistrhoPluginInfo.h new file mode 100644 index 00000000..3163c2c3 --- /dev/null +++ b/examples/ExternalUI/DistrhoPluginInfo.h @@ -0,0 +1,36 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> + * + * 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. + */ + +#ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED +#define DISTRHO_PLUGIN_INFO_H_INCLUDED + +#define DISTRHO_PLUGIN_BRAND "DISTRHO" +#define DISTRHO_PLUGIN_NAME "ExternalUI" +#define DISTRHO_PLUGIN_URI "http://distrho.sf.net/examples/ExternalUI" + +#define DISTRHO_PLUGIN_HAS_UI 1 +#define DISTRHO_PLUGIN_HAS_EMBED_UI 0 +#define DISTRHO_PLUGIN_HAS_EXTERNAL_UI 1 +#define DISTRHO_PLUGIN_IS_RT_SAFE 1 +#define DISTRHO_PLUGIN_NUM_INPUTS 1 +#define DISTRHO_PLUGIN_NUM_OUTPUTS 1 + +enum Parameters { + kParameterLevel = 0, + kParameterCount +}; + +#endif // DISTRHO_PLUGIN_INFO_H_INCLUDED diff --git a/examples/ExternalUI/ExternalExamplePlugin.cpp b/examples/ExternalUI/ExternalExamplePlugin.cpp new file mode 100644 index 00000000..dbecdcc6 --- /dev/null +++ b/examples/ExternalUI/ExternalExamplePlugin.cpp @@ -0,0 +1,188 @@ +/*
+ * DISTRHO Plugin Framework (DPF)
+ * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com>
+ *
+ * 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.
+ */
+
+#include "DistrhoPlugin.hpp"
+
+START_NAMESPACE_DISTRHO
+
+// -----------------------------------------------------------------------------------------------------------
+
+/**
+ Plugin to show how to get some basic information sent to the UI.
+ */
+class ExternalExamplePlugin : public Plugin
+{
+public:
+ ExternalExamplePlugin()
+ : Plugin(kParameterCount, 0, 0),
+ fValue(0.0f)
+ {
+ }
+
+protected:
+ /* --------------------------------------------------------------------------------------------------------
+ * Information */
+
+ /**
+ Get the plugin label.
+ This label is a short restricted name consisting of only _, a-z, A-Z and 0-9 characters.
+ */
+ const char* getLabel() const override
+ {
+ return "ExternalUI";
+ }
+
+ /**
+ Get an extensive comment/description about the plugin.
+ */
+ const char* getDescription() const override
+ {
+ return "Plugin to show how to use an external / remote UI.";
+ }
+
+ /**
+ Get the plugin author/maker.
+ */
+ const char* getMaker() const override
+ {
+ return "DISTRHO";
+ }
+
+ /**
+ Get the plugin homepage.
+ */
+ const char* getHomePage() const override
+ {
+ return "https://github.com/DISTRHO/DPF";
+ }
+
+ /**
+ Get the plugin license name (a single line of text).
+ For commercial plugins this should return some short copyright information.
+ */
+ const char* getLicense() const override
+ {
+ return "ISC";
+ }
+
+ /**
+ Get the plugin version, in hexadecimal.
+ */
+ uint32_t getVersion() const override
+ {
+ return d_version(1, 0, 0);
+ }
+
+ /**
+ Get the plugin unique Id.
+ This value is used by LADSPA, DSSI and VST plugin formats.
+ */
+ int64_t getUniqueId() const override
+ {
+ return d_cconst('d', 'E', 'x', 't');
+ }
+
+ /* --------------------------------------------------------------------------------------------------------
+ * Init */
+
+ /**
+ Initialize the parameter @a index.
+ This function will be called once, shortly after the plugin is created.
+ */
+ void initParameter(uint32_t index, Parameter& parameter) override
+ {
+ if (index != 0)
+ return;
+
+ parameter.hints = kParameterIsAutomable|kParameterIsInteger;
+ parameter.ranges.def = 0.0f;
+ parameter.ranges.min = 0.0f;
+ parameter.ranges.max = 100.0f;
+ parameter.name = "Value";
+ parameter.symbol = "value";
+ }
+
+ /* --------------------------------------------------------------------------------------------------------
+ * Internal data */
+
+ /**
+ Get the current value of a parameter.
+ The host may call this function from any context, including realtime processing.
+ */
+ float getParameterValue(uint32_t index) const override
+ {
+ if (index != 0)
+ return 0.0f;
+
+ return fValue;
+
+ }
+
+ /**
+ Change a parameter value.
+ The host may call this function from any context, including realtime processing.
+ When a parameter is marked as automable, you must ensure no non-realtime operations are performed.
+ @note This function will only be called for parameter inputs.
+ */
+ void setParameterValue(uint32_t index, float value) override
+ {
+ if (index != 0)
+ return;
+
+ fValue = value;
+ }
+
+ /* --------------------------------------------------------------------------------------------------------
+ * Audio/MIDI Processing */
+
+ /**
+ Run/process function for plugins without MIDI input.
+ @note Some parameters might be null if there are no audio inputs or outputs.
+ */
+ void run(const float** inputs, float** outputs, uint32_t frames) override
+ {
+ /**
+ This plugin does nothing, it just demonstrates information usage.
+ So here we directly copy inputs over outputs, leaving the audio untouched.
+ We need to be careful in case the host re-uses the same buffer for both ins and outs.
+ */
+ if (outputs[0] != inputs[0])
+ std::memcpy(outputs[0], inputs[0], sizeof(float)*frames);
+ }
+
+ // -------------------------------------------------------------------------------------------------------
+
+private:
+ // Parameters
+ float fValue;
+
+ /**
+ Set our plugin class as non-copyable and add a leak detector just in case.
+ */
+ DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ExternalExamplePlugin)
+};
+
+/* ------------------------------------------------------------------------------------------------------------
+ * Plugin entry point, called by DPF to create a new plugin instance. */
+
+Plugin* createPlugin()
+{
+ return new ExternalExamplePlugin();
+}
+
+// -----------------------------------------------------------------------------------------------------------
+
+END_NAMESPACE_DISTRHO
diff --git a/examples/ExternalUI/ExternalExampleUI.cpp b/examples/ExternalUI/ExternalExampleUI.cpp new file mode 100644 index 00000000..66cfc85b --- /dev/null +++ b/examples/ExternalUI/ExternalExampleUI.cpp @@ -0,0 +1,187 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> + * + * 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. + */ + +#include "DistrhoUI.hpp" + +// Extra includes for current path and fifo stuff +#include <dlfcn.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +START_NAMESPACE_DISTRHO + +// TODO: generate a random, not-yet-existing, filename +const char* const kFifoFilename = "/tmp/dpf-fifo-test"; + +// Helper to get current path of this plugin +static const char* getCurrentPluginFilename() +{ + Dl_info exeInfo; + void* localSymbol = (void*)kFifoFilename; + dladdr(localSymbol, &exeInfo); + return exeInfo.dli_fname; +} + +// Helper to check if a file exists +static bool fileExists(const char* const filename) +{ + return access(filename, F_OK) != -1; +} + +// Helper function to keep trying to write until it succeeds or really errors out +static ssize_t +writeRetry(int fd, const void* src, size_t size) +{ + ssize_t error; + + do { + error = write(fd, src, size); + } while (error == -1 && (errno == EINTR || errno == EPIPE)); + + return error; +} + +// ----------------------------------------------------------------------------------------------------------- + +class ExternalExampleUI : public UI +{ +public: + ExternalExampleUI() + : UI(405, 256, true), + fFifo(-1), + fValue(0.0f), + fExternalScript(getNextBundlePath()) + { + if (fExternalScript.isEmpty()) + { + fExternalScript = getCurrentPluginFilename(); + fExternalScript.truncate(fExternalScript.rfind('/')); + } + + fExternalScript += "/d_extui.sh"; + d_stdout("External script = %s", fExternalScript.buffer()); + } + +protected: + /* -------------------------------------------------------------------------------------------------------- + * DSP/Plugin Callbacks */ + + /** + A parameter has changed on the plugin side. + This is called by the host to inform the UI about parameter changes. + */ + void parameterChanged(uint32_t index, float value) override + { + if (index != 0) + return; + + fValue = value; + + if (fFifo == -1) + return; + + // NOTE: This is a terrible way to pass values, also locale might get in the way... + char valueStr[24]; + std::memset(valueStr, 0, sizeof(valueStr)); + std::snprintf(valueStr, 23, "%i\n", static_cast<int>(value + 0.5f)); + + DISTRHO_SAFE_ASSERT(writeRetry(fFifo, valueStr, 24) == sizeof(valueStr)); + } + + /* -------------------------------------------------------------------------------------------------------- + * External Window overrides */ + + /** + Manage external process and IPC when UI is requested to be visible. + */ + void setVisible(const bool yesNo) override + { + if (yesNo) + { + DISTRHO_SAFE_ASSERT_RETURN(fileExists(fExternalScript),); + + mkfifo(kFifoFilename, 0666); + sync(); + + char winIdStr[24]; + std::memset(winIdStr, 0, sizeof(winIdStr)); + std::snprintf(winIdStr, 23, "%lu", getTransientWinId()); + + const char* args[] = { + fExternalScript.buffer(), + kFifoFilename, + "--progressbar", "External UI example", + "--title", getTitle(), + nullptr, + }; + DISTRHO_SAFE_ASSERT_RETURN(startExternalProcess(args),); + + // NOTE: this can lockup the current thread if the other side does not read the file! + fFifo = open(kFifoFilename, O_WRONLY); + DISTRHO_SAFE_ASSERT_RETURN(fFifo != -1,); + + parameterChanged(0, fValue); + } + else + { + if (fFifo != -1) + { + if (isRunning()) + { + DISTRHO_SAFE_ASSERT(writeRetry(fFifo, "quit\n", 5) == 5); + fsync(fFifo); + } + close(fFifo); + fFifo = -1; + } + + unlink(kFifoFilename); + terminateAndWaitForProcess(); + } + + UI::setVisible(yesNo); + } + + // ------------------------------------------------------------------------------------------------------- + +private: + // IPC Stuff + int fFifo; + + // Current value, cached for when UI becomes visible + float fValue; + + // Path to external ui script + String fExternalScript; + + /** + Set our UI class as non-copyable and add a leak detector just in case. + */ + DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ExternalExampleUI) +}; + +/* ------------------------------------------------------------------------------------------------------------ + * UI entry point, called by DPF to create a new UI instance. */ + +UI* createUI() +{ + return new ExternalExampleUI(); +} + +// ----------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO diff --git a/examples/ExternalUI/ExternalLauncher.sh b/examples/ExternalUI/ExternalLauncher.sh new file mode 100755 index 00000000..1ca7c94c --- /dev/null +++ b/examples/ExternalUI/ExternalLauncher.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# Read FIFO argument from CLI +FIFO=${1} +shift + +if [ ! -e "${FIFO}" ]; then + echo "Fifo file ${FIFO} does not exist, cannot run" + exit 1 +fi + +# Start kdialog with all other arguments and get dbus reference +dbusRef=$(kdialog "$@" 100) + +if [ $? -ne 0 ] || [ -z "${dbusRef}" ]; then + echo "Failed to start kdialog" + exit 1 +fi + +# Setup cancellation point for this script +quitfn() { + qdbus ${dbusRef} close 2>/dev/null +} + +trap quitfn SIGINT +trap quitfn SIGTERM + +# Read Fifo for new values or a quit message +while read line <"${FIFO}"; do + if echo "${line}" | grep -q "quit"; then + break + fi + if ! qdbus ${dbusRef} Set "" value "${line}"; then + break + fi +done + +# Cleanup +rm -f "${FIFO}" +quitfn diff --git a/examples/ExternalUI/Makefile b/examples/ExternalUI/Makefile new file mode 100644 index 00000000..ba1df228 --- /dev/null +++ b/examples/ExternalUI/Makefile @@ -0,0 +1,40 @@ +#!/usr/bin/make -f +# Makefile for DISTRHO Plugins # +# ---------------------------- # +# Created by falkTX +# + +# -------------------------------------------------------------- +# Project name, used for binaries + +NAME = d_extui + +# -------------------------------------------------------------- +# Files to build + +FILES_DSP = \ + ExternalExamplePlugin.cpp + +FILES_UI = \ + ExternalExampleUI.cpp + +# -------------------------------------------------------------- +# Do some magic + +include ../../Makefile.plugins.mk + +LINK_FLAGS += -ldl + +# -------------------------------------------------------------- +# Enable all possible plugin types + +ifeq ($(HAVE_JACK),true) +TARGETS += jack +endif + +TARGETS += dssi +TARGETS += lv2_sep + +all: $(TARGETS) + +# -------------------------------------------------------------- diff --git a/examples/ExternalUI/README.md b/examples/ExternalUI/README.md new file mode 100644 index 00000000..6fb4d3ea --- /dev/null +++ b/examples/ExternalUI/README.md @@ -0,0 +1,11 @@ +# External UI example + +This example will show how to use an external / remote UI together with DPF.<br/> + +The Plugin has a shell script that calls kdialog and uses qdbus for sending values to it.<br/> +It is a very ugly way to show a remote UI (using a shell script!), but it is only to prove the point.<br/> + +Note that everything regarding external UIs is still a bit experimental in DPF.<br/> +There is Unix-specific code in there.<br/> + +If this is something you are interested on using and contributing to, please let us know.<br/> |