DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

commit 028060954a99720de194a43d8d03b6d694c66850
parent ac7c0ee3fa7e926f5413302c0d9a6f116c9b4fdf
Author: falkTX <falktx@falktx.com>
Date:   Fri, 19 Aug 2022 02:58:05 +0100

Make selftest more generally useful, inform plugins of such

Diffstat:
Mdistrho/DistrhoPlugin.hpp | 10++++++++++
Mdistrho/src/DistrhoPlugin.cpp | 38++++++++++++++++++++++----------------
Mdistrho/src/DistrhoPluginInternal.hpp | 3+++
Mdistrho/src/DistrhoPluginJACK.cpp | 118+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
4 files changed, 102 insertions(+), 67 deletions(-)

diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp @@ -951,6 +951,16 @@ public: */ bool isDummyInstance() const noexcept; + /** + Check if this plugin instance is a "selftest" one used for automated plugin tests.@n + To enable this mode build with `DPF_RUNTIME_TESTING` macro defined (i.e. set as compiler build flag), + and run the JACK/Standalone executable with "selftest" as its only and single argument. + + A few basic DSP and UI tests will run in self-test mode, with once instance having this function returning true.@n + You can use this chance to do a few tests of your own as well. + */ + bool isSelfTestInstance() const noexcept; + #if DISTRHO_PLUGIN_WANT_TIMEPOS /** Get the current host transport time position.@n diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp @@ -25,6 +25,7 @@ uint32_t d_nextBufferSize = 0; double d_nextSampleRate = 0.0; const char* d_nextBundlePath = nullptr; bool d_nextPluginIsDummy = false; +bool d_nextPluginIsSelfTest = false; bool d_nextCanRequestParameterValueChanges = false; /* ------------------------------------------------------------------------------------------------------------ @@ -42,45 +43,45 @@ const PortGroupWithId PluginExporter::sFallbackPortGroup; Plugin::Plugin(uint32_t parameterCount, uint32_t programCount, uint32_t stateCount) : pData(new PrivateData()) { -#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 pData->audioPorts = new AudioPortWithBusId[DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS]; -#endif + #endif -#ifdef DPF_ABORT_ON_ERROR -# define DPF_ABORT abort(); -#else -# define DPF_ABORT -#endif + #if defined(DPF_ABORT_ON_ERROR) || defined(DPF_RUNTIME_TESTING) + #define DPF_ABORT abort(); + #else + #define DPF_ABORT + #endif if (parameterCount > 0) { pData->parameterCount = parameterCount; - pData->parameters = new Parameter[parameterCount]; + pData->parameters = new Parameter[parameterCount]; } if (programCount > 0) { -#if DISTRHO_PLUGIN_WANT_PROGRAMS + #if DISTRHO_PLUGIN_WANT_PROGRAMS pData->programCount = programCount; pData->programNames = new String[programCount]; -#else + #else d_stderr2("DPF warning: Plugins with programs must define `DISTRHO_PLUGIN_WANT_PROGRAMS` to 1"); DPF_ABORT -#endif + #endif } if (stateCount > 0) { -#if DISTRHO_PLUGIN_WANT_STATE + #if DISTRHO_PLUGIN_WANT_STATE pData->stateCount = stateCount; - pData->states = new State[stateCount]; -#else + pData->states = new State[stateCount]; + #else d_stderr2("DPF warning: Plugins with state must define `DISTRHO_PLUGIN_WANT_STATE` to 1"); DPF_ABORT -#endif + #endif } -#undef DPF_ABORT + #undef DPF_ABORT } Plugin::~Plugin() @@ -111,6 +112,11 @@ bool Plugin::isDummyInstance() const noexcept return pData->isDummy; } +bool Plugin::isSelfTestInstance() const noexcept +{ + return pData->isSelfTest; +} + #if DISTRHO_PLUGIN_WANT_TIMEPOS const TimePosition& Plugin::getTimePosition() const noexcept { diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp @@ -39,6 +39,7 @@ extern uint32_t d_nextBufferSize; extern double d_nextSampleRate; extern const char* d_nextBundlePath; extern bool d_nextPluginIsDummy; +extern bool d_nextPluginIsSelfTest; extern bool d_nextCanRequestParameterValueChanges; // ----------------------------------------------------------------------- @@ -92,6 +93,7 @@ static void fillInPredefinedPortGroupData(const uint32_t groupId, PortGroup& por struct Plugin::PrivateData { const bool canRequestParameterValueChanges; const bool isDummy; + const bool isSelfTest; bool isProcessing; #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 @@ -136,6 +138,7 @@ struct Plugin::PrivateData { PrivateData() noexcept : canRequestParameterValueChanges(d_nextCanRequestParameterValueChanges), isDummy(d_nextPluginIsDummy), + isSelfTest(d_nextPluginIsSelfTest), isProcessing(false), #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 audioPorts(nullptr), diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp @@ -795,11 +795,11 @@ protected: while (! shouldThreadExit()) { -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT plugin.run(inputs, outputs, 128, nullptr, 0); -#else + #else plugin.run(inputs, outputs, 128); -#endif + #endif d_msleep(100); } @@ -824,7 +824,17 @@ bool runSelfTests() // simple processing { + d_nextPluginIsSelfTest = true; PluginExporter plugin(nullptr, nullptr, nullptr, nullptr); + d_nextPluginIsSelfTest = false; + + #if DISTRHO_PLUGIN_HAS_UI + UIExporter ui(nullptr, 0, plugin.getSampleRate(), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + plugin.getInstancePointer(), 0.0); + ui.showAndFocus(); + #endif + plugin.activate(); plugin.deactivate(); plugin.setBufferSize(128); @@ -839,15 +849,21 @@ bool runSelfTests() for (int i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) outputs[i] = buffer; -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + #if DISTRHO_PLUGIN_WANT_MIDI_INPUT plugin.run(inputs, outputs, 128, nullptr, 0); -#else + #else plugin.run(inputs, outputs, 128); -#endif + #endif plugin.deactivate(); + + #if DISTRHO_PLUGIN_HAS_UI + ui.plugin_idle(); + #endif } + return true; + // multi-threaded processing with UI { PluginExporter pluginA(nullptr, nullptr, nullptr, nullptr); @@ -866,7 +882,7 @@ bool runSelfTests() // stop the 2nd instance now procTestB.stopThread(5000); -#if DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_HAS_UI // start UI in the middle of this { UIExporter uiA(nullptr, 0, pluginA.getSampleRate(), @@ -893,7 +909,7 @@ bool runSelfTests() d_msleep(100); } } -#endif + #endif procTestA.stopThread(5000); procTestC.stopThread(5000); @@ -911,12 +927,47 @@ int main(int argc, char* argv[]) { USE_NAMESPACE_DISTRHO; -#ifdef DPF_RUNTIME_TESTING + initSignalHandler(); + + #if !defined(DISTRHO_OS_WINDOWS) && !defined(STATIC_BUILD) + // find plugin bundle + static String bundlePath; + if (bundlePath.isEmpty()) + { + String tmpPath(getBinaryFilename()); + tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); + #ifdef DISTRHO_OS_MAC + if (tmpPath.endsWith("/MacOS")) + { + tmpPath.truncate(tmpPath.rfind('/')); + if (tmpPath.endsWith("/Contents")) + { + tmpPath.truncate(tmpPath.rfind('/')); + bundlePath = tmpPath; + d_nextBundlePath = bundlePath.buffer(); + } + } + #else + if (access(tmpPath + DISTRHO_OS_SEP_STR "resources", F_OK) == 0) + { + bundlePath = tmpPath; + d_nextBundlePath = bundlePath.buffer(); + } + #endif + } + #endif + if (argc == 2 && std::strcmp(argv[1], "selftest") == 0) + { + #ifdef DPF_RUNTIME_TESTING return runSelfTests() ? 0 : 1; -#endif + #else + d_stderr2("Code was built without DPF_RUNTIME_TESTING macro enabled, selftest option is not available"); + return 1; + #endif + } -#if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI + #if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI /* the code below is based on * https://www.tillett.info/2013/05/13/how-to-create-a-windows-program-that-works-as-both-as-a-gui-and-console-application/ */ @@ -944,7 +995,7 @@ int main(int argc, char* argv[]) hasConsole = true; } -#endif + #endif jack_status_t status = jack_status_t(0x0); jack_client_t* client = jackbridge_client_open(DISTRHO_PLUGIN_NAME, JackNoStartServer, &status); @@ -1023,49 +1074,19 @@ int main(int argc, char* argv[]) return 1; } - initSignalHandler(); - d_nextBufferSize = jackbridge_get_buffer_size(client); d_nextSampleRate = jackbridge_get_sample_rate(client); d_nextCanRequestParameterValueChanges = true; - #if !defined(DISTRHO_OS_WINDOWS) && !defined(STATIC_BUILD) - // find plugin bundle - static String bundlePath; - if (bundlePath.isEmpty()) - { - String tmpPath(getBinaryFilename()); - tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); - #ifdef DISTRHO_OS_MAC - if (tmpPath.endsWith("/MacOS")) - { - tmpPath.truncate(tmpPath.rfind('/')); - if (tmpPath.endsWith("/Contents")) - { - tmpPath.truncate(tmpPath.rfind('/')); - bundlePath = tmpPath; - d_nextBundlePath = bundlePath.buffer(); - } - } - #else - if (access(tmpPath + DISTRHO_OS_SEP_STR "resources", F_OK) == 0) - { - bundlePath = tmpPath; - d_nextBundlePath = bundlePath.buffer(); - } - #endif - } - #endif - uintptr_t winId = 0; -#if DISTRHO_PLUGIN_HAS_UI + #if DISTRHO_PLUGIN_HAS_UI if (argc == 3 && std::strcmp(argv[1], "embed") == 0) winId = static_cast<uintptr_t>(std::atoll(argv[2])); -#endif + #endif const PluginJack p(client, winId); -#if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI + #if defined(DISTRHO_OS_WINDOWS) && DISTRHO_PLUGIN_HAS_UI /* the code below is based on * https://www.tillett.info/2013/05/13/how-to-create-a-windows-program-that-works-as-both-as-a-gui-and-console-application/ */ @@ -1091,14 +1112,9 @@ int main(int argc, char* argv[]) ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release SendInput(1, &ip, sizeof(INPUT)); } -#endif + #endif return 0; - -#ifndef DPF_RUNTIME_TESTING - // unused - (void)argc; (void)argv; -#endif } // -----------------------------------------------------------------------