DPF

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

commit 7f486d3836504874e1561dc4d46188b6ee637034
parent 34a62b6e284ce1009fbf2b27d366657a442ffcf7
Author: falkTX <falktx@falktx.com>
Date:   Fri, 15 Jul 2022 00:04:27 +0100

Share VST2 and VST3 keycode handling and a few other things

Diffstat:
Mdistrho/src/DistrhoPluginInternal.hpp | 4++--
Adistrho/src/DistrhoPluginVST.hpp | 421+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdistrho/src/DistrhoPluginVST2.cpp | 179++++++++++++++-----------------------------------------------------------------
Mdistrho/src/DistrhoPluginVST3.cpp | 20++++++++++----------
Ddistrho/src/DistrhoPluginVST3.hpp | 288-------------------------------------------------------------------------------
Mdistrho/src/DistrhoUIInternal.hpp | 51++++++++++++++++-----------------------------------
Mdistrho/src/DistrhoUIPrivateData.hpp | 2+-
Mdistrho/src/DistrhoUIVST3.cpp | 86++++++++++++++++++++++++++++++++++---------------------------------------------
8 files changed, 518 insertions(+), 533 deletions(-)

diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> + * Copyright (C) 2012-2022 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 @@ -20,7 +20,7 @@ #include "../DistrhoPlugin.hpp" #ifdef DISTRHO_PLUGIN_TARGET_VST3 -# include "DistrhoPluginVST3.hpp" +# include "DistrhoPluginVST.hpp" #endif #include <set> diff --git a/distrho/src/DistrhoPluginVST.hpp b/distrho/src/DistrhoPluginVST.hpp @@ -0,0 +1,421 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2022 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_VST_HPP_INCLUDED +#define DISTRHO_PLUGIN_VST_HPP_INCLUDED + +#include "DistrhoPluginChecks.h" +#include "../DistrhoUtils.hpp" + +#include <algorithm> +#include <cmath> + +#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI +# undef DISTRHO_PLUGIN_HAS_UI +# define DISTRHO_PLUGIN_HAS_UI 0 +#endif + +#if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI +# undef DISTRHO_PLUGIN_HAS_UI +# define DISTRHO_PLUGIN_HAS_UI 0 +#endif + +#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI +# include "Base.hpp" +#endif + +#if DISTRHO_PLUGIN_HAS_UI == 1 && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS == 0 +# define DPF_VST3_USES_SEPARATE_CONTROLLER 1 +#else +# define DPF_VST3_USES_SEPARATE_CONTROLLER 0 +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +#ifdef DISTRHO_PROPER_CPP11_SUPPORT +# include <atomic> +#else +// quick and dirty std::atomic replacement for the things we need +namespace std { + struct atomic_int { + volatile int value; + explicit atomic_int(volatile int v) noexcept : value(v) {} + int operator++() volatile noexcept { return __atomic_add_fetch(&value, 1, __ATOMIC_RELAXED); } + int operator--() volatile noexcept { return __atomic_sub_fetch(&value, 1, __ATOMIC_RELAXED); } + operator int() volatile noexcept { return __atomic_load_n(&value, __ATOMIC_RELAXED); } + }; +}; +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +START_NAMESPACE_DISTRHO + +// -------------------------------------------------------------------------------------------------------------------- + +enum Vst3InternalParameters { +#if DPF_VST3_USES_SEPARATE_CONTROLLER + kVst3InternalParameterBufferSize, + kVst3InternalParameterSampleRate, +#endif +#if DISTRHO_PLUGIN_WANT_LATENCY + kVst3InternalParameterLatency, +#endif +#if DISTRHO_PLUGIN_WANT_PROGRAMS + kVst3InternalParameterProgram, +#endif + kVst3InternalParameterBaseCount, +#if DISTRHO_PLUGIN_WANT_MIDI_INPUT + kVst3InternalParameterMidiCC_start = kVst3InternalParameterBaseCount, + kVst3InternalParameterMidiCC_end = kVst3InternalParameterMidiCC_start + 130*16, + kVst3InternalParameterCount = kVst3InternalParameterMidiCC_end +#else + kVst3InternalParameterCount = kVst3InternalParameterBaseCount +#endif +}; + +#if DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS || DISTRHO_PLUGIN_WANT_MIDI_INPUT +# define DPF_VST3_HAS_INTERNAL_PARAMETERS 1 +#else +# define DPF_VST3_HAS_INTERNAL_PARAMETERS 0 +#endif + +#if DPF_VST3_HAS_INTERNAL_PARAMETERS && DISTRHO_PLUGIN_WANT_MIDI_INPUT && \ + !(DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS) +# define DPF_VST3_PURE_MIDI_INTERNAL_PARAMETERS 1 +#else +# define DPF_VST3_PURE_MIDI_INTERNAL_PARAMETERS 0 +#endif + +// -------------------------------------------------------------------------------------------------------------------- + +static inline +bool strcmp_utf16(const int16_t* const str16, const char* const str8) +{ + size_t i = 0; + for (; str8[i] != '\0'; ++i) + { + const uint8_t char8 = static_cast<uint8_t>(str8[i]); + + // skip non-ascii chars, unsupported + if (char8 >= 0x80) + return false; + + if (str16[i] != char8) + return false; + } + + return str16[i] == str8[i]; +} + +// -------------------------------------------------------------------------------------------------------------------- + +static inline +size_t strlen_utf16(const int16_t* const str) +{ + size_t i = 0; + + while (str[i] != 0) + ++i; + + return i; +} + +// -------------------------------------------------------------------------------------------------------------------- + +static inline +void strncpy(char* const dst, const char* const src, const size_t length) +{ + DISTRHO_SAFE_ASSERT_RETURN(length > 0,); + + if (const size_t len = std::min(std::strlen(src), length-1U)) + { + std::memcpy(dst, src, len); + dst[len] = '\0'; + } + else + { + dst[0] = '\0'; + } +} + +static inline +void strncpy_utf8(char* const dst, const int16_t* const src, const size_t length) +{ + DISTRHO_SAFE_ASSERT_RETURN(length > 0,); + + if (const size_t len = std::min(strlen_utf16(src), length-1U)) + { + for (size_t i=0; i<len; ++i) + { + // skip non-ascii chars, unsupported + if (src[i] >= 0x80) + continue; + + dst[i] = src[i]; + } + dst[len] = 0; + } + else + { + dst[0] = 0; + } +} + +static inline +void strncpy_utf16(int16_t* const dst, const char* const src, const size_t length) +{ + DISTRHO_SAFE_ASSERT_RETURN(length > 0,); + + if (const size_t len = std::min(std::strlen(src), length-1U)) + { + for (size_t i=0; i<len; ++i) + { + // skip non-ascii chars, unsupported + if ((uint8_t)src[i] >= 0x80) + continue; + + dst[i] = src[i]; + } + dst[len] = 0; + } + else + { + dst[0] = 0; + } +} + +// -------------------------------------------------------------------------------------------------------------------- + +template<typename T> +static void snprintf_t(char* const dst, const T value, const char* const format, const size_t size) +{ + DISTRHO_SAFE_ASSERT_RETURN(size > 0,); + std::snprintf(dst, size-1, format, value); + dst[size-1] = '\0'; +} + +template<typename T> +static void snprintf_utf16_t(int16_t* const dst, const T value, const char* const format, const size_t size) +{ + DISTRHO_SAFE_ASSERT_RETURN(size > 0,); + + char* const tmpbuf = (char*)std::malloc(size); + DISTRHO_SAFE_ASSERT_RETURN(tmpbuf != nullptr,); + + std::snprintf(tmpbuf, size-1, format, value); + tmpbuf[size-1] = '\0'; + + strncpy_utf16(dst, tmpbuf, size); + std::free(tmpbuf); +} + +static inline +void snprintf_f32(char* const dst, const float value, const size_t size) +{ + return snprintf_t<float>(dst, value, "%f", size); +} + +static inline +void snprintf_i32(char* const dst, const int32_t value, const size_t size) +{ + return snprintf_t<int32_t>(dst, value, "%d", size); +} + +static inline +void snprintf_u32(char* const dst, const uint32_t value, const size_t size) +{ + return snprintf_t<uint32_t>(dst, value, "%u", size); +} + +static inline +void snprintf_f32_utf16(int16_t* const dst, const float value, const size_t size) +{ + return snprintf_utf16_t<float>(dst, value, "%f", size); +} + +static inline +void snprintf_i32_utf16(int16_t* const dst, const int32_t value, const size_t size) +{ + return snprintf_utf16_t<int32_t>(dst, value, "%d", size); +} + +static inline +void snprintf_u32_utf16(int16_t* const dst, const uint32_t value, const size_t size) +{ + return snprintf_utf16_t<uint32_t>(dst, value, "%u", size); +} + +#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI +// -------------------------------------------------------------------------------------------------------------------- +// translate a vstgui-based key character and code to matching values used by DPF + +static inline +uint translateVstKeyCode(bool& special, const int16_t keychar, const int16_t keycode) noexcept +{ + using namespace DGL_NAMESPACE; + + // special stuff first + special = true; + switch (keycode) + { + case 1: return kKeyBackspace; + // 2 \t (handled below) + // 3 clear + // 4 \r (handled below) + case 6: return kKeyEscape; + // 7 space (handled below) + // 8 next + // 17 select + // 18 print + // 19 \n (handled below) + // 20 snapshot + case 22: return kKeyDelete; + // 23 help + // 57 = (handled below) + // numpad stuff follows + // 24 0 (handled below) + // 25 1 (handled below) + // 26 2 (handled below) + // 27 3 (handled below) + // 28 4 (handled below) + // 29 5 (handled below) + // 30 6 (handled below) + // 31 7 (handled below) + // 32 8 (handled below) + // 33 9 (handled below) + // 34 * (handled below) + // 35 + (handled below) + // 36 separator + // 37 - (handled below) + // 38 . (handled below) + // 39 / (handled below) + // handle rest of special keys + /* these special keys are missing: + - kKeySuper + - kKeyCapsLock + - kKeyPrintScreen + */ + case 40: return kKeyF1; + case 41: return kKeyF2; + case 42: return kKeyF3; + case 43: return kKeyF4; + case 44: return kKeyF5; + case 45: return kKeyF6; + case 46: return kKeyF7; + case 47: return kKeyF8; + case 48: return kKeyF9; + case 49: return kKeyF10; + case 50: return kKeyF11; + case 51: return kKeyF12; + case 11: return kKeyLeft; + case 12: return kKeyUp; + case 13: return kKeyRight; + case 14: return kKeyDown; + case 15: return kKeyPageUp; + case 16: return kKeyPageDown; + case 10: return kKeyHome; + case 9: return kKeyEnd; + case 21: return kKeyInsert; + case 54: return kKeyShift; + case 55: return kKeyControl; + case 56: return kKeyAlt; + case 58: return kKeyMenu; + case 52: return kKeyNumLock; + case 53: return kKeyScrollLock; + case 5: return kKeyPause; + } + + // regular keys next + special = false; + switch (keycode) + { + case 2: return '\t'; + case 4: return '\r'; + case 7: return ' '; + case 19: return '\n'; + case 57: return '='; + case 24: return '0'; + case 25: return '1'; + case 26: return '2'; + case 27: return '3'; + case 28: return '4'; + case 29: return '5'; + case 30: return '6'; + case 31: return '7'; + case 32: return '8'; + case 33: return '9'; + case 34: return '*'; + case 35: return '+'; + case 37: return '-'; + case 38: return '.'; + case 39: return '/'; + } + + // fallback + return keychar; +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// handy way to create a utf16 string from a utf8 one on the current function scope, used for message strings + +struct ScopedUTF16String { + int16_t* str; + ScopedUTF16String(const char* const s) noexcept + : str(nullptr) + { + const size_t len = std::strlen(s); + str = static_cast<int16_t*>(std::malloc(sizeof(int16_t) * (len + 1))); + DISTRHO_SAFE_ASSERT_RETURN(str != nullptr,); + strncpy_utf16(str, s, len + 1); + } + + ~ScopedUTF16String() noexcept + { + std::free(str); + } + + operator const int16_t*() const noexcept + { + return str; + } +}; + +// -------------------------------------------------------------------------------------------------------------------- +// handy way to create a utf8 string from a utf16 one on the current function scope (limited to 128 chars) + +struct ScopedUTF8String { + char str[128]; + + ScopedUTF8String(const int16_t* const s) noexcept + { + strncpy_utf8(str, s, 128); + } + + operator const char*() const noexcept + { + return str; + } +}; + +// -------------------------------------------------------------------------------------------------------------------- + +END_NAMESPACE_DISTRHO + +// -------------------------------------------------------------------------------------------------------------------- + +#endif // DISTRHO_PLUGIN_VST_HPP_INCLUDED diff --git a/distrho/src/DistrhoPluginVST2.cpp b/distrho/src/DistrhoPluginVST2.cpp @@ -15,20 +15,11 @@ */ #include "DistrhoPluginInternal.hpp" +#include "DistrhoPluginVST.hpp" #include "../DistrhoPluginUtils.hpp" #include "../extra/ScopedSafeLocale.hpp" #include "../extra/ScopedPointer.hpp" -#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI -# undef DISTRHO_PLUGIN_HAS_UI -# define DISTRHO_PLUGIN_HAS_UI 0 -#endif - -#if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI -# undef DISTRHO_PLUGIN_HAS_UI -# define DISTRHO_PLUGIN_HAS_UI 0 -#endif - #if DISTRHO_PLUGIN_HAS_UI # include "DistrhoUIInternal.hpp" # include "../extra/RingBuffer.hpp" @@ -85,37 +76,6 @@ static const requestParameterValueChangeFunc requestParameterValueChangeCallback // ----------------------------------------------------------------------- -void strncpy(char* const dst, const char* const src, const size_t size) -{ - DISTRHO_SAFE_ASSERT_RETURN(size > 0,); - - if (const size_t len = std::min(std::strlen(src), size-1U)) - { - std::memcpy(dst, src, len); - dst[len] = '\0'; - } - else - { - dst[0] = '\0'; - } -} - -void snprintf_param(char* const dst, const float value, const size_t size) -{ - DISTRHO_SAFE_ASSERT_RETURN(size > 0,); - std::snprintf(dst, size-1, "%f", value); - dst[size-1] = '\0'; -} - -void snprintf_iparam(char* const dst, const int32_t value, const size_t size) -{ - DISTRHO_SAFE_ASSERT_RETURN(size > 0,); - std::snprintf(dst, size-1, "%d", value); - dst[size-1] = '\0'; -} - -// ----------------------------------------------------------------------- - struct ParameterAndNotesHelper { float* parameterValues; @@ -257,85 +217,16 @@ public: # endif # if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - int handlePluginKeyEvent(const bool down, int32_t index, const intptr_t value) + int handlePluginKeyEvent(const bool down, const int32_t index, const intptr_t value) { d_stdout("handlePluginKeyEvent %i %i %li\n", down, index, (long int)value); using namespace DGL_NAMESPACE; - switch (value) - { - // convert some VST2 special values to normal keys - case 1: index = kKeyBackspace; break; - case 2: index = '\t'; break; - // 3 clear - case 4: index = '\r'; break; - case 6: index = kKeyEscape; break; - case 7: index = ' '; break; - // 8 next - // 17 select - // 18 print - case 19: index = '\n'; break; - // 20 snapshot - case 22: index = kKeyDelete; break; - // 23 help - case 57: index = '='; break; - - // numpad stuff follows - case 24: index = '0'; break; - case 25: index = '1'; break; - case 26: index = '2'; break; - case 27: index = '3'; break; - case 28: index = '4'; break; - case 29: index = '5'; break; - case 30: index = '6'; break; - case 31: index = '7'; break; - case 32: index = '8'; break; - case 33: index = '9'; break; - case 34: index = '*'; break; - case 35: index = '+'; break; - // 36 separator - case 37: index = '-'; break; - case 38: index = '.'; break; - case 39: index = '/'; break; - - // handle rest of special keys - /* these special keys are missing: - - kKeySuper - - kKeyCapsLock - - kKeyPrintScreen - */ - case 40: index = kKeyF1; break; - case 41: index = kKeyF2; break; - case 42: index = kKeyF3; break; - case 43: index = kKeyF4; break; - case 44: index = kKeyF5; break; - case 45: index = kKeyF6; break; - case 46: index = kKeyF7; break; - case 47: index = kKeyF8; break; - case 48: index = kKeyF9; break; - case 49: index = kKeyF10; break; - case 50: index = kKeyF11; break; - case 51: index = kKeyF12; break; - case 11: index = kKeyLeft; break; - case 12: index = kKeyUp; break; - case 13: index = kKeyRight; break; - case 14: index = kKeyDown; break; - case 15: index = kKeyPageUp; break; - case 16: index = kKeyPageDown; break; - case 10: index = kKeyHome; break; - case 9: index = kKeyEnd; break; - case 21: index = kKeyInsert; break; - case 54: index = kKeyShift; break; - case 55: index = kKeyControl; break; - case 56: index = kKeyAlt; break; - case 58: index = kKeyMenu; break; - case 52: index = kKeyNumLock; break; - case 53: index = kKeyScrollLock; break; - case 5: index = kKeyPause; break; - } + bool special; + const uint key = translateVstKeyCode(special, index, static_cast<int32_t>(value)); - switch (index) + switch (key) { case kKeyShift: if (down) @@ -357,17 +248,9 @@ public: break; } - if (index > 0) - { - // keyboard events must always be lowercase - if (index >= 'A' && index <= 'Z') - index += 'a' - 'A'; // A-Z -> a-z - - fUI.handlePluginKeyboardVST2(down, static_cast<uint>(index), fKeyboardModifiers); - return 1; - } - - return 0; + return fUI.handlePluginKeyboardVST(down, special, key, + value >= 0 ? static_cast<uint>(value) : 0, + fKeyboardModifiers) ? 1 : 0; } # endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI @@ -490,7 +373,7 @@ public: fAudioMaster(audioMaster), fEffect(effect) { - std::memset(fProgramName, 0, sizeof(char)*(32+1)); + std::memset(fProgramName, 0, sizeof(fProgramName)); std::strcpy(fProgramName, "Default"); const uint32_t parameterCount = fPlugin.getParameterCount(); @@ -575,7 +458,7 @@ public: case effSetProgramName: if (char* const programName = (char*)ptr) { - DISTRHO_NAMESPACE::strncpy(fProgramName, programName, 32); + strncpy(fProgramName, programName, 32); return 1; } break; @@ -583,7 +466,7 @@ public: case effGetProgramName: if (char* const programName = (char*)ptr) { - DISTRHO_NAMESPACE::strncpy(programName, fProgramName, 24); + strncpy(programName, fProgramName, 24); return 1; } break; @@ -591,7 +474,7 @@ public: case effGetProgramNameIndexed: if (char* const programName = (char*)ptr) { - DISTRHO_NAMESPACE::strncpy(programName, fProgramName, 24); + strncpy(programName, fProgramName, 24); return 1; } break; @@ -621,14 +504,14 @@ public: if (d_isNotEqual(value, enumValues.values[i].value)) continue; - DISTRHO_NAMESPACE::strncpy((char*)ptr, enumValues.values[i].label.buffer(), 24); + strncpy((char*)ptr, enumValues.values[i].label.buffer(), 24); return 1; } if (hints & kParameterIsInteger) - DISTRHO_NAMESPACE::snprintf_iparam((char*)ptr, (int32_t)value, 24); + snprintf_i32((char*)ptr, (int32_t)value, 24); else - DISTRHO_NAMESPACE::snprintf_param((char*)ptr, value, 24); + snprintf_f32((char*)ptr, value, 24); return 1; } @@ -1196,7 +1079,7 @@ private: AEffect* const fEffect; // Temporary data - char fProgramName[32+1]; + char fProgramName[32]; #if DISTRHO_PLUGIN_WANT_MIDI_INPUT uint32_t fMidiEventCount; @@ -1477,7 +1360,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t case effGetParamLabel: if (ptr != nullptr && index < static_cast<int32_t>(sPlugin->getParameterCount())) { - DISTRHO_NAMESPACE::strncpy((char*)ptr, sPlugin->getParameterUnit(index), 8); + strncpy((char*)ptr, sPlugin->getParameterUnit(index), 8); return 1; } return 0; @@ -1487,9 +1370,9 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t { const String& shortName(sPlugin->getParameterShortName(index)); if (shortName.isNotEmpty()) - DISTRHO_NAMESPACE::strncpy((char*)ptr, shortName, 16); + strncpy((char*)ptr, shortName, 16); else - DISTRHO_NAMESPACE::strncpy((char*)ptr, sPlugin->getParameterName(index), 16); + strncpy((char*)ptr, sPlugin->getParameterName(index), 16); return 1; } return 0; @@ -1502,17 +1385,17 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t memset(properties, 0, sizeof(VstParameterProperties)); // full name - DISTRHO_NAMESPACE::strncpy(properties->label, - sPlugin->getParameterName(index), - sizeof(properties->label)); + strncpy(properties->label, + sPlugin->getParameterName(index), + sizeof(properties->label)); // short name const String& shortName(sPlugin->getParameterShortName(index)); if (shortName.isNotEmpty()) - DISTRHO_NAMESPACE::strncpy(properties->shortLabel, - sPlugin->getParameterShortName(index), - sizeof(properties->shortLabel)); + strncpy(properties->shortLabel, + sPlugin->getParameterShortName(index), + sizeof(properties->shortLabel)); // parameter hints const uint32_t hints = sPlugin->getParameterHints(index); @@ -1551,9 +1434,9 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t if (portGroup.groupId == groupId) { properties->category = i + 1; - DISTRHO_NAMESPACE::strncpy(properties->categoryLabel, - portGroup.name.buffer(), - sizeof(properties->categoryLabel)); + strncpy(properties->categoryLabel, + portGroup.name.buffer(), + sizeof(properties->categoryLabel)); break; } } @@ -1581,7 +1464,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t case effGetEffectName: if (char* const cptr = (char*)ptr) { - DISTRHO_NAMESPACE::strncpy(cptr, sPlugin->getName(), 32); + strncpy(cptr, sPlugin->getName(), 32); return 1; } return 0; @@ -1589,7 +1472,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t case effGetVendorString: if (char* const cptr = (char*)ptr) { - DISTRHO_NAMESPACE::strncpy(cptr, sPlugin->getMaker(), 32); + strncpy(cptr, sPlugin->getMaker(), 32); return 1; } return 0; @@ -1597,7 +1480,7 @@ static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t case effGetProductString: if (char* const cptr = (char*)ptr) { - DISTRHO_NAMESPACE::strncpy(cptr, sPlugin->getLabel(), 32); + strncpy(cptr, sPlugin->getLabel(), 32); return 1; } return 0; diff --git a/distrho/src/DistrhoPluginVST3.cpp b/distrho/src/DistrhoPluginVST3.cpp @@ -1019,22 +1019,22 @@ public: buffer[sizeof(buffer)-1] = '\xff'; v3_result res; - for (int32_t pos = 0, term = 0, read; term == 0; pos += read) + for (int32_t terminated = 0, read; terminated == 0;) { + read = -1; res = v3_cpp_obj(stream)->read(stream, buffer, sizeof(buffer)-1, &read); DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res); + DISTRHO_SAFE_ASSERT_INT_RETURN(read > 0, read, V3_INTERNAL_ERR); if (read == 0) return V3_OK; - DISTRHO_SAFE_ASSERT_INT_RETURN(read > 0, read, V3_INTERNAL_ERR); - for (int32_t i = 0; i < read; ++i) { // found terminator, stop here if (buffer[i] == '\xfe') { - term = 1; + terminated = 1; break; } @@ -1808,9 +1808,9 @@ public: info->flags = V3_PARAM_CAN_AUTOMATE | V3_PARAM_IS_HIDDEN; info->step_count = 127; char ccstr[24]; - snprintf(ccstr, sizeof(ccstr)-1, "MIDI Ch. %d CC %d", static_cast<uint8_t>(index / 130) + 1, index % 130); + snprintf(ccstr, sizeof(ccstr), "MIDI Ch. %d CC %d", static_cast<uint8_t>(index / 130) + 1, index % 130); strncpy_utf16(info->title, ccstr, 128); - snprintf(ccstr, sizeof(ccstr)-1, "Ch.%d CC%d", index / 130 + 1, index % 130); + snprintf(ccstr, sizeof(ccstr), "Ch.%d CC%d", index / 130 + 1, index % 130); strncpy_utf16(info->short_title, ccstr+5, 128); return V3_OK; } @@ -4300,10 +4300,10 @@ static const char* getPluginVersion() const uint32_t versionNum = getPluginInfo().getVersion(); char versionBuf[64]; - snprintf(versionBuf, sizeof(versionBuf)-1, "%d.%d.%d", - (versionNum >> 16) & 0xff, - (versionNum >> 8) & 0xff, - (versionNum >> 0) & 0xff); + std::snprintf(versionBuf, sizeof(versionBuf)-1, "%d.%d.%d", + (versionNum >> 16) & 0xff, + (versionNum >> 8) & 0xff, + (versionNum >> 0) & 0xff); versionBuf[sizeof(versionBuf)-1] = '\0'; version = versionBuf; } diff --git a/distrho/src/DistrhoPluginVST3.hpp b/distrho/src/DistrhoPluginVST3.hpp @@ -1,288 +0,0 @@ -/* - * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 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_VST3_HPP_INCLUDED -#define DISTRHO_PLUGIN_VST3_HPP_INCLUDED - -#include "DistrhoPluginChecks.h" -#include "../DistrhoUtils.hpp" - -#include <algorithm> -#include <cmath> - -#if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI -# undef DISTRHO_PLUGIN_HAS_UI -# define DISTRHO_PLUGIN_HAS_UI 0 -#endif - -#if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI -# undef DISTRHO_PLUGIN_HAS_UI -# define DISTRHO_PLUGIN_HAS_UI 0 -#endif - -#if DISTRHO_PLUGIN_HAS_UI == 1 && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS == 0 -# define DPF_VST3_USES_SEPARATE_CONTROLLER 1 -#else -# define DPF_VST3_USES_SEPARATE_CONTROLLER 0 -#endif - -// -------------------------------------------------------------------------------------------------------------------- - -#ifdef DISTRHO_PROPER_CPP11_SUPPORT -# include <atomic> -#else -// quick and dirty std::atomic replacement for the things we need -namespace std { - struct atomic_int { - volatile int value; - explicit atomic_int(volatile int v) noexcept : value(v) {} - int operator++() volatile noexcept { return __atomic_add_fetch(&value, 1, __ATOMIC_RELAXED); } - int operator--() volatile noexcept { return __atomic_sub_fetch(&value, 1, __ATOMIC_RELAXED); } - operator int() volatile noexcept { return __atomic_load_n(&value, __ATOMIC_RELAXED); } - }; -}; -#endif - -// -------------------------------------------------------------------------------------------------------------------- - -START_NAMESPACE_DISTRHO - -// -------------------------------------------------------------------------------------------------------------------- - -enum Vst3InternalParameters { -#if DPF_VST3_USES_SEPARATE_CONTROLLER - kVst3InternalParameterBufferSize, - kVst3InternalParameterSampleRate, -#endif -#if DISTRHO_PLUGIN_WANT_LATENCY - kVst3InternalParameterLatency, -#endif -#if DISTRHO_PLUGIN_WANT_PROGRAMS - kVst3InternalParameterProgram, -#endif - kVst3InternalParameterBaseCount, -#if DISTRHO_PLUGIN_WANT_MIDI_INPUT - kVst3InternalParameterMidiCC_start = kVst3InternalParameterBaseCount, - kVst3InternalParameterMidiCC_end = kVst3InternalParameterMidiCC_start + 130*16, - kVst3InternalParameterCount = kVst3InternalParameterMidiCC_end -#else - kVst3InternalParameterCount = kVst3InternalParameterBaseCount -#endif -}; - -#if DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS || DISTRHO_PLUGIN_WANT_MIDI_INPUT -# define DPF_VST3_HAS_INTERNAL_PARAMETERS 1 -#else -# define DPF_VST3_HAS_INTERNAL_PARAMETERS 0 -#endif - -#if DPF_VST3_HAS_INTERNAL_PARAMETERS && DISTRHO_PLUGIN_WANT_MIDI_INPUT && \ - !(DPF_VST3_USES_SEPARATE_CONTROLLER || DISTRHO_PLUGIN_WANT_LATENCY || DISTRHO_PLUGIN_WANT_PROGRAMS) -# define DPF_VST3_PURE_MIDI_INTERNAL_PARAMETERS 1 -#else -# define DPF_VST3_PURE_MIDI_INTERNAL_PARAMETERS 0 -#endif - -// -------------------------------------------------------------------------------------------------------------------- - -static inline -bool strcmp_utf16(const int16_t* const str16, const char* const str8) -{ - size_t i = 0; - for (; str8[i] != '\0'; ++i) - { - const uint8_t char8 = static_cast<uint8_t>(str8[i]); - - // skip non-ascii chars, unsupported - if (char8 >= 0x80) - return false; - - if (str16[i] != char8) - return false; - } - - return str16[i] == str8[i]; -} - -// -------------------------------------------------------------------------------------------------------------------- - -static inline -size_t strlen_utf16(const int16_t* const str) -{ - size_t i = 0; - - while (str[i] != 0) - ++i; - - return i; -} - -// -------------------------------------------------------------------------------------------------------------------- - -static inline -void strncpy(char* const dst, const char* const src, const size_t length) -{ - DISTRHO_SAFE_ASSERT_RETURN(length > 0,); - - if (const size_t len = std::min(std::strlen(src), length-1U)) - { - std::memcpy(dst, src, len); - dst[len] = '\0'; - } - else - { - dst[0] = '\0'; - } -} - -static inline -void strncpy_utf8(char* const dst, const int16_t* const src, const size_t length) -{ - DISTRHO_SAFE_ASSERT_RETURN(length > 0,); - - if (const size_t len = std::min(strlen_utf16(src), length-1U)) - { - for (size_t i=0; i<len; ++i) - { - // skip non-ascii chars, unsupported - if (src[i] >= 0x80) - continue; - - dst[i] = src[i]; - } - dst[len] = 0; - } - else - { - dst[0] = 0; - } -} - -static inline -void strncpy_utf16(int16_t* const dst, const char* const src, const size_t length) -{ - DISTRHO_SAFE_ASSERT_RETURN(length > 0,); - - if (const size_t len = std::min(std::strlen(src), length-1U)) - { - for (size_t i=0; i<len; ++i) - { - // skip non-ascii chars, unsupported - if ((uint8_t)src[i] >= 0x80) - continue; - - dst[i] = src[i]; - } - dst[len] = 0; - } - else - { - dst[0] = 0; - } -} - -// -------------------------------------------------------------------------------------------------------------------- - -template<typename T> -static void snprintf_t(char* const dst, const T value, const char* const format, const size_t size) -{ - DISTRHO_SAFE_ASSERT_RETURN(size > 0,); - std::snprintf(dst, size-1, format, value); - dst[size-1] = '\0'; -} - -template<typename T> -static void snprintf_utf16_t(int16_t* const dst, const T value, const char* const format, const size_t size) -{ - DISTRHO_SAFE_ASSERT_RETURN(size > 0,); - - char* const tmpbuf = (char*)std::malloc(size); - DISTRHO_SAFE_ASSERT_RETURN(tmpbuf != nullptr,); - - std::snprintf(tmpbuf, size-1, format, value); - tmpbuf[size-1] = '\0'; - - strncpy_utf16(dst, tmpbuf, size); - std::free(tmpbuf); -} - -static inline -void snprintf_u32(char* const dst, const uint32_t value, const size_t size) -{ - return snprintf_t<uint32_t>(dst, value, "%u", size); -} - -static inline -void snprintf_f32_utf16(int16_t* const dst, const float value, const size_t size) -{ - return snprintf_utf16_t<float>(dst, value, "%f", size); -} - -static inline -void snprintf_i32_utf16(int16_t* const dst, const int value, const size_t size) -{ - return snprintf_utf16_t<int>(dst, value, "%d", size); -} - -// -------------------------------------------------------------------------------------------------------------------- -// handy way to create a utf16 string from a utf8 one on the current function scope, used for message strings - -struct ScopedUTF16String { - int16_t* str; - ScopedUTF16String(const char* const s) noexcept - : str(nullptr) - { - const size_t len = std::strlen(s); - str = static_cast<int16_t*>(std::malloc(sizeof(int16_t) * (len + 1))); - DISTRHO_SAFE_ASSERT_RETURN(str != nullptr,); - strncpy_utf16(str, s, len + 1); - } - - ~ScopedUTF16String() noexcept - { - std::free(str); - } - - operator const int16_t*() const noexcept - { - return str; - } -}; - -// -------------------------------------------------------------------------------------------------------------------- -// handy way to create a utf8 string from a utf16 one on the current function scope (limited to 128 chars) - -struct ScopedUTF8String { - char str[128]; - - ScopedUTF8String(const int16_t* const s) noexcept - { - strncpy_utf8(str, s, 128); - } - - operator const char*() const noexcept - { - return str; - } -}; - -// -------------------------------------------------------------------------------------------------------------------- - -END_NAMESPACE_DISTRHO - -// -------------------------------------------------------------------------------------------------------------------- - -#endif // DISTRHO_PLUGIN_VST3_HPP_INCLUDED diff --git a/distrho/src/DistrhoUIInternal.hpp b/distrho/src/DistrhoUIInternal.hpp @@ -329,31 +329,7 @@ public: } #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI - bool handlePluginKeyboardVST2(const bool press, const uint key, const uint16_t mods) - { - DGL_NAMESPACE::Widget::KeyboardEvent ev; - ev.mod = mods; - ev.press = press; - ev.key = key; - - const bool ret = ui->onKeyboard(ev); - - if (! press) - return ret; - - DGL_NAMESPACE::Widget::CharacterInputEvent cev; - cev.mod = mods; - cev.character = key; - - // if shift modifier is on, convert a-z -> A-Z for character input - if (key >= 'a' && key <= 'z' && (mods & DGL_NAMESPACE::kModifierShift) != 0) - cev.character -= 'a' - 'A'; - - ui->onCharacterInput(cev); - return ret; - } - - bool handlePluginKeyboardVST3(const bool press, const uint keychar, const uint keycode, const uint16_t mods) + bool handlePluginKeyboardVST(const bool press, const bool special, const uint keychar, const uint keycode, const uint16_t mods) { DGL_NAMESPACE::Widget::KeyboardEvent ev; ev.mod = mods; @@ -361,21 +337,26 @@ public: ev.key = keychar; ev.keycode = keycode; + // keyboard events must always be lowercase + if (ev.key >= 'A' && ev.key <= 'Z') + ev.key += 'a' - 'A'; // A-Z -> a-z + const bool ret = ui->onKeyboard(ev); - if (! press) - return ret; + if (press && !special && (mods & (kModifierControl|kModifierAlt|kModifierSuper)) == 0x0) + { + DGL_NAMESPACE::Widget::CharacterInputEvent cev; + cev.mod = mods; + cev.character = keychar; + cev.keycode = keycode; - DGL_NAMESPACE::Widget::CharacterInputEvent cev; - cev.mod = mods; - cev.keycode = keycode; - cev.character = keychar; + // if shift modifier is on, convert a-z -> A-Z for character input + if (cev.character >= 'a' && cev.character <= 'z' && (mods & DGL_NAMESPACE::kModifierShift) != 0) + cev.character -= 'a' - 'A'; - // if shift modifier is on, convert a-z -> A-Z for character input - if (keychar >= 'a' && keychar <= 'z' && (mods & DGL_NAMESPACE::kModifierShift) != 0) - cev.character -= 'a' - 'A'; + ui->onCharacterInput(cev); + } - ui->onCharacterInput(cev); return ret; } #endif diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp @@ -20,7 +20,7 @@ #include "../DistrhoUI.hpp" #ifdef DISTRHO_PLUGIN_TARGET_VST3 -# include "DistrhoPluginVST3.hpp" +# include "DistrhoPluginVST.hpp" #endif #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI diff --git a/distrho/src/DistrhoUIVST3.cpp b/distrho/src/DistrhoUIVST3.cpp @@ -32,7 +32,6 @@ /* TODO items: * - mousewheel event - * - key down/up events * - file request? */ @@ -106,6 +105,31 @@ static void applyGeometryConstraints(const uint minimumWidth, // -------------------------------------------------------------------------------------------------------------------- +#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI +static uint translateVST3Modifiers(const int64_t modifiers) noexcept +{ + uint dglmods = 0; + if (modifiers & (1 << 0)) + dglmods |= kModifierShift; + if (modifiers & (1 << 1)) + dglmods |= kModifierAlt; + #ifdef DISTRHO_OS_MAC + if (modifiers & (1 << 2)) + dglmods |= kModifierSuper; + if (modifiers & (1 << 3)) + dglmods |= kModifierControl; + #else + if (modifiers & (1 << 2)) + dglmods |= kModifierControl; + if (modifiers & (1 << 3)) + dglmods |= kModifierSuper; + #endif + return dglmods; +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- + /** * Helper class for getting a native idle timer, either through pugl or via native APIs. */ @@ -330,64 +354,28 @@ public: v3_result onKeyDown(const int16_t keychar, const int16_t keycode, const int16_t modifiers) { - d_stdout("onKeyDown %i %i %x\n", keychar, keycode, modifiers); DISTRHO_SAFE_ASSERT_INT_RETURN(keychar >= 0 && keychar < 0x7f, keychar, V3_FALSE); - using namespace DGL_NAMESPACE; + bool special; + const uint key = translateVstKeyCode(special, keychar, keycode); + d_debug("onKeyDown %d %d %x -> %d %d", keychar, keycode, modifiers, special, key); - // TODO - uint dglcode = 0; - - // TODO verify these - uint dglmods = 0; - if (modifiers & (1 << 0)) - dglmods |= kModifierShift; - if (modifiers & (1 << 1)) - dglmods |= kModifierAlt; - #ifdef DISTRHO_OS_MAC - if (modifiers & (1 << 2)) - dglmods |= kModifierSuper; - if (modifiers & (1 << 3)) - dglmods |= kModifierControl; - #else - if (modifiers & (1 << 2)) - dglmods |= kModifierControl; - if (modifiers & (1 << 3)) - dglmods |= kModifierSuper; - #endif - - return fUI.handlePluginKeyboardVST3(true, static_cast<uint>(keychar), dglcode, dglmods) ? V3_TRUE : V3_FALSE; + return fUI.handlePluginKeyboardVST(true, special, key, + keycode >= 0 ? static_cast<uint>(keycode) : 0, + translateVST3Modifiers(modifiers)) ? V3_TRUE : V3_FALSE; } v3_result onKeyUp(const int16_t keychar, const int16_t keycode, const int16_t modifiers) { - d_stdout("onKeyDown %i %i %x\n", keychar, keycode, modifiers); DISTRHO_SAFE_ASSERT_INT_RETURN(keychar >= 0 && keychar < 0x7f, keychar, V3_FALSE); - using namespace DGL_NAMESPACE; - - // TODO - uint dglcode = 0; - - // TODO verify these - uint dglmods = 0; - if (modifiers & (1 << 0)) - dglmods |= kModifierShift; - if (modifiers & (1 << 1)) - dglmods |= kModifierAlt; - #ifdef DISTRHO_OS_MAC - if (modifiers & (1 << 2)) - dglmods |= kModifierSuper; - if (modifiers & (1 << 3)) - dglmods |= kModifierControl; - #else - if (modifiers & (1 << 2)) - dglmods |= kModifierControl; - if (modifiers & (1 << 3)) - dglmods |= kModifierSuper; - #endif + bool special; + const uint key = translateVstKeyCode(special, keychar, keycode); + d_debug("onKeyUp %d %d %x -> %d %d", keychar, keycode, modifiers, special, key); - return fUI.handlePluginKeyboardVST3(false, static_cast<uint>(keychar), dglcode, dglmods) ? V3_TRUE : V3_FALSE; + return fUI.handlePluginKeyboardVST(false, special, key, + keycode >= 0 ? static_cast<uint>(keycode) : 0, + translateVST3Modifiers(modifiers)) ? V3_TRUE : V3_FALSE; } v3_result onFocus(const bool state)