From 74c6c55fe6bd3eaf5a54677451d102c5d2b34cd2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 27 Jul 2025 19:02:00 +0200 Subject: [PATCH] Add Quad-Panner internal plugin, cleanup Signed-off-by: falkTX --- source/native-plugins/Makefile | 1 + source/native-plugins/_all.c | 24 +-- source/native-plugins/_data.cpp | 34 ++-- source/native-plugins/audio-gain.c | 23 +-- source/native-plugins/midi-to-cv.c | 26 +-- source/native-plugins/quadpanner.c | 280 +++++++++++++++++++++++++++++ 6 files changed, 315 insertions(+), 73 deletions(-) create mode 100644 source/native-plugins/quadpanner.c diff --git a/source/native-plugins/Makefile b/source/native-plugins/Makefile index bc010395f..4fbe4bbd2 100644 --- a/source/native-plugins/Makefile +++ b/source/native-plugins/Makefile @@ -40,6 +40,7 @@ OBJS = \ $(OBJDIR)/bypass.c.o \ $(OBJDIR)/cv-to-audio.c.o \ $(OBJDIR)/lfo.c.o \ + $(OBJDIR)/quadpanner.c.o \ $(OBJDIR)/midi-channel-filter.c.o \ $(OBJDIR)/midi-channel-ab.c.o \ $(OBJDIR)/midi-channelize.c.o \ diff --git a/source/native-plugins/_all.c b/source/native-plugins/_all.c index 955f2daed..2397bd63b 100644 --- a/source/native-plugins/_all.c +++ b/source/native-plugins/_all.c @@ -1,19 +1,5 @@ -/* - * Carla Native Plugins - * Copyright (C) 2012-2020 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 - * published by the Free Software Foundation; either version 2 of - * the License, or any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * For a full copy of the GNU General Public License see the doc/GPL.txt file. - */ +// SPDX-FileCopyrightText: 2011-2025 Filipe Coelho +// SPDX-License-Identifier: GPL-2.0-or-later #include "CarlaDefines.h" #include "CarlaNative.h" @@ -25,6 +11,7 @@ extern void carla_register_native_plugin_audiogain(void); extern void carla_register_native_plugin_bypass(void); extern void carla_register_native_plugin_cv2audio(void); extern void carla_register_native_plugin_lfo(void); +extern void carla_register_native_plugin_quadpanner(void); extern void carla_register_native_plugin_midi2cv(void); extern void carla_register_native_plugin_midichanab(void); extern void carla_register_native_plugin_midichanfilter(void); @@ -38,15 +25,15 @@ extern void carla_register_native_plugin_miditranspose(void); // Audio file extern void carla_register_native_plugin_audiofile(void); -// MIDI file and sequencer +// MIDI file extern void carla_register_native_plugin_midifile(void); -extern void carla_register_native_plugin_midipattern(void); // Carla extern void carla_register_native_plugin_carla(void); // External-UI plugins extern void carla_register_native_plugin_bigmeter(void); +extern void carla_register_native_plugin_midipattern(void); extern void carla_register_native_plugin_notes(void); extern void carla_register_native_plugin_xycontroller(void); @@ -65,6 +52,7 @@ void carla_register_all_native_plugins(void) carla_register_native_plugin_bypass(); carla_register_native_plugin_cv2audio(); carla_register_native_plugin_lfo(); + carla_register_native_plugin_quadpanner(); carla_register_native_plugin_midi2cv(); carla_register_native_plugin_midichanab(); carla_register_native_plugin_midichannelize(); diff --git a/source/native-plugins/_data.cpp b/source/native-plugins/_data.cpp index f19c9e633..3920438ab 100644 --- a/source/native-plugins/_data.cpp +++ b/source/native-plugins/_data.cpp @@ -1,19 +1,5 @@ -/* - * Carla Native Plugins - * Copyright (C) 2012-2020 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 - * published by the Free Software Foundation; either version 2 of - * the License, or any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * For a full copy of the GNU General Public License see the doc/GPL.txt file. - */ +// SPDX-FileCopyrightText: 2011-2025 Filipe Coelho +// SPDX-License-Identifier: GPL-2.0-or-later #include "CarlaNative.h" #include "CarlaMIDI.h" @@ -122,6 +108,22 @@ static const NativePluginDescriptor sNativePluginDescriptors[] = { /* copyright */ "GNU GPL v2+", DESCFUNCS_WITHOUTCV }, +{ + /* category */ NATIVE_PLUGIN_CATEGORY_UTILITY, + /* hints */ NATIVE_PLUGIN_IS_RTSAFE, + /* supports */ NATIVE_PLUGIN_SUPPORTS_NOTHING, + /* audioIns */ 4, + /* audioOuts */ 4, + /* midiIns */ 0, + /* midiOuts */ 0, + /* paramIns */ 2, + /* paramOuts */ 0, + /* name */ "Quad-Panner", + /* label */ "quadpanner", + /* maker */ "falkTX", + /* copyright */ "GNU GPL v2+", + DESCFUNCS_WITHOUTCV +}, { /* category */ NATIVE_PLUGIN_CATEGORY_UTILITY, /* hints */ NATIVE_PLUGIN_IS_RTSAFE, diff --git a/source/native-plugins/audio-gain.c b/source/native-plugins/audio-gain.c index d69a6f277..4115b6c35 100644 --- a/source/native-plugins/audio-gain.c +++ b/source/native-plugins/audio-gain.c @@ -1,22 +1,7 @@ -/* - * Carla Native Plugins - * Copyright (C) 2012-2019 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 - * published by the Free Software Foundation; either version 2 of - * the License, or any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * For a full copy of the GNU General Public License see the doc/GPL.txt file. - */ +// SPDX-FileCopyrightText: 2011-2025 Filipe Coelho +// SPDX-License-Identifier: GPL-2.0-or-later #include "CarlaNative.h" -#include "CarlaMIDI.h" #include #include @@ -203,8 +188,8 @@ void handle_audio_buffers(const float* inBuffer, float* outBuffer, Filter* const // FIXME for v3.0, use const for the input buffer static void audiogain_process(NativePluginHandle handle, - float** inBuffer, float** outBuffer, uint32_t frames, - const NativeMidiEvent* midiEvents, uint32_t midiEventCount) + float** inBuffer, float** outBuffer, uint32_t frames, + const NativeMidiEvent* midiEvents, uint32_t midiEventCount) { const float gain = handlePtr->gain; const bool applyLeft = handlePtr->applyLeft; diff --git a/source/native-plugins/midi-to-cv.c b/source/native-plugins/midi-to-cv.c index a1063af05..36e053198 100644 --- a/source/native-plugins/midi-to-cv.c +++ b/source/native-plugins/midi-to-cv.c @@ -1,19 +1,5 @@ -/* - * Carla Native Plugins - * Copyright (C) 2012-2019 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 - * published by the Free Software Foundation; either version 2 of - * the License, or any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * For a full copy of the GNU General Public License see the doc/GPL.txt file. - */ +// SPDX-FileCopyrightText: 2011-2025 Filipe Coelho +// SPDX-License-Identifier: GPL-2.0-or-later /* This plugin code is based on MOD Devices' midi-to-cv-mono by Bram Giesen and Jarno Verheesen */ @@ -385,9 +371,6 @@ static const NativePluginDescriptor midi2cvDesc = { .set_midi_program = NULL, .set_custom_data = NULL, - .get_buffer_port_name = midi2cv_get_buffer_port_name, - .get_buffer_port_range = midi2cv_get_buffer_port_range, - .ui_show = NULL, .ui_idle = NULL, @@ -404,7 +387,10 @@ static const NativePluginDescriptor midi2cvDesc = { .dispatcher = NULL, - .render_inline_display = NULL + .render_inline_display = NULL, + + .get_buffer_port_name = midi2cv_get_buffer_port_name, + .get_buffer_port_range = midi2cv_get_buffer_port_range, }; // ----------------------------------------------------------------------- diff --git a/source/native-plugins/quadpanner.c b/source/native-plugins/quadpanner.c new file mode 100644 index 000000000..1097d60f6 --- /dev/null +++ b/source/native-plugins/quadpanner.c @@ -0,0 +1,280 @@ +// SPDX-FileCopyrightText: 2011-2025 Filipe Coelho +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "CarlaNative.h" + +#include +#include +#include +#include + +// ----------------------------------------------------------------------- + +typedef struct { + float a0, b1, z1; +} Filter; + +static inline +void set_filter_sample_rate(Filter* const filter, const float sampleRate) +{ + const float frequency = 30.0f / sampleRate; + + filter->b1 = expf(-2.0f * (float)M_PI * frequency); + filter->a0 = 1.0f - filter->b1; + filter->z1 = 0.0f; +} + +// ----------------------------------------------------------------------- + +typedef enum { + PARAM_LEFT_RIGHT = 0, + PARAM_FRONT_REAR, + PARAM_COUNT +} QuadPannerParams; + +typedef struct { + Filter lowpass[PARAM_COUNT * 2]; + float params[PARAM_COUNT]; +} QuadPannerHandle; + +// ----------------------------------------------------------------------- + +static NativePluginHandle quadpanner_instantiate(const NativeHostDescriptor* host) +{ + QuadPannerHandle* const handle = calloc(1, sizeof(QuadPannerHandle)); + + if (handle == NULL) + return NULL; + + const float sampleRate = host->get_sample_rate(host->handle); + + for (unsigned i = 0; i < PARAM_COUNT * 2; ++i) + set_filter_sample_rate(&handle->lowpass[i], sampleRate); + + return handle; +} + +#define handlePtr ((QuadPannerHandle*)handle) + +static void quadpanner_cleanup(NativePluginHandle handle) +{ + free(handlePtr); +} + +static uint32_t quadpanner_get_parameter_count(NativePluginHandle handle) +{ + return PARAM_COUNT; + + // unused + (void)handle; +} + +static const NativeParameter* quadpanner_get_parameter_info(NativePluginHandle handle, uint32_t index) +{ + if (index > PARAM_COUNT) + return NULL; + + static NativeParameter param; + + param.hints = NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_AUTOMATABLE; + param.unit = "%"; + param.ranges.def = 0.0f; + param.ranges.min = -100.0f; + param.ranges.max = 100.0f; + param.ranges.step = 1; + param.ranges.stepSmall = 1; + param.ranges.stepLarge = 10; + + switch (index) + { + case PARAM_LEFT_RIGHT: + param.name = "Left/Right"; + break; + case PARAM_FRONT_REAR: + param.name = "Front/Rear"; + break; + } + + return ¶m; + + // unused + (void)handle; +} + +static float quadpanner_get_parameter_value(NativePluginHandle handle, uint32_t index) +{ + return handlePtr->params[index]; +} + +static void quadpanner_set_parameter_value(NativePluginHandle handle, uint32_t index, float value) +{ + handlePtr->params[index] = value; +} + +static inline +void handle_audio_buffers(const float* inBuffer, + float* outBuffer, + Filter* const filter, + const float gain, + const uint32_t frames) +{ + const float a0 = filter->a0; + const float b1 = filter->b1; + float z1 = filter->z1; + + for (uint32_t i = 0; i < frames; ++i) { + z1 = gain * a0 + z1 * b1; + *outBuffer++ = *inBuffer++ * z1; + } + + filter->z1 = z1; +} + +// FIXME for v3.0, use const for the input buffer +static void quadpanner_process(NativePluginHandle handle, + float** inBuffer, float** outBuffer, uint32_t frames, + const NativeMidiEvent* midiEvents, uint32_t midiEventCount) +{ + float v1, v2, tmp; + + // left/right + if ((tmp = handlePtr->params[PARAM_LEFT_RIGHT]) < 0.f) + { + v1 = 1.f; + v2 = 1.f - tmp * -0.01f; + } + else + { + v1 = 1.f - tmp * 0.01f; + v2 = 1.f; + } + handle_audio_buffers(inBuffer[0], outBuffer[0], &handlePtr->lowpass[0], v1, frames); + handle_audio_buffers(inBuffer[1], outBuffer[1], &handlePtr->lowpass[1], v2, frames); + + // front/rear + if ((tmp = handlePtr->params[PARAM_FRONT_REAR]) < 0.f) + { + v1 = 1.f; + v2 = 1.f - tmp * -0.01f; + } + else + { + v1 = 1.f - tmp * 0.01f; + v2 = 1.f; + } + handle_audio_buffers(inBuffer[2], outBuffer[2], &handlePtr->lowpass[2], v1, frames); + handle_audio_buffers(inBuffer[3], outBuffer[3], &handlePtr->lowpass[3], v2, frames); + + return; + + // unused + (void)midiEvents; + (void)midiEventCount; +} + +static intptr_t quadpanner_dispatcher(NativePluginHandle handle, NativePluginDispatcherOpcode opcode, int32_t index, intptr_t value, void* ptr, float opt) +{ + switch (opcode) + { + case NATIVE_PLUGIN_OPCODE_SAMPLE_RATE_CHANGED: + for (unsigned i = 0; i < PARAM_COUNT * 2; ++i) + set_filter_sample_rate(&handlePtr->lowpass[i], opt); + break; + default: + break; + } + + return 0; + + // unused + (void)index; + (void)value; + (void)ptr; +} + +static const char* quadpanner_get_buffer_port_name(NativePluginHandle handle, uint32_t index, bool isOutput) +{ + static const char* const kInNames[4] = { + "In-Left", + "In-Right", + "In-Front", + "In-Rear", + }; + static const char* const kOutNames[4] = { + "Out-Left", + "Out-Right", + "Out-Front", + "Out-Rear", + }; + + return isOutput ? kOutNames[index] : kInNames[index]; + + // unused + (void)handle; +} + +// ----------------------------------------------------------------------- + +static const NativePluginDescriptor quadpannerDesc = { + .category = NATIVE_PLUGIN_CATEGORY_UTILITY, + .hints = NATIVE_PLUGIN_IS_RTSAFE, + .supports = NATIVE_PLUGIN_SUPPORTS_NOTHING, + .audioIns = 4, + .audioOuts = 4, + .cvIns = 0, + .cvOuts = 0, + .midiIns = 0, + .midiOuts = 0, + .paramIns = PARAM_COUNT, + .paramOuts = 0, + .name = "Quad-Panner", + .label = "quadpanner", + .maker = "falkTX", + .copyright = "GNU GPL v2+", + + .instantiate = quadpanner_instantiate, + .cleanup = quadpanner_cleanup, + + .get_parameter_count = quadpanner_get_parameter_count, + .get_parameter_info = quadpanner_get_parameter_info, + .get_parameter_value = quadpanner_get_parameter_value, + + .get_midi_program_count = NULL, + .get_midi_program_info = NULL, + + .set_parameter_value = quadpanner_set_parameter_value, + .set_midi_program = NULL, + .set_custom_data = NULL, + + .ui_show = NULL, + .ui_idle = NULL, + + .ui_set_parameter_value = NULL, + .ui_set_midi_program = NULL, + .ui_set_custom_data = NULL, + + .activate = NULL, + .deactivate = NULL, + .process = quadpanner_process, + + .get_state = NULL, + .set_state = NULL, + + .dispatcher = quadpanner_dispatcher, + + .render_inline_display = NULL, + + .get_buffer_port_name = quadpanner_get_buffer_port_name, + .get_buffer_port_range = NULL, +}; + +// ----------------------------------------------------------------------- + +void carla_register_native_plugin_quadpanner(void); + +void carla_register_native_plugin_quadpanner(void) +{ + carla_register_native_plugin(&quadpannerDesc); +} + +// -----------------------------------------------------------------------