diff --git a/include/clapwrapper/wrapper_host.h b/include/clapwrapper/wrapper_host.h new file mode 100644 index 00000000..903da62c --- /dev/null +++ b/include/clapwrapper/wrapper_host.h @@ -0,0 +1,54 @@ +#ifndef CLAPWRAPPER_WRAPPER_HOST_H +#define CLAPWRAPPER_WRAPPER_HOST_H + +#include "clap/private/macros.h" +#include "clap/host.h" + +// CLAP_ABI was introduced in CLAP 1.1.2, for older versions we make it transparent +#ifndef CLAP_ABI +#define CLAP_ABI +#endif + +/* + clap_wrapper_host_information + + a host extension every clap-wrapper host provides, so a plugin can tell it is wrapped + and in what. a null result from get_extension means not wrapped. + + auto *cwh = (const clap_wrapper_host_information_t *)host->get_extension( + host, CLAP_WRAPPER_HOST_INFORMATION); + if (cwh && strcmp(cwh->get_wrapper_flavor(host), CLAP_WRAPPER_HOST_FLAVOR_AUV2) == 0) + ... +*/ + +#ifdef __cplusplus +extern "C" +{ +#endif + + static const CLAP_CONSTEXPR char CLAP_WRAPPER_HOST_INFORMATION[] = "clap-wrapper.host-information/0"; + + // compare with strcmp; the pointers are not guaranteed to be these arrays + static const CLAP_CONSTEXPR char CLAP_WRAPPER_HOST_FLAVOR_AUV2[] = "auv2"; + static const CLAP_CONSTEXPR char CLAP_WRAPPER_HOST_FLAVOR_AUV3[] = "auv3"; + static const CLAP_CONSTEXPR char CLAP_WRAPPER_HOST_FLAVOR_AAX[] = "aax"; + static const CLAP_CONSTEXPR char CLAP_WRAPPER_HOST_FLAVOR_VST3[] = "vst3"; + static const CLAP_CONSTEXPR char CLAP_WRAPPER_HOST_FLAVOR_STANDALONE[] = "standalone"; + + typedef struct clap_wrapper_host_information + { + // one of CLAP_WRAPPER_HOST_FLAVOR_*, valid for the life of the host + // [thread-safe] + const char *(CLAP_ABI *get_wrapper_flavor)(const clap_host_t *host); + + // the real host's name without host->name's wrapper suffix, e.g. "REAPER" + // null when unknowable + // [thread-safe] + const char *(CLAP_ABI *get_underlying_host_name)(const clap_host_t *host); + } clap_wrapper_host_information_t; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/clap_proxy.cpp b/src/clap_proxy.cpp index b836e522..1d4e84b5 100644 --- a/src/clap_proxy.cpp +++ b/src/clap_proxy.cpp @@ -145,6 +145,10 @@ static bool track_info_get(const clap_host_t *host, clap_track_info_t *info) const clap_host_track_info trackinfo = {track_info_get}; +const clap_wrapper_host_information_t wrapper_host_information = { + [](const clap_host_t *host) -> const char * { return self(host)->wrapper_flavor(); }, + [](const clap_host_t *host) -> const char * { return self(host)->underlying_host_name(); }}; + const clap_host_preset_load_t preset_load = { /* on_error */ [](const clap_host_t *host, uint32_t location_kind, const char *location, const char *load_key, @@ -589,6 +593,7 @@ const void *Plugin::clapExtension(const clap_host * /*host*/, const char *extens if (!strcmp(extension, CLAP_EXT_CONTEXT_MENU)) return &HostExt::context_menu; if (!strcmp(extension, CLAP_EXT_PRESET_LOAD) || !strcmp(extension, CLAP_EXT_PRESET_LOAD_COMPAT)) return &HostExt::preset_load; + if (!strcmp(extension, CLAP_WRAPPER_HOST_INFORMATION)) return &HostExt::wrapper_host_information; #if LIN if (!strcmp(extension, CLAP_EXT_POSIX_FD_SUPPORT)) return &HostExt::hostposixfd; diff --git a/src/clap_proxy.h b/src/clap_proxy.h index de63839c..88f58a3a 100644 --- a/src/clap_proxy.h +++ b/src/clap_proxy.h @@ -74,6 +74,11 @@ class IHost virtual bool track_info_get(clap_track_info_t *info) = 0; virtual const char *host_get_name() = 0; + // one of CLAP_WRAPPER_HOST_FLAVOR_*, reported through CLAP_WRAPPER_HOST_INFORMATION + virtual const char *wrapper_flavor() const = 0; + // set before the plugin is created and not changed after; nullptr when unknowable + virtual const char *underlying_host_name() const = 0; + // clap.preset-load, host side. Defaulted rather than pure: only the formats // that can show a preset list (VST3 program lists, AU factory presets) have // anything to do here, and making these pure would force every other @@ -283,6 +288,15 @@ class Plugin return _parentHost->track_info_get(info); } + const char *wrapper_flavor() const + { + return _parentHost->wrapper_flavor(); + } + const char *underlying_host_name() const + { + return _parentHost->underlying_host_name(); + } + // clap_timer support bool register_timer(uint32_t period_ms, clap_id *timer_id); bool unregister_timer(clap_id timer_id); diff --git a/src/detail/aax/audioconfig.cpp b/src/detail/aax/audioconfig.cpp index 8f21905c..fc2b4606 100644 --- a/src/detail/aax/audioconfig.cpp +++ b/src/detail/aax/audioconfig.cpp @@ -196,6 +196,9 @@ plugin_bus_info_t getAvailableBusConfigs(Clap::Library *factory, uint32_t index) static const clap_host_audio_ports_t micro_audio_ports = { [](const clap_host_t *host, uint32_t flag) -> bool { return false; }, [](const clap_host_t *host, uint32_t flags) -> void {}}; + static const clap_wrapper_host_information_t micro_wrapper_info = { + [](const clap_host_t *host) -> const char * { return CLAP_WRAPPER_HOST_FLAVOR_AAX; }, + [](const clap_host_t *host) -> const char * { return nullptr; }}; clap_host_t microhost = { CLAP_VERSION, nullptr, @@ -209,6 +212,7 @@ plugin_bus_info_t getAvailableBusConfigs(Clap::Library *factory, uint32_t index) LOGDETAIL("plugin requests microhost extension {}", extension_id); if (!strcmp(CLAP_EXT_PARAMS, extension_id)) return µ_params; if (!strcmp(CLAP_EXT_AUDIO_PORTS, extension_id)) return µ_audio_ports; + if (!strcmp(CLAP_WRAPPER_HOST_INFORMATION, extension_id)) return µ_wrapper_info; return nullptr; }, [](const struct clap_host *host) -> void {}, // request_restart diff --git a/src/detail/aax/wrapper.cpp b/src/detail/aax/wrapper.cpp index caf7e05d..d22fefa7 100644 --- a/src/detail/aax/wrapper.cpp +++ b/src/detail/aax/wrapper.cpp @@ -550,6 +550,9 @@ AAX_Result ClapAsAAX::EffectInit() LOGINFO(fmt::format("AAX Effect Init for '{}'", m.StdString().c_str())); + AAX_CString hostname; + if (AAX_SUCCESS == _aax_ctrl->GetHostName(&hostname)) _underlying_hostname = hostname.StdString(); + _library = CLAPAAX::guarantee_clap(); _plugin = Clap::Plugin::createInstance(_library->_pluginFactory, m.StdString(), this); @@ -1163,13 +1166,7 @@ bool ClapAsAAX::track_info_get(clap_track_info_t *info) const char *ClapAsAAX::host_get_name() { - AAX_IController *ctrl = Controller(); - AAX_CString hostname; - if (AAX_SUCCESS == ctrl->GetHostName(&hostname)) - { - _wrapper_hostname = hostname.StdString(); - _wrapper_hostname.append(" (CLAP-as-AAX)"); - } + if (!_underlying_hostname.empty()) _wrapper_hostname = _underlying_hostname + " (CLAP-as-AAX)"; return _wrapper_hostname.c_str(); } diff --git a/src/detail/aax/wrapper.h b/src/detail/aax/wrapper.h index 51eb7f49..29994b74 100644 --- a/src/detail/aax/wrapper.h +++ b/src/detail/aax/wrapper.h @@ -246,6 +246,14 @@ class ClapAsAAX : public AAX_CEffectParameters, bool track_info_get(clap_track_info_t *info) override; const char *host_get_name() override; + const char *wrapper_flavor() const override + { + return CLAP_WRAPPER_HOST_FLAVOR_AAX; + } + const char *underlying_host_name() const override + { + return _underlying_hostname.empty() ? nullptr : _underlying_hostname.c_str(); + } bool supportsContextMenu() const override; // context_menu @@ -273,6 +281,7 @@ class ClapAsAAX : public AAX_CEffectParameters, os::State _os_attached; std::string _wrapper_hostname = "CLAP-As-AAX-Wrapper"; + std::string _underlying_hostname; // _parameterMap maps the AAX CParamID to the wrapped parameter std::map> _parameterMap; diff --git a/src/detail/auv2/auv2_base_classes.h b/src/detail/auv2/auv2_base_classes.h index 34aafe44..e1ff5c29 100644 --- a/src/detail/auv2/auv2_base_classes.h +++ b/src/detail/auv2/auv2_base_classes.h @@ -698,6 +698,15 @@ class WrapAsAUV2 : public ausdk::AUBase, return false; } + const char *wrapper_flavor() const override + { + return CLAP_WRAPPER_HOST_FLAVOR_AUV2; + } + const char *underlying_host_name() const override + { + return _underlying_hostname.empty() ? nullptr : _underlying_hostname.c_str(); + } + const char *host_get_name() override { char text[65]; @@ -920,6 +929,7 @@ class WrapAsAUV2 : public ausdk::AUBase, // std::vector _midi_outports_info; std::string _hostname = "CLAP-as-AUv2"; + std::string _underlying_hostname; #ifdef DUAL_SCHEDULING_ENABLED bool _midi_dualscheduling_mode = false; diff --git a/src/detail/auv3/auv3_audiounit.mm b/src/detail/auv3/auv3_audiounit.mm index 6f1dc567..d9bb62e5 100644 --- a/src/detail/auv3/auv3_audiounit.mm +++ b/src/detail/auv3/auv3_audiounit.mm @@ -136,6 +136,7 @@ - (void)_applyGUISizeWidth:(uint32_t)width height:(uint32_t)height; int _idx = 0; os::State _os_attached; std::string _hostname = "CLAP-as-AUv3"; + std::string _underlying_hostname; std::atomic _initialized{false}; std::atomic_bool _requestUICallback{false}; // set by mark_dirty(), serviced by the idle timer @@ -600,6 +601,15 @@ void fireTimers() } } + const char *wrapper_flavor() const override + { + return CLAP_WRAPPER_HOST_FLAVOR_AUV3; + } + const char *underlying_host_name() const override + { + return _underlying_hostname.empty() ? nullptr : _underlying_hostname.c_str(); + } + const char *host_get_name() override { NSBundle *mainBundle = [NSBundle mainBundle]; @@ -981,6 +991,7 @@ - (instancetype)initWithComponentDescription:(AudioComponentDescription)componen // Create the plugin instance AUV3LOG("init: creating plugin instance via factory"); + _impl->_underlying_hostname = os::getHostAppName(); _impl->_plugin = Clap::Plugin::createInstance(_library._pluginFactory, _impl->_desc->id, _impl.get()); if (!_impl->_plugin) diff --git a/src/detail/clap/fsutil.h b/src/detail/clap/fsutil.h index aaeb78b7..61210460 100644 --- a/src/detail/clap/fsutil.h +++ b/src/detail/clap/fsutil.h @@ -20,6 +20,7 @@ #include "clapwrapper/vst3.h" #include "clapwrapper/auv2.h" #include "clapwrapper/aax.h" +#include "clapwrapper/wrapper_host.h" #include "../ara/ara.h" #include "detail/os/fs.h" diff --git a/src/detail/os/macos.mm b/src/detail/os/macos.mm index 05de6147..dc84e209 100644 --- a/src/detail/os/macos.mm +++ b/src/detail/os/macos.mm @@ -149,4 +149,15 @@ uint64_t getTickInMS() return getPluginPath().stem(); } +std::string getHostAppName() +{ + NSBundle *mainBundle = [NSBundle mainBundle]; + // AUHostingService and our own appex are both XPC! bundles + if ([[mainBundle objectForInfoDictionaryKey:@"CFBundlePackageType"] isEqualToString:@"XPC!"]) + return {}; + NSString *name = [mainBundle objectForInfoDictionaryKey:@"CFBundleName"]; + if (!name) name = [[NSProcessInfo processInfo] processName]; + return name ? std::string([name UTF8String]) : std::string(); +} + } // namespace os diff --git a/src/detail/os/osutil.h b/src/detail/os/osutil.h index 49b4670d..f0d8e705 100644 --- a/src/detail/os/osutil.h +++ b/src/detail/os/osutil.h @@ -85,6 +85,11 @@ fs::path getPluginPath(); std::string getParentFolderName(); std::string getBinaryName(); +#if MAC +// empty when loaded into an xpc service or appex, where the real host is unknowable +std::string getHostAppName(); +#endif + #ifdef WIN32 void init(); void terminate(); diff --git a/src/detail/standalone/standalone_host.h b/src/detail/standalone/standalone_host.h index da2fef4e..61f14e95 100644 --- a/src/detail/standalone/standalone_host.h +++ b/src/detail/standalone/standalone_host.h @@ -230,6 +230,20 @@ struct StandaloneHost : Clap::IHost bool unregister_timer(clap_id timer_id) override; const char *host_get_name() override; + const char *wrapper_flavor() const override + { + return CLAP_WRAPPER_HOST_FLAVOR_STANDALONE; + } + const char *underlying_host_name() const override + { +#if MAC + return "Wrapper Mac Standalone"; +#elif WIN + return "Wrapper Windows Standalone"; +#else + return "Wrapper Linux Standalone"; +#endif + } bool track_info_get(clap_track_info_t *info) override { diff --git a/src/wrapasauv2.cpp b/src/wrapasauv2.cpp index 63f3b8a2..ab4f3c6f 100644 --- a/src/wrapasauv2.cpp +++ b/src/wrapasauv2.cpp @@ -169,6 +169,7 @@ WrapAsAUV2::WrapAsAUV2(const std::string &clapname, const std::string &clapid, i */ // pffffrzz(); // <- enable this to have a hook to attach a debugger + _underlying_hostname = os::getHostAppName(); _plugin = Clap::Plugin::createInstance(_library._pluginFactory, _desc->id, this); if (_plugin) { diff --git a/src/wrapasvst3.cpp b/src/wrapasvst3.cpp index 0baf2881..d9b2ef01 100644 --- a/src/wrapasvst3.cpp +++ b/src/wrapasvst3.cpp @@ -152,6 +152,9 @@ tresult PLUGIN_API ClapAsVst3::initialize(FUnknown *context) { if (!_plugin) { + Steinberg::Vst::String128 res; + if (vst3HostApplication && kResultOk == vst3HostApplication->getName(res)) + underlying_hostname = stringconv::convert(res); _plugin = Clap::Plugin::createInstance(*_library, _libraryIndex, this); } result = (_plugin && _plugin->initialize()) ? kResultOk : kResultFalse; @@ -1759,15 +1762,7 @@ bool ClapAsVst3::unregister_timer(clap_id timer_id) const char *ClapAsVst3::host_get_name() { - if (vst3HostApplication) - { - Steinberg::Vst::String128 res; - if (kResultOk == vst3HostApplication->getName(res)) - { - wrapper_hostname = stringconv::convert(res); - wrapper_hostname.append(" (CLAP-as-VST3)"); - } - } + if (!underlying_hostname.empty()) wrapper_hostname = underlying_hostname + " (CLAP-as-VST3)"; return wrapper_hostname.c_str(); } diff --git a/src/wrapasvst3.h b/src/wrapasvst3.h index 5d809c73..cf59fc48 100644 --- a/src/wrapasvst3.h +++ b/src/wrapasvst3.h @@ -306,6 +306,7 @@ class ClapAsVst3 : public Steinberg::Vst::SingleComponentEffect, IPtr vst3ContextMenu = nullptr; IPtr vst3HostApplication = nullptr; std::string wrapper_hostname = "CLAP-As-VST3-Wrapper"; + std::string underlying_hostname; std::vector contextmenuitems; uint32_t vst3ContextMenuParamID = 0; @@ -348,6 +349,14 @@ class ClapAsVst3 : public Steinberg::Vst::SingleComponentEffect, bool unregister_timer(clap_id timer_id) override; const char *host_get_name() override; + const char *wrapper_flavor() const override + { + return CLAP_WRAPPER_HOST_FLAVOR_VST3; + } + const char *underlying_host_name() const override + { + return underlying_hostname.empty() ? nullptr : underlying_hostname.c_str(); + } bool supportsContextMenu() const override; // context_menu diff --git a/tests/clap-first-example/distortion_clap.cpp b/tests/clap-first-example/distortion_clap.cpp index 1b765922..ee1c96ca 100644 --- a/tests/clap-first-example/distortion_clap.cpp +++ b/tests/clap-first-example/distortion_clap.cpp @@ -14,6 +14,7 @@ #include #include #include "clapwrapper/vst3.h" +#include "clapwrapper/wrapper_host.h" #include "distortion_clap_entry.h" static const char *features[] = {CLAP_PLUGIN_FEATURE_AUDIO_EFFECT, CLAP_PLUGIN_FEATURE_STEREO, @@ -371,6 +372,32 @@ static bool clap1stDist_init(const struct clap_plugin *plugin) if (plug->hostLog && plug->host) { plug->hostLog->log(plug->host, CLAP_LOG_INFO, "Created clap1st Distortion compiled with C++"); + + // null means a real clap host rather than clap-wrapper + auto *cwh = (const clap_wrapper_host_information_t *)plug->host->get_extension( + plug->host, CLAP_WRAPPER_HOST_INFORMATION); + const char *how = "Running as a CLAP"; + if (cwh) + { + const char *flavor = cwh->get_wrapper_flavor(plug->host); + if (strcmp(flavor, CLAP_WRAPPER_HOST_FLAVOR_VST3) == 0) + how = "Wrapped as a VST3"; + else if (strcmp(flavor, CLAP_WRAPPER_HOST_FLAVOR_AUV2) == 0) + how = "Wrapped as an AUv2"; + else if (strcmp(flavor, CLAP_WRAPPER_HOST_FLAVOR_STANDALONE) == 0) + how = "Wrapped as a standalone"; + else + how = "Wrapped in some other format"; + } + plug->hostLog->log(plug->host, CLAP_LOG_INFO, how); + + if (cwh) + { + const char *underlying = cwh->get_underlying_host_name(plug->host); + char msg[256]; + snprintf(msg, sizeof(msg), "Underlying host is '%s'", underlying ? underlying : "unknown"); + plug->hostLog->log(plug->host, CLAP_LOG_INFO, msg); + } } return true;