diff --git a/source/discovery/carla-discovery.cpp b/source/discovery/carla-discovery.cpp index 003c95c58..e6fa0fee6 100644 --- a/source/discovery/carla-discovery.cpp +++ b/source/discovery/carla-discovery.cpp @@ -29,13 +29,6 @@ #include "CarlaVst3Utils.hpp" #ifdef CARLA_OS_MAC -# define Component CocoaComponent -# define MemoryBlock CocoaMemoryBlock -# define Point CocoaPoint -# import -# undef Component -# undef MemoryBlock -# undef Point # include "CarlaMacUtils.cpp" # if defined(USING_JUCE) && defined(__aarch64__) # include @@ -74,6 +67,9 @@ # if JUCE_PLUGINHOST_VST # define USING_JUCE_FOR_VST2 # endif +# if JUCE_PLUGINHOST_VST3 +# define USING_JUCE_FOR_VST3 +# endif # pragma GCC diagnostic pop #endif @@ -993,44 +989,31 @@ static intptr_t VSTCALLBACK vstHostCallback(AEffect* const effect, const int32_t return ret; } -static void do_vst_check(lib_t& libHandle, const char* const filename, const bool doInit) +static void do_vst2_check(lib_t& libHandle, const char* const filename, const bool doInit) { VST_Function vstFn = nullptr; #ifdef CARLA_OS_MAC - CFBundleRef bundleRef = nullptr; - CFBundleRefNum resFileId = 0; + BundleLoader bundleLoader; if (libHandle == nullptr) { - const CFURLRef urlRef = CFURLCreateFromFileSystemRepresentation(0, (const UInt8*)filename, (CFIndex)strlen(filename), true); - CARLA_SAFE_ASSERT_RETURN(urlRef != nullptr,); - - bundleRef = CFBundleCreate(kCFAllocatorDefault, urlRef); - CFRelease(urlRef); - CARLA_SAFE_ASSERT_RETURN(bundleRef != nullptr,); - - if (! CFBundleLoadExecutable(bundleRef)) + if (! bundleLoader.load(filename)) { - CFRelease(bundleRef); - DISCOVERY_OUT("error", "Failed to load VST bundle executable"); + DISCOVERY_OUT("error", "Failed to load VST2 bundle executable"); return; } - vstFn = (VST_Function)CFBundleGetFunctionPointerForName(bundleRef, CFSTR("main_macho")); + vstFn = (VST_Function)CFBundleGetFunctionPointerForName(bundleLoader.ref, CFSTR("main_macho")); if (vstFn == nullptr) - vstFn = (VST_Function)CFBundleGetFunctionPointerForName(bundleRef, CFSTR("VSTPluginMain")); + vstFn = (VST_Function)CFBundleGetFunctionPointerForName(bundleLoader.ref, CFSTR("VSTPluginMain")); if (vstFn == nullptr) { - CFBundleUnloadExecutable(bundleRef); - CFRelease(bundleRef); - DISCOVERY_OUT("error", "Not a VST plugin"); + DISCOVERY_OUT("error", "Not a VST2 plugin"); return; } - - resFileId = CFBundleOpenBundleResourceMap(bundleRef); } else #endif @@ -1355,14 +1338,7 @@ static void do_vst_check(lib_t& libHandle, const char* const filename, const boo effect->dispatcher(effect, effClose, 0, 0, nullptr, 0.0f); } -#ifdef CARLA_OS_MAC - if (bundleRef != nullptr) - { - CFBundleCloseBundleResourceMap(bundleRef, resFileId); - CFBundleUnloadExecutable(bundleRef); - CFRelease(bundleRef); - } -#else +#ifndef CARLA_OS_MAC return; // unused @@ -1371,6 +1347,499 @@ static void do_vst_check(lib_t& libHandle, const char* const filename, const boo } #endif // ! USING_JUCE_FOR_VST2 +#ifndef USING_JUCE_FOR_VST3 +static uint32_t V3_API v3_ref_static(void*) { return 1; } +static uint32_t V3_API v3_unref_static(void*) { return 0; } + +struct carla_v3_host_application : v3_host_application_cpp { + carla_v3_host_application() + { + query_interface = carla_query_interface; + ref = v3_ref_static; + unref = v3_unref_static; + app.get_name = carla_get_name; + app.create_instance = carla_create_instance; + } + + static v3_result V3_API carla_query_interface(void* const self, const v3_tuid iid, void** const iface) + { + if (v3_tuid_match(iid, v3_funknown_iid) || + v3_tuid_match(iid, v3_host_application_iid)) + { + *iface = self; + return V3_OK; + } + + *iface = nullptr; + return V3_NO_INTERFACE; + } + + static v3_result V3_API carla_get_name(void*, v3_str_128 name) + { + static const char hostname[] = "Carla-Discovery\0"; + + for (size_t i=0; i(libHandle, V3_ENTRYFNNAME); + v3_exit = lib_symbol(libHandle, V3_EXITFNNAME); + v3_get = lib_symbol(libHandle, V3_GETFNNAME); +#endif + + if (v3_entry == nullptr || v3_exit == nullptr || v3_get == nullptr) + { + DISCOVERY_OUT("error", "Not a VST3 plugin"); + return; + } + + // call entry point +#if defined(CARLA_OS_MAC) + v3_entry(bunbleLoader.ref); +#elif defined(CARLA_OS_WIN) + v3_entry(); +#else + v3_entry(libHandle); +#endif + + carla_v3_host_application hostApplication; + carla_v3_host_application* const hostApplicationPtr = &hostApplication; + v3_funknown** const hostContext = (v3_funknown**)&hostApplicationPtr; + + // fetch initial factory + v3_plugin_factory** factory1 = v3_get(); + CARLA_SAFE_ASSERT_RETURN(factory1 != nullptr, v3_exit()); + + // get factory info + v3_factory_info factoryInfo = {}; + CARLA_SAFE_ASSERT_RETURN(v3_cpp_obj(factory1)->get_factory_info(factory1, &factoryInfo) == V3_OK, v3_exit()); + + // get num classes + const int32_t numClasses = v3_cpp_obj(factory1)->num_classes(factory1); + CARLA_SAFE_ASSERT_RETURN(numClasses > 0, v3_exit()); + + // query 2nd factory + v3_plugin_factory_2** factory2 = nullptr; + if (v3_cpp_obj_query_interface(factory1, v3_plugin_factory_2_iid, &factory2) == V3_OK) + { + CARLA_SAFE_ASSERT_RETURN(factory2 != nullptr, v3_exit()); + } + else + { + CARLA_SAFE_ASSERT(factory2 == nullptr); + factory2 = nullptr; + } + + // query 3rd factory + v3_plugin_factory_3** factory3 = nullptr; + if (factory2 != nullptr && v3_cpp_obj_query_interface(factory2, v3_plugin_factory_3_iid, &factory3) == V3_OK) + { + CARLA_SAFE_ASSERT_RETURN(factory3 != nullptr, v3_exit()); + } + else + { + CARLA_SAFE_ASSERT(factory3 == nullptr); + factory3 = nullptr; + } + + // set host context (application) if 3rd factory provided + if (factory3 != nullptr) + v3_cpp_obj(factory3)->set_host_context(factory3, hostContext); + + // go through all relevant classes + for (int32_t i=0; iget_class_info_2(factory2, i, &classInfo.v2); + else + v3_cpp_obj(factory1)->get_class_info(factory1, i, &classInfo.v1); + + // safety check + CARLA_SAFE_ASSERT_CONTINUE(classInfo.v1.cardinality == 0x7FFFFFFF); + + // only check for audio plugins + if (std::strcmp(classInfo.v1.category, "Audio Module Class") != 0) + continue; + + // create instance + void* instance = nullptr; + CARLA_SAFE_ASSERT_CONTINUE(v3_cpp_obj(factory1)->create_instance(factory1, classInfo.v1.class_id, v3_component_iid, &instance) == V3_OK); + CARLA_SAFE_ASSERT_CONTINUE(instance != nullptr); + + // initialize instance + v3_component** const component = static_cast(instance); + + CARLA_SAFE_ASSERT_CONTINUE(v3_cpp_obj_initialize(component, hostContext) == V3_OK); + + // create edit controller + v3_edit_controller** controller = nullptr; + bool shouldTerminateController; + if (v3_cpp_obj_query_interface(component, v3_edit_controller_iid, &controller) != V3_OK) + controller = nullptr; + + if (controller != nullptr) + { + // got edit controller from casting component, assume they belong to the same object + shouldTerminateController = false; + } + else + { + // try to create edit controller from factory + v3_tuid uid = {}; + if (v3_cpp_obj(component)->get_controller_class_id(component, uid) == V3_OK) + { + instance = nullptr; + if (v3_cpp_obj(factory1)->create_instance(factory1, uid, v3_edit_controller_iid, &instance) == V3_OK && instance != nullptr) + controller = static_cast(instance); + } + + if (controller == nullptr) + { + DISCOVERY_OUT("warning", "Plugin '" << classInfo.v1.name << "' does not have an edit controller"); + v3_cpp_obj_terminate(component); + v3_cpp_obj_unref(component); + continue; + } + + // component is separate from controller, needs its dedicated initialize and terminate + shouldTerminateController = true; + v3_cpp_obj_initialize(controller, hostContext); + } + + // fill in all the details + uint hints = 0x0; + int audioIns = 0; + int audioOuts = 0; + int cvIns = 0; + int cvOuts = 0; + int parameterIns = 0; + int parameterOuts = 0; + + const int32_t numAudioInputBuses = v3_cpp_obj(component)->get_bus_count(component, V3_AUDIO, V3_INPUT); + const int32_t numEventInputBuses = v3_cpp_obj(component)->get_bus_count(component, V3_EVENT, V3_INPUT); + const int32_t numAudioOutputBuses = v3_cpp_obj(component)->get_bus_count(component, V3_AUDIO, V3_OUTPUT); + const int32_t numEventOutputBuses = v3_cpp_obj(component)->get_bus_count(component, V3_EVENT, V3_OUTPUT); + const int32_t numParameters = v3_cpp_obj(controller)->get_parameter_count(controller); + + CARLA_SAFE_ASSERT(numAudioInputBuses >= 0); + CARLA_SAFE_ASSERT(numEventInputBuses >= 0); + CARLA_SAFE_ASSERT(numAudioOutputBuses >= 0); + CARLA_SAFE_ASSERT(numEventOutputBuses >= 0); + CARLA_SAFE_ASSERT(numParameters >= 0); + + for (int32_t j=0; jget_bus_info(component, V3_AUDIO, V3_INPUT, j, &busInfo) == V3_OK); + + if (busInfo.flags & V3_IS_CONTROL_VOLTAGE) + ++cvIns; + else + ++audioIns; + } + + for (int32_t j=0; jget_bus_info(component, V3_AUDIO, V3_OUTPUT, j, &busInfo) == V3_OK); + + if (busInfo.flags & V3_IS_CONTROL_VOLTAGE) + ++cvOuts; + else + ++audioOuts; + } + + for (int32_t j=0; jget_parameter_info(controller, j, ¶mInfo) == V3_OK); + + if (paramInfo.flags & (V3_PARAM_IS_BYPASS|V3_PARAM_IS_HIDDEN|V3_PARAM_PROGRAM_CHANGE)) + continue; + + if (paramInfo.flags & V3_PARAM_READ_ONLY) + ++parameterOuts; + else + ++parameterIns; + } + + if (v3_plugin_view** const view = v3_cpp_obj(controller)->create_view(controller, "view")) + { + if (v3_cpp_obj(view)->is_platform_type_supported(view, V3_VIEW_PLATFORM_TYPE_NATIVE) == V3_TRUE) + hints |= PLUGIN_HAS_CUSTOM_UI; + + v3_cpp_obj_unref(view); + } + + if (factory2 != nullptr && std::strstr(classInfo.v2.sub_categories, "Instrument") != nullptr) + hints |= PLUGIN_IS_SYNTH; + + if (doInit) + { + v3_audio_processor** processor = nullptr; + CARLA_SAFE_ASSERT_BREAK(v3_cpp_obj_query_interface(component, v3_audio_processor_iid, &processor) == V3_OK); + CARLA_SAFE_ASSERT_BREAK(processor != nullptr); + + CARLA_SAFE_ASSERT_BREAK(v3_cpp_obj(processor)->can_process_sample_size(processor, V3_SAMPLE_32) == V3_OK); + + CARLA_SAFE_ASSERT_BREAK(v3_cpp_obj(component)->set_active(component, true) == V3_OK); + CARLA_SAFE_ASSERT_BREAK(v3_cpp_obj(component)->set_active(component, false) == V3_OK); + + v3_process_setup setup = { V3_REALTIME, V3_SAMPLE_32, kBufferSize, kSampleRate }; + CARLA_SAFE_ASSERT_BREAK(v3_cpp_obj(processor)->setup_processing(processor, &setup) == V3_OK); + + for (int32_t j=0; jget_bus_info(component, V3_AUDIO, V3_INPUT, j, &busInfo) == V3_OK); + + if ((busInfo.flags & V3_DEFAULT_ACTIVE) == 0x0) { + CARLA_SAFE_ASSERT_BREAK(v3_cpp_obj(component)->activate_bus(component, V3_AUDIO, V3_INPUT, j, true) == V3_OK); + } + } + + for (int32_t j=0; jget_bus_info(component, V3_AUDIO, V3_OUTPUT, j, &busInfo) == V3_OK); + + if ((busInfo.flags & V3_DEFAULT_ACTIVE) == 0x0) { + CARLA_SAFE_ASSERT_BREAK(v3_cpp_obj(component)->activate_bus(component, V3_AUDIO, V3_OUTPUT, j, true) == V3_OK); + } + } + + CARLA_SAFE_ASSERT_BREAK(v3_cpp_obj(component)->set_active(component, true) == V3_OK); + CARLA_SAFE_ASSERT_BREAK(v3_cpp_obj(processor)->set_processing(processor, true) == V3_OK); + + float* bufferAudioIn[std::max(1, numAudioInputBuses)]; + float* bufferAudioOut[std::max(1, numAudioOutputBuses)]; + + if (numAudioInputBuses == 0) + { + bufferAudioIn[0] = nullptr; + } + else + { + for (int j=0; j < numAudioInputBuses; ++j) + { + bufferAudioIn[j] = new float[kBufferSize]; + carla_zeroFloats(bufferAudioIn[j], kBufferSize); + } + } + + if (numAudioOutputBuses == 0) + { + bufferAudioOut[0] = nullptr; + } + else + { + for (int j=0; j < numAudioOutputBuses; ++j) + { + bufferAudioOut[j] = new float[kBufferSize]; + carla_zeroFloats(bufferAudioOut[j], kBufferSize); + } + } + + carla_v3_event_list eventList; + carla_v3_event_list* eventListPtr = &eventList; + + carla_v3_param_changes paramChanges; + carla_v3_param_changes* paramChangesPtr = ¶mChanges; + + v3_audio_bus_buffers processInputs = { numAudioInputBuses, 0, { bufferAudioIn } }; + v3_audio_bus_buffers processOutputs = { numAudioOutputBuses, 0, { bufferAudioOut } }; + + v3_process_context processContext = {}; + processContext.sample_rate = kSampleRate; + + v3_process_data processData = { + V3_REALTIME, + V3_SAMPLE_32, + kBufferSize, + numAudioInputBuses, + numAudioOutputBuses, + &processInputs, + &processOutputs, + (v3_param_changes**)¶mChangesPtr, + (v3_param_changes**)¶mChangesPtr, + (v3_event_list**)&eventListPtr, + (v3_event_list**)&eventListPtr, + &processContext + }; + CARLA_SAFE_ASSERT_BREAK(v3_cpp_obj(processor)->process(processor, &processData) == V3_OK); + + for (int j=0; j < numAudioInputBuses; ++j) + delete[] bufferAudioIn[j]; + for (int j=0; j < numAudioOutputBuses; ++j) + delete[] bufferAudioOut[j]; + + CARLA_SAFE_ASSERT_BREAK(v3_cpp_obj(processor)->set_processing(processor, false) == V3_OK); + CARLA_SAFE_ASSERT_BREAK(v3_cpp_obj(component)->set_active(component, false) == V3_OK); + + v3_cpp_obj_unref(processor); + } + + if (shouldTerminateController) + v3_cpp_obj_terminate(controller); + + v3_cpp_obj_unref(controller); + + v3_cpp_obj_terminate(component); + v3_cpp_obj_unref(component); + + DISCOVERY_OUT("init", "-----------"); + DISCOVERY_OUT("build", BINARY_NATIVE); + DISCOVERY_OUT("hints", hints); + DISCOVERY_OUT("category", getPluginCategoryAsString(factory2 != nullptr ? getPluginCategoryFromV3SubCategories(classInfo.v2.sub_categories) + : getPluginCategoryFromName(classInfo.v1.name))); + DISCOVERY_OUT("name", classInfo.v1.name); + DISCOVERY_OUT("label", tuid2str(classInfo.v1.class_id)); + DISCOVERY_OUT("maker", (factory2 != nullptr ? classInfo.v2.vendor : factoryInfo.vendor)); + DISCOVERY_OUT("audio.ins", audioIns); + DISCOVERY_OUT("audio.outs", audioOuts); + DISCOVERY_OUT("cv.ins", cvIns); + DISCOVERY_OUT("cv.outs", cvOuts); + DISCOVERY_OUT("midi.ins", numEventInputBuses); + DISCOVERY_OUT("midi.outs", numEventOutputBuses); + DISCOVERY_OUT("parameters.ins", parameterIns); + DISCOVERY_OUT("parameters.outs", parameterOuts); + DISCOVERY_OUT("end", "------------"); + } + + // unref interfaces + if (factory2 != nullptr) + v3_cpp_obj_unref(factory2); + + v3_cpp_obj_unref(factory1); + + v3_exit(); +} +#endif // ! USING_JUCE_FOR_VST3 + #ifdef USING_JUCE // ------------------------------------------------------------------------------------------------------------------- // find all available plugin audio ports @@ -1790,7 +2259,7 @@ int main(int argc, char* argv[]) #if defined(USING_JUCE) && JUCE_PLUGINHOST_VST retryJucePlugin = do_juce_check(filename, "VST2", doInit); #else - do_vst_check(handle, filename, doInit); + do_vst2_check(handle, filename, doInit); #endif break; @@ -1798,7 +2267,7 @@ int main(int argc, char* argv[]) #if defined(USING_JUCE) && JUCE_PLUGINHOST_VST3 retryJucePlugin = do_juce_check(filename, "VST3", doInit); #else - DISCOVERY_OUT("error", "VST3 support not available"); + do_vst3_check(handle, filename, doInit); #endif break; diff --git a/source/includes/CarlaDefines.h b/source/includes/CarlaDefines.h index edce8fbb0..214ef958f 100644 --- a/source/includes/CarlaDefines.h +++ b/source/includes/CarlaDefines.h @@ -39,7 +39,7 @@ /* Check OS */ #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(_M_ARM64) # define CARLA_OS_WIN64 -#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(_M_ARM) # define CARLA_OS_WIN32 #elif defined(__APPLE__) # define CARLA_OS_MAC diff --git a/source/includes/travesty/audio_processor.h b/source/includes/travesty/audio_processor.h index 85b2297d7..8783d8e5c 100644 --- a/source/includes/travesty/audio_processor.h +++ b/source/includes/travesty/audio_processor.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * 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 @@ -90,8 +90,9 @@ struct v3_process_setup { */ struct v3_param_value_queue { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_param_id (V3_API* get_param_id)(void* self); int32_t (V3_API* get_point_count)(void* self); v3_result (V3_API* get_point)(void* self, int32_t idx, int32_t* sample_offset, double* value); @@ -102,8 +103,9 @@ static constexpr const v3_tuid v3_param_value_queue_iid = V3_ID(0x01263A18, 0xED074F6F, 0x98C9D356, 0x4686F9BA); struct v3_param_changes { +#ifndef __cplusplus struct v3_funknown; - +#endif int32_t (V3_API* get_param_count)(void* self); struct v3_param_value_queue** (V3_API* get_param_data)(void* self, int32_t idx); struct v3_param_value_queue** (V3_API* add_param_data)(void* self, v3_param_id* id, int32_t* index); @@ -181,8 +183,9 @@ enum { }; struct v3_process_context_requirements { +#ifndef __cplusplus struct v3_funknown; - +#endif uint32_t (V3_API* get_process_context_requirements)(void* self); }; @@ -222,8 +225,9 @@ struct v3_process_data { */ struct v3_audio_processor { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* set_bus_arrangements)(void* self, v3_speaker_arrangement* inputs, int32_t num_inputs, v3_speaker_arrangement* outputs, int32_t num_outputs); v3_result (V3_API* get_bus_arrangement)(void* self, int32_t bus_direction, int32_t idx, v3_speaker_arrangement*); diff --git a/source/includes/travesty/base.h b/source/includes/travesty/base.h index 417d3061c..ab5245d63 100644 --- a/source/includes/travesty/base.h +++ b/source/includes/travesty/base.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * 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 @@ -24,33 +24,8 @@ * deal with C vs C++ differences */ -#ifdef __cplusplus - -/** - * cast object into its proper C++ type. - * this is needed because `struct v3_funknown;` on a C++ class does not inherit `v3_funknown`'s fields. - * we can use this as a little helper for keeping both C and C++ compatiblity. - * specialized templated calls are defined where required - * (that is, object inherits from something other than `v3_funknown`) - * - * example usage: `v3_cpp_obj(obj)->method(obj, args...);` - */ -template static inline -constexpr T* v3_cpp_obj(T** obj) -{ - /** - * this ugly piece of code is required due to C++ assuming `reinterpret_cast` by default, - * but we need everything to be `static_cast` for it to be `constexpr` compatible. - */ - return static_cast(static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*3)); -} - -#else - -# ifndef constexpr -# define constexpr -# endif - +#if !defined(__cplusplus) && !defined(constexpr) +# define constexpr #endif /** @@ -177,8 +152,9 @@ static constexpr const v3_tuid v3_funknown_iid = */ struct v3_plugin_base { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* initialize)(void* self, struct v3_funknown** context); v3_result (V3_API* terminate)(void* self); }; @@ -188,6 +164,27 @@ static constexpr const v3_tuid v3_plugin_base_iid = #ifdef __cplusplus +/** + * cast object into its proper C++ type. + * this is needed because `struct v3_funknown;` on a C++ class does not inherit `v3_funknown`'s fields. + * + * we can use this as a little helper for keeping both C and C++ compatiblity. + * specialized templated calls are defined where required + * (that is, object inherits from something other than `v3_funknown`) + * + * example usage: `v3_cpp_obj(obj)->method(obj, args...);` + */ + +template static inline +constexpr T* v3_cpp_obj(T** obj) +{ + /** + * this ugly piece of code is required due to C++ assuming `reinterpret_cast` by default, + * but we need everything to be `static_cast` for it to be `constexpr` compatible. + */ + return static_cast(static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*3)); +} + /** * helper C++ functions to manually call v3_funknown methods on an object. */ @@ -210,4 +207,18 @@ uint32_t v3_cpp_obj_unref(T** obj) return static_cast(static_cast(*obj))->unref(obj); } +template static inline +v3_result v3_cpp_obj_initialize(T** obj, v3_funknown** context) +{ + return static_cast( + static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*3))->initialize(obj, context); +} + +template static inline +v3_result v3_cpp_obj_terminate(T** obj) +{ + return static_cast( + static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*3))->terminate(obj); +} + #endif diff --git a/source/includes/travesty/bstream.h b/source/includes/travesty/bstream.h index a1bfcd3f3..92bbc3b4e 100644 --- a/source/includes/travesty/bstream.h +++ b/source/includes/travesty/bstream.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * 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 @@ -25,8 +25,9 @@ enum v3_seek_mode { }; struct v3_bstream { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API *read)(void* self, void* buffer, int32_t num_bytes, int32_t* bytes_read); v3_result (V3_API *write)(void* self, void* buffer, int32_t num_bytes, int32_t* bytes_written); v3_result (V3_API *seek)(void* self, int64_t pos, int32_t seek_mode, int64_t* result); diff --git a/source/includes/travesty/component.h b/source/includes/travesty/component.h index 4e73c477f..652da36e2 100644 --- a/source/includes/travesty/component.h +++ b/source/includes/travesty/component.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * 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 @@ -89,8 +89,9 @@ struct v3_bus_info { struct v3_routing_info; struct v3_component { +#ifndef __cplusplus struct v3_plugin_base; - +#endif v3_result (V3_API *get_controller_class_id)(void* self, v3_tuid class_id); v3_result (V3_API *set_io_mode)(void* self, int32_t io_mode); int32_t (V3_API *get_bus_count)(void* self, int32_t media_type, int32_t bus_direction); diff --git a/source/includes/travesty/edit_controller.h b/source/includes/travesty/edit_controller.h index 055e7d71c..c83a285be 100644 --- a/source/includes/travesty/edit_controller.h +++ b/source/includes/travesty/edit_controller.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * 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 @@ -40,8 +40,9 @@ enum { }; struct v3_component_handler { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* begin_edit)(void* self, v3_param_id); v3_result (V3_API* perform_edit)(void* self, v3_param_id, double value_normalised); v3_result (V3_API* end_edit)(void* self, v3_param_id); @@ -51,6 +52,19 @@ struct v3_component_handler { static constexpr const v3_tuid v3_component_handler_iid = V3_ID(0x93A0BEA3, 0x0BD045DB, 0x8E890B0C, 0xC1E46AC6); +struct v3_component_handler2 { +#ifndef __cplusplus + struct v3_funknown; +#endif + v3_result (V3_API* set_dirty)(void* self, v3_bool state); + v3_result (V3_API* request_open_editor)(void* self, const char* name); + v3_result (V3_API* start_group_edit)(void* self); + v3_result (V3_API* finish_group_edit)(void* self); +}; + +static constexpr const v3_tuid v3_component_handler2_iid = + V3_ID(0xF040B4B3, 0xA36045EC, 0xABCDC045, 0xB4D5A2CC); + /** * edit controller */ @@ -77,11 +91,12 @@ struct v3_param_info { }; struct v3_edit_controller { +#ifndef __cplusplus struct v3_plugin_base; - - v3_result (V3_API* set_component_state)(void* self, struct v3_bstream*); - v3_result (V3_API* set_state)(void* self, struct v3_bstream*); - v3_result (V3_API* get_state)(void* self, struct v3_bstream*); +#endif + v3_result (V3_API* set_component_state)(void* self, struct v3_bstream**); + v3_result (V3_API* set_state)(void* self, struct v3_bstream**); + v3_result (V3_API* get_state)(void* self, struct v3_bstream**); int32_t (V3_API* get_parameter_count)(void* self); v3_result (V3_API* get_parameter_info)(void* self, int32_t param_idx, struct v3_param_info*); v3_result (V3_API* get_parameter_string_for_value)(void* self, v3_param_id, double normalised, v3_str_128 output); @@ -102,8 +117,9 @@ static constexpr const v3_tuid v3_edit_controller_iid = */ struct v3_midi_mapping { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* get_midi_controller_assignment)(void* self, int32_t bus, int16_t channel, int16_t cc, v3_param_id* id); }; diff --git a/source/includes/travesty/events.h b/source/includes/travesty/events.h index 72a4830a0..9c83bf84a 100644 --- a/source/includes/travesty/events.h +++ b/source/includes/travesty/events.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * 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 @@ -132,8 +132,9 @@ struct v3_event { */ struct v3_event_list { +#ifndef __cplusplus struct v3_funknown; - +#endif uint32_t (V3_API* get_event_count)(void* self); v3_result (V3_API* get_event)(void* self, int32_t idx, struct v3_event* event); v3_result (V3_API* add_event)(void* self, struct v3_event* event); @@ -142,4 +143,16 @@ struct v3_event_list { static constexpr const v3_tuid v3_event_list_iid = V3_ID(0x3A2C4214, 0x346349FE, 0xB2C4F397, 0xB9695A44); +#ifdef __cplusplus + +/** + * C++ variants + */ + +struct v3_event_list_cpp : v3_funknown { + v3_event_list list; +}; + +#endif + #include "align_pop.h" diff --git a/source/includes/travesty/factory.h b/source/includes/travesty/factory.h index adad83cdb..055b707df 100644 --- a/source/includes/travesty/factory.h +++ b/source/includes/travesty/factory.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * 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 @@ -37,8 +37,9 @@ struct v3_class_info { }; struct v3_plugin_factory { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API *get_factory_info)(void* self, struct v3_factory_info*); int32_t (V3_API *num_classes)(void* self); v3_result (V3_API *get_class_info)(void* self, int32_t idx, struct v3_class_info*); @@ -70,8 +71,9 @@ struct v3_class_info_2 { }; struct v3_plugin_factory_2 { +#ifndef __cplusplus struct v3_plugin_factory; - +#endif v3_result (V3_API *get_class_info_2)(void* self, int32_t idx, struct v3_class_info_2*); }; @@ -98,8 +100,9 @@ struct v3_class_info_3 { }; struct v3_plugin_factory_3 { +#ifndef __cplusplus struct v3_plugin_factory_2; - +#endif v3_result (V3_API *get_class_info_utf16)(void* self, int32_t idx, struct v3_class_info_3*); v3_result (V3_API *set_host_context)(void* self, struct v3_funknown** host); }; @@ -119,4 +122,26 @@ struct v3_plugin_factory_cpp : v3_funknown { v3_plugin_factory_3 v3; }; +template<> inline +constexpr v3_plugin_factory_2* v3_cpp_obj(v3_plugin_factory_2** obj) +{ + /** + * this ugly piece of code is required due to C++ assuming `reinterpret_cast` by default, + * but we need everything to be `static_cast` for it to be `constexpr` compatible. + */ + return static_cast( + static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*7)); +} + +template<> inline +constexpr v3_plugin_factory_3* v3_cpp_obj(v3_plugin_factory_3** obj) +{ + /** + * this ugly piece of code is required due to C++ assuming `reinterpret_cast` by default, + * but we need everything to be `static_cast` for it to be `constexpr` compatible. + */ + return static_cast( + static_cast(static_cast(static_cast(*obj)) + sizeof(void*)*8)); +} + #endif diff --git a/source/includes/travesty/host.h b/source/includes/travesty/host.h index 032a7eed8..bb6566bdb 100644 --- a/source/includes/travesty/host.h +++ b/source/includes/travesty/host.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * 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 @@ -21,12 +21,13 @@ #include "align_push.h" /** - * connection point + * host application */ struct v3_host_application { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* get_name)(void* self, v3_str_128 name); // wtf? v3_result (V3_API* create_instance)(void* self, v3_tuid cid, v3_tuid iid, void** obj); }; diff --git a/source/includes/travesty/message.h b/source/includes/travesty/message.h index 3cd77a684..3ab8f4612 100644 --- a/source/includes/travesty/message.h +++ b/source/includes/travesty/message.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * 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 @@ -25,8 +25,9 @@ */ struct v3_attribute_list { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* set_int)(void* self, const char* id, int64_t value); v3_result (V3_API* get_int)(void* self, const char* id, int64_t* value); v3_result (V3_API* set_float)(void* self, const char* id, double value); @@ -45,8 +46,9 @@ static constexpr const v3_tuid v3_attribute_list_iid = */ struct v3_message { +#ifndef __cplusplus struct v3_funknown; - +#endif const char* (V3_API* get_message_id)(void* self); void (V3_API* set_message_id)(void* self, const char* id); v3_attribute_list** (V3_API* get_attributes)(void* self); @@ -60,8 +62,9 @@ static constexpr const v3_tuid v3_message_iid = */ struct v3_connection_point { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* connect)(void* self, struct v3_connection_point** other); v3_result (V3_API* disconnect)(void* self, struct v3_connection_point** other); v3_result (V3_API* notify)(void* self, struct v3_message** message); diff --git a/source/includes/travesty/unit.h b/source/includes/travesty/unit.h new file mode 100644 index 000000000..77bc9adc6 --- /dev/null +++ b/source/includes/travesty/unit.h @@ -0,0 +1,69 @@ +/* + * travesty, pure C VST3-compatible interface + * Copyright (C) 2021-2022 Filipe Coelho + * + * 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. + */ + +#pragma once + +#include "base.h" + +#include "align_push.h" + +struct v3_unit_info { + int32_t id; // 0 for root unit + int32_t parent_unit_id; // -1 for none + v3_str_128 name; + int32_t program_list_id; // -1 for none +}; + +struct v3_program_list_info { + int32_t id; + v3_str_128 name; + int32_t programCount; +}; + +struct v3_unit_information { +#ifndef __cplusplus + struct v3_funknown; +#endif + int32_t (V3_API* get_unit_count)(void* self); + v3_result (V3_API* get_unit_info)(void* self, int32_t unit_idx, v3_unit_info* info); + int32_t (V3_API* get_program_list_count)(void* self); + v3_result (V3_API* get_program_list_info)(void* self, int32_t list_idx, v3_program_list_info* info); + v3_result (V3_API* get_program_name)(void* self, int32_t list_id, int32_t program_idx, v3_str_128 name); + v3_result (V3_API* get_program_info)(void* self, int32_t list_id, int32_t program_idx, const char *attribute_id, v3_str_128 attribute_value); + v3_result (V3_API* has_program_pitch_names)(void* self, int32_t list_id, int32_t program_idx); + v3_result (V3_API* get_program_pitch_name)(void* self, int32_t list_id, int32_t program_idx, int16_t midi_pitch, v3_str_128 name); + int32_t (V3_API* get_selected_unit)(void* self); + v3_result (V3_API* select_unit)(void* self, int32_t unit_id); + v3_result (V3_API* get_unit_by_bus)(void* self, int32_t type, int32_t bus_direction, int32_t bus_idx, int32_t channel, int32_t* unit_id); + v3_result (V3_API* set_unit_program_data)(void* self, int32_t list_or_unit_id, int32_t program_idx, struct v3_bstream** data); +}; + +static constexpr const v3_tuid v3_unit_information_iid = + V3_ID(0x3D4BD6B5, 0x913A4FD2, 0xA886E768, 0xA5EB92C1); + +#ifdef __cplusplus + +/** + * C++ variants + */ + +struct v3_unit_information_cpp : v3_funknown { + v3_unit_information unit; +}; + +#endif + +#include "align_pop.h" diff --git a/source/includes/travesty/view.h b/source/includes/travesty/view.h index 0abbd0558..2f5c698d6 100644 --- a/source/includes/travesty/view.h +++ b/source/includes/travesty/view.h @@ -1,6 +1,6 @@ /* * travesty, pure C VST3-compatible interface - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * 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 @@ -48,8 +48,9 @@ struct v3_view_rect { struct v3_plugin_frame; struct v3_plugin_view { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* is_platform_type_supported)(void* self, const char* platform_type); v3_result (V3_API* attached)(void* self, void* parent, const char* platform_type); v3_result (V3_API* removed)(void* self); @@ -72,8 +73,9 @@ static constexpr const v3_tuid v3_plugin_view_iid = */ struct v3_plugin_frame { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* resize_view)(void* self, struct v3_plugin_view**, struct v3_view_rect*); }; @@ -86,8 +88,9 @@ static constexpr const v3_tuid v3_plugin_frame_iid = */ struct v3_plugin_view_content_scale { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* set_content_scale_factor)(void* self, float factor); }; @@ -99,8 +102,9 @@ static constexpr const v3_tuid v3_plugin_view_content_scale_iid = */ struct v3_plugin_view_parameter_finder { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* find_parameter)(void* self, int32_t x, int32_t y, v3_param_id *); }; @@ -112,8 +116,9 @@ static constexpr const v3_tuid v3_plugin_view_parameter_finder_iid = */ struct v3_event_handler { +#ifndef __cplusplus struct v3_funknown; - +#endif void (V3_API* on_fd_is_set)(void* self, int fd); }; @@ -125,8 +130,9 @@ static constexpr const v3_tuid v3_event_handler_iid = */ struct v3_timer_handler { +#ifndef __cplusplus struct v3_funknown; - +#endif void (V3_API* on_timer)(void* self); }; @@ -138,8 +144,9 @@ static constexpr const v3_tuid v3_timer_handler_iid = */ struct v3_run_loop { +#ifndef __cplusplus struct v3_funknown; - +#endif v3_result (V3_API* register_event_handler)(void* self, v3_event_handler** handler, int fd); v3_result (V3_API* unregister_event_handler)(void* self, v3_event_handler** handler); v3_result (V3_API* register_timer)(void* self, v3_timer_handler** handler, uint64_t ms); @@ -176,7 +183,7 @@ struct v3_event_handler_cpp : v3_funknown { }; struct v3_timer_handler_cpp : v3_funknown { - v3_timer_handler handler; + v3_timer_handler timer; }; struct v3_run_loop_cpp : v3_funknown { diff --git a/source/utils/CarlaMacUtils.cpp b/source/utils/CarlaMacUtils.cpp index abf7d9216..c9e3a2a1b 100644 --- a/source/utils/CarlaMacUtils.cpp +++ b/source/utils/CarlaMacUtils.cpp @@ -92,6 +92,42 @@ AutoNSAutoreleasePool::~AutoNSAutoreleasePool() // -------------------------------------------------------------------------------------------------------------------- +BundleLoader::BundleLoader() noexcept + : ref(nullptr), + refNum(0) {} + +BundleLoader::~BundleLoader() +{ + if (ref == nullptr) + return; + + CFBundleCloseBundleResourceMap(ref, refNum); + CFBundleUnloadExecutable(ref); + CFRelease(ref); +} + +bool BundleLoader::load(const char* const filename) +{ + const CFURLRef urlRef = CFURLCreateFromFileSystemRepresentation(0, (const UInt8*)filename, (CFIndex)std::strlen(filename), true); + CARLA_SAFE_ASSERT_RETURN(urlRef != nullptr, false); + + ref = CFBundleCreate(kCFAllocatorDefault, urlRef); + CFRelease(urlRef); + CARLA_SAFE_ASSERT_RETURN(ref != nullptr, false); + + if (! CFBundleLoadExecutable(ref)) + { + CFRelease(ref); + ref = nullptr; + return false; + } + + refNum = CFBundleOpenBundleResourceMap(ref); + return true; +} + +// -------------------------------------------------------------------------------------------------------------------- + CARLA_BACKEND_END_NAMESPACE #endif // CARLA_OS_MAC diff --git a/source/utils/CarlaMacUtils.hpp b/source/utils/CarlaMacUtils.hpp index 84448c1b6..c9e9d8c1c 100644 --- a/source/utils/CarlaMacUtils.hpp +++ b/source/utils/CarlaMacUtils.hpp @@ -43,6 +43,8 @@ const char* findBinaryInBundle(const char* const bundleDir); */ bool removeFileFromQuarantine(const char* const filename); +// -------------------------------------------------------------------------------------------------------------------- + /* * ... */ @@ -57,6 +59,17 @@ private: // -------------------------------------------------------------------------------------------------------------------- +struct BundleLoader { + CFBundleRef ref; + CFBundleRefNum refNum; + + BundleLoader() noexcept; + ~BundleLoader(); + bool load(const char* const filename); +}; + +// -------------------------------------------------------------------------------------------------------------------- + CARLA_BACKEND_END_NAMESPACE #endif // CARLA_MAC_UTILS_HPP_INCLUDED diff --git a/source/utils/CarlaVst3Utils.hpp b/source/utils/CarlaVst3Utils.hpp index 9fd17bac6..75ddf514e 100644 --- a/source/utils/CarlaVst3Utils.hpp +++ b/source/utils/CarlaVst3Utils.hpp @@ -1,6 +1,6 @@ /* * Carla VST3 utils - * Copyright (C) 2021 Filipe Coelho + * Copyright (C) 2021-2022 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -18,9 +18,137 @@ #ifndef CARLA_VST3_UTILS_HPP_INCLUDED #define CARLA_VST3_UTILS_HPP_INCLUDED +#include "CarlaBackend.h" #include "CarlaUtils.hpp" +#include "travesty/audio_processor.h" #include "travesty/component.h" #include "travesty/edit_controller.h" +#include "travesty/factory.h" +#include "travesty/host.h" + +#if !(defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)) + #if defined(__aarch64__) || defined(__arm64__) + #define V3_ARCHITECTURE "aarch64" + #elif defined(__TARGET_ARCH_ARM) + #if __TARGET_ARCH_ARM == 9 + #define V3_ARCHITECTURE "armv9l" + #elif __TARGET_ARCH_ARM == 8 + #define V3_ARCHITECTURE "armv8l" + #elif __TARGET_ARCH_ARM == 7 + #define V3_ARCHITECTURE "armv7l" + #elif _TARGET_ARCH_ARM == 6 + #define V3_ARCHITECTURE "armv6l" + #elif __TARGET_ARCH_ARM == 5 + #define V3_ARCHITECTURE "armv5l" + #else + #define V3_ARCHITECTURE "arm" + #endif + #elif defined(__arm__) + #define V3_ARCHITECTURE "arm" + #elif defined(__x86_64) || defined(__x86_64__) || defined(__amd64) + #define V3_ARCHITECTURE "x86_64" + #elif defined(__i386) || defined(__i386__) + #define V3_ARCHITECTURE "i386" + #elif defined(__ia64) || defined(__ia64__) + #define V3_ARCHITECTURE "ia64" + #elif defined(__mips64) + #define V3_ARCHITECTURE "mips64" + #elif defined(__mips) || defined(__mips__) + #define V3_ARCHITECTURE "mips" + #elif defined(__ppc64) || defined(__ppc64__) || defined(__powerpc64__) + #define V3_ARCHITECTURE "ppc64" + #elif defined(__ppc) || defined(__ppc__) || defined(__powerpc__) + #define V3_ARCHITECTURE "ppc" + #elif defined(__riscv) + #if __riscv_xlen == 64 + #define V3_ARCHITECTURE "riscv64" + #else + #define V3_ARCHITECTURE "riscv" + #endif + #else + #define V3_ARCHITECTURE "unknown" + #endif + #if defined(__HAIKU__) + #define V3_PLATFORM "haiku" + #elif defined(__linux__) || defined(__linux) + #define V3_PLATFORM "linux" + #elif defined(__FreeBSD__) + #define V3_PLATFORM "freebsd" + #elif defined(__NetBSD__) + #define V3_PLATFORM "netbsd" + #elif defined(__OpenBSD__) + #define V3_PLATFORM "openbsd" + #elif defined(__GNU__) + #define V3_PLATFORM "hurd" + #else + #define V3_PLATFORM "unknown" + #endif +#endif + +#if defined(CARLA_OS_MAC) + #define V3_CONTENT_DIR "MacOS" +#elif defined(_M_ARM64) + #define V3_CONTENT_DIR "aarch64-win" /* TBD */ +#elif defined(_M_ARM) + #define V3_CONTENT_DIR "arm-win" /* TBD */ +#elif defined(CARLA_OS_WIN64) + #define V3_CONTENT_DIR "x86_64-win" +#elif defined(CARLA_OS_WIN32) + #define V3_CONTENT_DIR "x86-win" +#else + #define V3_CONTENT_DIR V3_ARCHITECTURE "-" V3_PLATFORM +#endif + +#if defined(CARLA_OS_MAC) +# define V3_ENTRYFNNAME "bundleEntry" +# define V3_EXITFNNAME "bundleExit" +typedef void (*V3_ENTRYFN)(void*); +typedef void (*V3_EXITFN)(void); +#elif defined(CARLA_OS_WIN) +# define V3_ENTRYFNNAME "InitDll" +# define V3_EXITFNNAME "ExitDll" +typedef void (*V3_ENTRYFN)(void); +typedef void (*V3_EXITFN)(void); +#else +# define V3_ENTRYFNNAME "ModuleEntry" +# define V3_EXITFNNAME "ModuleExit" +typedef void (*V3_ENTRYFN)(void*); +typedef void (*V3_EXITFN)(void); +#endif + +#define V3_GETFNNAME "GetPluginFactory" +typedef v3_plugin_factory** (*V3_GETFN)(void); + +CARLA_BACKEND_START_NAMESPACE + +static inline +PluginCategory getPluginCategoryFromV3SubCategories(const char* const subcategories) +{ + if (std::strstr(subcategories, "Instrument") != nullptr) + return PLUGIN_CATEGORY_SYNTH; + + return subcategories[0] != '\0' ? PLUGIN_CATEGORY_OTHER : PLUGIN_CATEGORY_NONE; +} + +static inline constexpr +uint32_t v3_cconst(const uint8_t a, const uint8_t b, const uint8_t c, const uint8_t d) noexcept +{ + return static_cast((a << 24) | (b << 16) | (c << 8) | (d << 0)); +} + +static inline +const char* tuid2str(const v3_tuid iid) +{ + static char buf[44]; + std::snprintf(buf, sizeof(buf), "0x%08X,0x%08X,0x%08X,0x%08X", + v3_cconst(iid[ 0], iid[ 1], iid[ 2], iid[ 3]), + v3_cconst(iid[ 4], iid[ 5], iid[ 6], iid[ 7]), + v3_cconst(iid[ 8], iid[ 9], iid[10], iid[11]), + v3_cconst(iid[12], iid[13], iid[14], iid[15])); + return buf; +} + +CARLA_BACKEND_END_NAMESPACE #endif // CARLA_VST3_UTILS_HPP_INCLUDED