From 7cd23fd51b657f5a3099b66e0ae95e10f63fbd0f Mon Sep 17 00:00:00 2001 From: defiantnerd <97224712+defiantnerd@users.noreply.github.com> Date: Fri, 26 Apr 2024 22:58:04 +0200 Subject: [PATCH 1/5] Allow child NSView to resize the wrapper NSView in Audio Units. (#249) Co-authored-by: Joseph Lyncheski --- src/detail/auv2/wrappedview.asinclude.mm | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/detail/auv2/wrappedview.asinclude.mm b/src/detail/auv2/wrappedview.asinclude.mm index 33b70df8..ecf9c185 100644 --- a/src/detail/auv2/wrappedview.asinclude.mm +++ b/src/detail/auv2/wrappedview.asinclude.mm @@ -111,7 +111,18 @@ - (id)initWithAUv2:(free_audio::auv2_wrapper::ui_connection *)cont preferredSize gui->set_parent(ui._plugin->_plugin, &m); gui->set_scale(ui._plugin->_plugin, 1.0); - if (gui->can_resize(ui._plugin->_plugin)) gui->set_size(ui._plugin->_plugin, size.width, size.height); + if (gui->can_resize(ui._plugin->_plugin)) + { + clap_gui_resize_hints_t resize_hints; + gui->get_resize_hints(ui._plugin->_plugin, &resize_hints); + NSAutoresizingMaskOptions mask = 0; + + if (resize_hints.can_resize_horizontally) mask |= NSViewWidthSizable; + if (resize_hints.can_resize_vertically) mask |= NSViewHeightSizable; + + [self setAutoresizingMask:mask]; + gui->set_size(ui._plugin->_plugin, size.width, size.height); + } idleTimer = nil; CFTimeInterval TIMER_INTERVAL = .05; // In SurgeGUISynthesizer.h it uses 50 ms From 0baf76b5583016c4df47986fdb57dab655a68c81 Mon Sep 17 00:00:00 2001 From: defiantnerd <97224712+defiantnerd@users.noreply.github.com> Date: Sat, 27 Apr 2024 22:52:40 +0200 Subject: [PATCH 2/5] provide IsRecording flag (AUv2) (#251) * provide IsRecording flag (AUv2) why ever apple decided not to pass the isRecording flag, it is possible to retrieve the host callback structure and get it ourselves. * Update wrapasauv2.cpp of course _isRecording must be initialized --- src/detail/auv2/process.cpp | 1 + src/detail/auv2/process.h | 1 + src/wrapasauv2.cpp | 26 ++++++++++++++++++-------- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/detail/auv2/process.cpp b/src/detail/auv2/process.cpp index 845c1f59..7aac617a 100644 --- a/src/detail/auv2/process.cpp +++ b/src/detail/auv2/process.cpp @@ -180,6 +180,7 @@ void ProcessAdapter::process(ProcessData& data) { // TODO: transportchanged flag? _transport.flags |= data._isPlaying ? CLAP_TRANSPORT_IS_PLAYING : 0; + _transport.flags |= data._isRecording ? CLAP_TRANSPORT_IS_RECORDING : 0; // CLAP_TRANSPORT_IS_RECORDING can not be retrieved from this data block _transport.flags |= data._isLooping ? CLAP_TRANSPORT_IS_LOOP_ACTIVE : 0; // CLAP_TRANSPORT_IS_RECORDING can not be retrieved from the AudioUnit API diff --git a/src/detail/auv2/process.h b/src/detail/auv2/process.h index 41d60773..636761af 100644 --- a/src/detail/auv2/process.h +++ b/src/detail/auv2/process.h @@ -53,6 +53,7 @@ struct ProcessData Boolean _isPlaying; Boolean _transportChanged; Boolean _isLooping; + Boolean _isRecording; // -------------- bool _AUbeatAndTempoValid; // true if: diff --git a/src/wrapasauv2.cpp b/src/wrapasauv2.cpp index 8df41684..9e4e639f 100644 --- a/src/wrapasauv2.cpp +++ b/src/wrapasauv2.cpp @@ -920,19 +920,29 @@ OSStatus WrapAsAUV2::Render(AudioUnitRenderActionFlags& inFlags, const AudioTime // retrieve musical information for this render block - // TODO: clarify how we can get transportStateProc2 - // mHostCallbackInfo.transportStateProc2 -#if 1 - data._AUtransportValid = - (noErr == CallHostTransportState(&data._isPlaying, &data._transportChanged, - &data._currentSongPosInSeconds, &data._isLooping, - &data._cycleStart, &data._cycleEnd)); + auto hcb = GetHostCallbackInfo(); + if (hcb.transportStateProc2) + { + data._AUtransportValid = + (noErr == (hcb.transportStateProc2)(hcb.hostUserData, &data._isPlaying, &data._isRecording, + &data._transportChanged, &data._currentSongPosInSeconds, + &data._isLooping, &data._cycleStart, &data._cycleEnd)); + } + else + { + data._isRecording = FALSE; + + data._AUtransportValid = + (noErr == CallHostTransportState(&data._isPlaying, &data._transportChanged, + &data._currentSongPosInSeconds, &data._isLooping, + &data._cycleStart, &data._cycleEnd)); + } + data._currentSongPosInSeconds /= std::max(_plugin->getSampleRate(), 1.0); // just in case data._AUbeatAndTempoValid = (noErr == CallHostBeatAndTempo(&data._beat, &data._tempo)); data._AUmusicalTimeValid = (noErr == CallHostMusicalTimeLocation(&data._offsetToNextBeat, &data._musicalNumerator, &data._musicalDenominator, &data._currentDownBeat)); -#endif // Get output buffer list and extract the i/o buffer pointers. // The loop is done so that an arbitrary number of output busses // with an arbitrary number of output channels is mapped onto a From fd59b28bbf3a144d2ef757db7656db8d97cf418c Mon Sep 17 00:00:00 2001 From: defiantnerd <97224712+defiantnerd@users.noreply.github.com> Date: Wed, 1 May 2024 11:19:25 +0200 Subject: [PATCH 3/5] Implement offline processing for CLAPs (#250) adapting VST3 IAudioProcessor::setupProcessingand CLAP extension clap.render see https://github.com/free-audio/clap-wrapper/issues/12 --- src/clap_proxy.cpp | 4 ---- src/wrapasvst3.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++++++ src/wrapasvst3.h | 2 ++ 3 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/clap_proxy.cpp b/src/clap_proxy.cpp index 67867ff1..f0729057 100644 --- a/src/clap_proxy.cpp +++ b/src/clap_proxy.cpp @@ -501,10 +501,6 @@ const void* Plugin::clapExtension(const clap_host* /*host*/, const char* extensi { return &HostExt::tail; } - if (!strcmp(extension, CLAP_EXT_RENDER)) - { - // TODO: implement CLAP_EXT_RENDER - } if (!strcmp(extension, CLAP_EXT_STATE)) return &HostExt::state; if (!strcmp(extension, CLAP_EXT_CONTEXT_MENU)) return &HostExt::context_menu; diff --git a/src/wrapasvst3.cpp b/src/wrapasvst3.cpp index 6ce37dd5..43b18f8f 100644 --- a/src/wrapasvst3.cpp +++ b/src/wrapasvst3.cpp @@ -188,6 +188,22 @@ tresult PLUGIN_API ClapAsVst3::setupProcessing(Vst::ProcessSetup& newSetup) { return kResultFalse; } + if (_plugin->_ext._render) + { + if (_plugin->_ext._render->has_hard_realtime_requirement(_plugin->_plugin) && + newSetup.processMode != Vst::kRealtime) + { + return kResultFalse; + } + clap_plugin_render_mode new_render_mode = CLAP_RENDER_REALTIME; + if (newSetup.processMode == Vst::kOffline) + { + new_render_mode = CLAP_RENDER_OFFLINE; + } + // handling Vst::kPrefetch as Vst::kRealTime + + _plugin->_ext._render->set(_plugin->_plugin, new_render_mode); + } _plugin->setSampleRate(newSetup.sampleRate); _plugin->setBlockSizes(newSetup.maxSamplesPerBlock, newSetup.maxSamplesPerBlock); @@ -330,6 +346,39 @@ tresult PLUGIN_API ClapAsVst3::activateBus(Vst::MediaType type, Vst::BusDirectio return super::activateBus(type, dir, index, state); } +tresult PLUGIN_API ClapAsVst3::setIoMode(Vst::IoMode mode) +{ +#if 0 // disabled for now + // since there is always the override in setupProcessing, setting the mode here + // does not make much sense - even for a VST3 + // so for now this stays kUnimplemented until we find a proper use case + + auto rext = _plugin->_ext._render; + + if (rext) + { + auto mainthread = _plugin->AlwaysMainThread(); + + bool realtime_only = rext->has_hard_realtime_requirement(_plugin->_plugin); + switch (mode) + { + case Vst::kOfflineProcessing: + if (realtime_only) return kResultFalse; + return (rext->set(_plugin->_plugin, CLAP_RENDER_OFFLINE)) ? kResultOk : kResultFalse; + break; + case Vst::kSimple: + case Vst::kAdvanced: + // both does not make any difference + return (rext->set(_plugin->_plugin, CLAP_RENDER_REALTIME)) ? kResultOk : kResultFalse; + break; + default: + return kNotImplemented; + } + } +#endif + return super::setIoMode(mode); +} + //----------------------------------------------------------------------------- tresult PLUGIN_API ClapAsVst3::setComponentHandler(Vst::IComponentHandler* handler) diff --git a/src/wrapasvst3.h b/src/wrapasvst3.h index d6438a7a..b9fb6149 100644 --- a/src/wrapasvst3.h +++ b/src/wrapasvst3.h @@ -149,6 +149,8 @@ class ClapAsVst3 : public Steinberg::Vst::SingleComponentEffect, tresult PLUGIN_API activateBus(Vst::MediaType type, Vst::BusDirection dir, int32 index, TBool state) override; + tresult PLUGIN_API setIoMode(Vst::IoMode mode) override; + // from IEditController tresult PLUGIN_API setComponentHandler(Vst::IComponentHandler* handler) override; From f9c82c3d4a1aa0f300ad6df2ffd4f87485f93782 Mon Sep 17 00:00:00 2001 From: defiantnerd <97224712+defiantnerd@users.noreply.github.com> Date: Wed, 8 May 2024 18:22:44 +0200 Subject: [PATCH 4/5] fixing VST3 program changes (#252) * fixing VST3 program changes * pushing version to 0.9.1 --- CMakeLists.txt | 2 +- src/detail/vst3/parameter.cpp | 4 ---- src/detail/vst3/parameter.h | 4 ++++ src/wrapasvst3.h | 19 ++++++++++--------- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a797edc..aedfcd85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ option(CLAP_WRAPPER_WINDOWS_SINGLE_FILE "Build a single fine (rather than folder project(clap-wrapper LANGUAGES C CXX - VERSION 0.9.0 + VERSION 0.9.1 DESCRIPTION "CLAP-as-X wrappers" ) set(CLAP_WRAPPER_VERSION "${CMAKE_PROJECT_VERSION}" CACHE STRING "Version of the wrapper project") diff --git a/src/detail/vst3/parameter.cpp b/src/detail/vst3/parameter.cpp index 4edd5af0..8c4ace7d 100644 --- a/src/detail/vst3/parameter.cpp +++ b/src/detail/vst3/parameter.cpp @@ -45,10 +45,6 @@ Vst3Parameter::~Vst3Parameter() = default; bool Vst3Parameter::setNormalized(Steinberg::Vst::ParamValue v) { - if (isMidi && (info.flags & Steinberg::Vst::ParameterInfo::kIsProgramChange)) - { - return true; - } return super::setNormalized(v); } diff --git a/src/detail/vst3/parameter.h b/src/detail/vst3/parameter.h index 0b764187..2f8e1881 100644 --- a/src/detail/vst3/parameter.h +++ b/src/detail/vst3/parameter.h @@ -52,6 +52,10 @@ class Vst3Parameter : public Steinberg::Vst::Parameter inline double asClapValue(double vst3value) const { + if (info.stepCount > 0) + { + return (vst3value * info.stepCount) + min_value; + } return (vst3value * (max_value - min_value)) + min_value; } inline double asVst3Value(double clapvalue) const diff --git a/src/wrapasvst3.h b/src/wrapasvst3.h index b9fb6149..4ec78248 100644 --- a/src/wrapasvst3.h +++ b/src/wrapasvst3.h @@ -192,15 +192,16 @@ class ClapAsVst3 : public Steinberg::Vst::SingleComponentEffect, tresult PLUGIN_API getUnitByBus(Vst::MediaType /*type*/, Vst::BusDirection /*dir*/, int32 /*busIndex*/, int32 /*channel*/, Vst::UnitID& /*unitId*/ /*out*/) SMTG_OVERRIDE; -#if 0 - // units selection -------------------- - Vst::UnitID PLUGIN_API getSelectedUnit () SMTG_OVERRIDE { return selectedUnit; } - tresult PLUGIN_API selectUnit (Vst::UnitID unitId) SMTG_OVERRIDE - { - selectedUnit = unitId; - return kResultTrue; - } -#endif + // units selection -------------------- + Vst::UnitID PLUGIN_API getSelectedUnit() SMTG_OVERRIDE + { + return selectedUnit; + } + tresult PLUGIN_API selectUnit(Vst::UnitID unitId) SMTG_OVERRIDE + { + selectedUnit = unitId; + return kResultTrue; + } //---IContextMenuTarget ---------------------------------------------------------------- tresult PLUGIN_API executeMenuItem(int32 tag) override; From 0968ee8d48ef85ea51a8ad313329c974fd2eca00 Mon Sep 17 00:00:00 2001 From: defiantnerd <97224712+defiantnerd@users.noreply.github.com> Date: Tue, 24 Sep 2024 05:48:46 +0200 Subject: [PATCH 5/5] VST3: adding correct utf8 to utf16 converter, ditching the VST3 SDK functions --- src/detail/vst3/parameter.cpp | 14 ++++-- src/detail/vst3/parameter.h | 2 + src/wrapasvst3.cpp | 90 ++++++++++++++++++++++++++++++++++- 3 files changed, 100 insertions(+), 6 deletions(-) diff --git a/src/detail/vst3/parameter.cpp b/src/detail/vst3/parameter.cpp index 40c934ff..c5073b5f 100644 --- a/src/detail/vst3/parameter.cpp +++ b/src/detail/vst3/parameter.cpp @@ -92,9 +92,11 @@ Vst3Parameter* Vst3Parameter::create( } } - str8ToStr16(v.title, fullname.c_str(), str16BufferSize(v.title)); + // str8ToStr16(v.title, fullname.c_str(), str16BufferSize(v.title)); + utf8_to_utf16l(fullname.c_str(), (uint16_t*)(v.title), str16BufferSize(v.title)); // TODO: string shrink algorithm shortening the string a bit - str8ToStr16(v.shortTitle, info->name, str16BufferSize(v.shortTitle)); + // str8ToStr16(v.shortTitle, info->name, str16BufferSize(v.shortTitle)); + utf8_to_utf16l(info->name, (uint16_t*)v.shortTitle, str16BufferSize(v.shortTitle)); v.units[0] = 0; // unfortunately, CLAP has no unit for parameter values v.unitId = unit; @@ -153,9 +155,13 @@ Vst3Parameter* Vst3Parameter::create(uint8_t bus, uint8_t channel, uint8_t cc, V { fullname = "controller"; } - str8ToStr16(v.title, fullname.c_str(), str16BufferSize(v.title)); + + utf8_to_utf16l(fullname.c_str(), (uint16_t*)(v.title), str16BufferSize(v.title)); + utf8_to_utf16l(name, (uint16_t*)v.shortTitle, str16BufferSize(v.shortTitle)); + + // str8ToStr16(v.title, fullname.c_str(), str16BufferSize(v.title)); // TODO: string shrink algorithm shortening the string a bit - str8ToStr16(v.shortTitle, name, str16BufferSize(v.shortTitle)); + // str8ToStr16(v.shortTitle, name, str16BufferSize(v.shortTitle)); v.units[0] = 0; // nothing in the "unit" field // the unit will not be set here, but outside diff --git a/src/detail/vst3/parameter.h b/src/detail/vst3/parameter.h index e2ef4c57..72dea7d2 100644 --- a/src/detail/vst3/parameter.h +++ b/src/detail/vst3/parameter.h @@ -28,6 +28,8 @@ #include #include +void utf8_to_utf16l(const char* utf8string, uint16_t* target, size_t targetsize); + class Vst3Parameter : public Steinberg::Vst::Parameter { using super = Steinberg::Vst::Parameter; diff --git a/src/wrapasvst3.cpp b/src/wrapasvst3.cpp index f26b1af1..2d7103a4 100644 --- a/src/wrapasvst3.cpp +++ b/src/wrapasvst3.cpp @@ -42,6 +42,85 @@ struct ClapHostExtensions }; #endif +void utf8_to_utf16l(const char* utf8string, uint16_t* target, size_t targetsize) +{ + bool result = true; + uint32_t codepoint = 0; + int state = 1; + size_t targetpos = 0; + + auto src = reinterpret_cast(utf8string); + size_t pos = 0; + while (src[pos] && (targetpos < (targetsize-1))) + { + auto byte = src[pos]; + + if ((byte & 0b10000000) == 0b00000000) + { + codepoint = byte; + pos += 1; + } + else + { + if (((byte & 0b11100000) == 0b11000000) && src[1]) + { + codepoint = byte & 0b00011111; + codepoint = (codepoint << 6) | ((src[pos + 1]) & 0b00111111); + pos += 2; + } + else if (((byte & 0b11110000) == 0b11100000) && src[1] && src[2]) + { + codepoint = byte & 0b00001111; + codepoint = (codepoint << 6) | ((src[pos + 1] & 0b00111111)); + codepoint = (codepoint << 6) | ((src[pos + 2] & 0b00111111)); + pos += 3; + } + else if (((byte & 0b11111000) == 0b11110000) && src[1] && src[2] && src[3]) + { + codepoint = byte & 0b00000111; + codepoint = (codepoint << 6) | ((src[pos + 1] & 0b00111111)); + codepoint = (codepoint << 6) | ((src[pos + 2] & 0b00111111)); + codepoint = (codepoint << 6) | ((src[pos + 3] & 0b00111111)); + pos += 4; + } + else + { + return; + } + } + { + if (codepoint >= 0xD800 && codepoint <= 0xDFFF) + { + target[targetpos] = 0; + return; + // throw conversion_error("illegal UTF-32 codepoint (surrogat area)"); + } + if (codepoint <= 0xFFFF) + { + target[targetpos++] = codepoint; + } + else + { + if (codepoint <= 0x10FFFF && (targetpos < (targetsize-3))) + { + codepoint -= 0x10000; + uint16_t highsurr = static_cast((codepoint >> 10) + 0xD800); + uint16_t lowsurr = static_cast((codepoint & 0x3FF) + 0xDC00); + target[targetpos++] = highsurr; + target[targetpos++] = lowsurr; + } + else + { + target[targetpos] = 0; + return; + } + } + } + } + target[targetpos] = 0; +} + + tresult PLUGIN_API ClapAsVst3::initialize(FUnknown* context) { auto result = super::initialize(context); @@ -337,6 +416,10 @@ tresult PLUGIN_API ClapAsVst3::getParamValueByString(Vst::ParamID id, Vst::TChar char inbuf[128]; m.copyTo8(inbuf, 0, 128); double out = 0.; + if (param->isMidi) + { + return Steinberg::kResultFalse; + } if (this->_plugin->_ext._params->text_to_value(_plugin->_plugin, param->id, inbuf, &out)) { valueNormalized = param->asVst3Value(out); @@ -545,7 +628,9 @@ void ClapAsVst3::addAudioBusFrom(const clap_audio_port_info_t* info, bool is_inp // bool supports64bit = (info->flags & CLAP_AUDIO_PORT_SUPPORTS_64BITS); Steinberg::char16 name16[256]; // str8tostr16 writes to position n to terminate, so don't overflow - Steinberg::str8ToStr16(&name16[0], info->name, 255); + + // Steinberg::str8ToStr16(&name16[0], info->name, 255); + utf8_to_utf16l(info->name, (uint16_t*)name16, 255); if (is_input) { addAudioInput(name16, spk, bustype, Vst::BusInfo::kDefaultActive); @@ -569,7 +654,8 @@ void ClapAsVst3::addMIDIBusFrom(const clap_note_port_info_t* info, uint32_t inde Steinberg::char16 name16[256]; // str8tostr16 writes to position n to terminate, so don't overflow - Steinberg::str8ToStr16(&name16[0], info->name, 255); + // Steinberg::str8ToStr16(&name16[0], info->name, 255); + utf8_to_utf16l(info->name, (uint16_t*)name16, 255); if (is_input) { addEventInput(name16, numchannels, Vst::BusTypes::kMain, Vst::BusInfo::kDefaultActive);