diff --git a/source/backend/plugin/CarlaPluginVST3.cpp b/source/backend/plugin/CarlaPluginVST3.cpp index e9439b931..8df91caa9 100644 --- a/source/backend/plugin/CarlaPluginVST3.cpp +++ b/source/backend/plugin/CarlaPluginVST3.cpp @@ -570,7 +570,6 @@ private: } CARLA_DECLARE_NON_COPYABLE(carla_v3_input_param_value_queue) - // CARLA_PREVENT_HEAP_ALLOCATION }; struct carla_v3_input_param_changes : v3_param_changes_cpp { @@ -606,10 +605,7 @@ struct carla_v3_input_param_changes : v3_param_changes_cpp { carla_zeroStructs(updatedParams, paramCount); for (uint32_t i=0; i(paramData.data[i].rindex)); - } } ~carla_v3_input_param_changes() @@ -674,7 +670,7 @@ private: return me->pluginExposedQueue[index]; } - static v3_param_value_queue** V3_API add_param_data(void*, v3_param_id*, int32_t*) + static v3_param_value_queue** V3_API add_param_data(void*, const v3_param_id*, int32_t*) { // there is nothing here for input parameters, plugins are not meant to call this! return nullptr; @@ -685,8 +681,88 @@ private: // -------------------------------------------------------------------------------------------------------------------- +struct carla_v3_output_param_value_queue : v3_param_value_queue_cpp { + const v3_param_id paramId; + bool used; + int32_t offset; + double value; + + carla_v3_output_param_value_queue(const v3_param_id pId) + : paramId(pId), + used(false), + offset(0), + value(0.0) + { + query_interface = v3_query_interface_static; + ref = v3_ref_static; + unref = v3_unref_static; + queue.get_param_id = get_param_id; + queue.get_point_count = get_point_count; + queue.get_point = get_point; + queue.add_point = add_point; + } + + void init() + { + used = false; + offset = 0; + value = 0.0; + } + +private: + static v3_param_id V3_API get_param_id(void* self) + { + carla_v3_output_param_value_queue* const me = *static_cast(self); + return me->paramId; + } + + static int32_t V3_API get_point_count(void* self) + { + carla_v3_output_param_value_queue* const me = *static_cast(self); + return me->used ? 1 : 0; + } + + static v3_result V3_API get_point(void* const self, + const int32_t index, int32_t* const sample_offset, double* const value) + { + carla_v3_output_param_value_queue* const me = *static_cast(self); + CARLA_SAFE_ASSERT_RETURN(me->used, V3_INVALID_ARG); + CARLA_SAFE_ASSERT_INT_RETURN(index == 0, index, V3_INVALID_ARG); + + *sample_offset = me->offset; + *value = me->value; + return V3_OK; + } + + static v3_result V3_API add_point(void* const self, + const int32_t sample_offset, const double value, int32_t* const index) + { + carla_v3_output_param_value_queue* const me = *static_cast(self); + CARLA_SAFE_ASSERT_INT_RETURN(sample_offset >= 0, sample_offset, V3_INVALID_ARG); + CARLA_SAFE_ASSERT_RETURN(value >= 0 && value <= 1, V3_INVALID_ARG); + CARLA_SAFE_ASSERT_RETURN(index != nullptr, V3_INVALID_ARG); + + me->offset = sample_offset; + me->value = value; + *index = 0; + return V3_OK; + } + + CARLA_DECLARE_NON_COPYABLE(carla_v3_output_param_value_queue) +}; + struct carla_v3_output_param_changes : v3_param_changes_cpp { - carla_v3_output_param_changes() + const uint32_t numParameters; + int32_t numParametersUsed; + bool* const parametersUsed; + carla_v3_output_param_value_queue** const queue; + std::unordered_map paramIds; + + carla_v3_output_param_changes(const PluginParameterData& paramData) + : numParameters(paramData.count), + numParametersUsed(0), + parametersUsed(new bool[paramData.count]), + queue(new carla_v3_output_param_value_queue*[paramData.count]) { query_interface = v3_query_interface_static; ref = v3_ref_static; @@ -694,24 +770,63 @@ struct carla_v3_output_param_changes : v3_param_changes_cpp { changes.get_param_count = get_param_count; changes.get_param_data = get_param_data; changes.add_param_data = add_param_data; + + carla_zeroStructs(parametersUsed, numParameters); + + for (uint32_t i=0; i(self); + CARLA_SAFE_ASSERT_RETURN(paramIdPtr != nullptr, nullptr); + + const v3_param_id paramId = *paramIdPtr; + + if (me->paramIds.find(paramId) == me->paramIds.end()) + return nullptr; + + const int32_t paramIndex = me->paramIds[paramId]; + CARLA_SAFE_ASSERT_RETURN(!me->parametersUsed[paramIndex], nullptr); + + *index = me->numParametersUsed++; + me->parametersUsed[paramIndex] = true; + me->queue[paramIndex]->init(); + + return (v3_param_value_queue**)&me->queue[paramIndex]; } CARLA_DECLARE_NON_COPYABLE(carla_v3_output_param_changes) @@ -2017,7 +2132,7 @@ public: if (numParameters > 0) { fEvents.paramInputs = new carla_v3_input_param_changes(pData->param); - fEvents.paramOutputs = new carla_v3_output_param_changes; + fEvents.paramOutputs = new carla_v3_output_param_changes(pData->param); } if (needsCtrlIn) @@ -2701,6 +2816,45 @@ public: v3_cpp_obj(fV3.processor)->process(fV3.processor, &processData); } CARLA_SAFE_EXCEPTION("process"); + // ------------------------------------------------------------------------------------------------------------ + // Handle parameter outputs + + if (fEvents.paramOutputs != nullptr && fEvents.paramOutputs->numParametersUsed != 0) + { + uint8_t channel; + uint16_t param; + + for (uint32_t i=0; i < pData->param.count; ++i) + { + if (fEvents.paramOutputs->parametersUsed[i]) + { + carla_v3_output_param_value_queue* const queue = fEvents.paramOutputs->queue[i]; + const v3_param_id paramId = pData->param.data[i].rindex; + + const float value = v3_cpp_obj(fV3.controller)->normalised_parameter_to_plain(fV3.controller, + paramId, + queue->value); + + pData->postponeParameterChangeRtEvent(true, static_cast(i), value); + + if (pData->param.data[i].type == PARAMETER_OUTPUT && pData->param.data[i].mappedControlIndex > 0) + { + channel = pData->param.data[i].midiChannel; + param = static_cast(pData->param.data[i].mappedControlIndex); + + pData->event.portOut->writeControlEvent(queue->offset, + channel, + kEngineControlEventTypeParameter, + param, + -1, + queue->value); + } + } + } + } + + pData->postRtEvents.trySplice(); + fEvents.init(); #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH @@ -3587,6 +3741,9 @@ private: { if (paramInputs != nullptr) paramInputs->prepare(); + + if (paramOutputs != nullptr) + paramOutputs->prepare(); } CARLA_DECLARE_NON_COPYABLE(Events) diff --git a/source/discovery/carla-discovery.cpp b/source/discovery/carla-discovery.cpp index bbfc3f3c3..28ffdf324 100644 --- a/source/discovery/carla-discovery.cpp +++ b/source/discovery/carla-discovery.cpp @@ -1476,7 +1476,7 @@ struct carla_v3_param_changes : v3_param_changes_cpp { private: static int32_t V3_API carla_get_param_count(void*) { return 0; } static v3_param_value_queue** V3_API carla_get_param_data(void*, int32_t) { return nullptr; } - static v3_param_value_queue** V3_API carla_add_param_data(void*, v3_param_id*, int32_t*) { return nullptr; } + static v3_param_value_queue** V3_API carla_add_param_data(void*, const v3_param_id*, int32_t*) { return nullptr; } CARLA_DECLARE_NON_COPYABLE(carla_v3_param_changes) CARLA_PREVENT_HEAP_ALLOCATION diff --git a/source/includes/travesty/audio_processor.h b/source/includes/travesty/audio_processor.h index 8783d8e5c..f4b4ed069 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-2022 Filipe Coelho + * Copyright (C) 2021-2023 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 @@ -108,7 +108,7 @@ struct v3_param_changes { #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); + struct v3_param_value_queue** (V3_API* add_param_data)(void* self, const v3_param_id* id, int32_t* idx); }; static constexpr const v3_tuid v3_param_changes_iid =