Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

281 lines
6.9KB

  1. // SPDX-FileCopyrightText: 2011-2025 Filipe Coelho <falktx@falktx.com>
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "CarlaNative.h"
  4. #include <math.h>
  5. #include <stdbool.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. // -----------------------------------------------------------------------
  9. typedef struct {
  10. float a0, b1, z1;
  11. } Filter;
  12. static inline
  13. void set_filter_sample_rate(Filter* const filter, const float sampleRate)
  14. {
  15. const float frequency = 30.0f / sampleRate;
  16. filter->b1 = expf(-2.0f * (float)M_PI * frequency);
  17. filter->a0 = 1.0f - filter->b1;
  18. filter->z1 = 0.0f;
  19. }
  20. // -----------------------------------------------------------------------
  21. typedef enum {
  22. PARAM_LEFT_RIGHT = 0,
  23. PARAM_FRONT_REAR,
  24. PARAM_COUNT
  25. } QuadPannerParams;
  26. typedef struct {
  27. Filter lowpass[PARAM_COUNT * 2];
  28. float params[PARAM_COUNT];
  29. } QuadPannerHandle;
  30. // -----------------------------------------------------------------------
  31. static NativePluginHandle quadpanner_instantiate(const NativeHostDescriptor* host)
  32. {
  33. QuadPannerHandle* const handle = calloc(1, sizeof(QuadPannerHandle));
  34. if (handle == NULL)
  35. return NULL;
  36. const float sampleRate = host->get_sample_rate(host->handle);
  37. for (unsigned i = 0; i < PARAM_COUNT * 2; ++i)
  38. set_filter_sample_rate(&handle->lowpass[i], sampleRate);
  39. return handle;
  40. }
  41. #define handlePtr ((QuadPannerHandle*)handle)
  42. static void quadpanner_cleanup(NativePluginHandle handle)
  43. {
  44. free(handlePtr);
  45. }
  46. static uint32_t quadpanner_get_parameter_count(NativePluginHandle handle)
  47. {
  48. return PARAM_COUNT;
  49. // unused
  50. (void)handle;
  51. }
  52. static const NativeParameter* quadpanner_get_parameter_info(NativePluginHandle handle, uint32_t index)
  53. {
  54. if (index > PARAM_COUNT)
  55. return NULL;
  56. static NativeParameter param;
  57. param.hints = NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_AUTOMATABLE;
  58. param.unit = "%";
  59. param.ranges.def = 0.0f;
  60. param.ranges.min = -100.0f;
  61. param.ranges.max = 100.0f;
  62. param.ranges.step = 1;
  63. param.ranges.stepSmall = 1;
  64. param.ranges.stepLarge = 10;
  65. switch (index)
  66. {
  67. case PARAM_LEFT_RIGHT:
  68. param.name = "Left/Right";
  69. break;
  70. case PARAM_FRONT_REAR:
  71. param.name = "Front/Rear";
  72. break;
  73. }
  74. return &param;
  75. // unused
  76. (void)handle;
  77. }
  78. static float quadpanner_get_parameter_value(NativePluginHandle handle, uint32_t index)
  79. {
  80. return handlePtr->params[index];
  81. }
  82. static void quadpanner_set_parameter_value(NativePluginHandle handle, uint32_t index, float value)
  83. {
  84. handlePtr->params[index] = value;
  85. }
  86. static inline
  87. void handle_audio_buffers(const float* inBuffer,
  88. float* outBuffer,
  89. Filter* const filter,
  90. const float gain,
  91. const uint32_t frames)
  92. {
  93. const float a0 = filter->a0;
  94. const float b1 = filter->b1;
  95. float z1 = filter->z1;
  96. for (uint32_t i = 0; i < frames; ++i) {
  97. z1 = gain * a0 + z1 * b1;
  98. *outBuffer++ = *inBuffer++ * z1;
  99. }
  100. filter->z1 = z1;
  101. }
  102. // FIXME for v3.0, use const for the input buffer
  103. static void quadpanner_process(NativePluginHandle handle,
  104. float** inBuffer, float** outBuffer, uint32_t frames,
  105. const NativeMidiEvent* midiEvents, uint32_t midiEventCount)
  106. {
  107. float v1, v2, tmp;
  108. // left/right
  109. if ((tmp = handlePtr->params[PARAM_LEFT_RIGHT]) < 0.f)
  110. {
  111. v1 = 1.f;
  112. v2 = 1.f - tmp * -0.01f;
  113. }
  114. else
  115. {
  116. v1 = 1.f - tmp * 0.01f;
  117. v2 = 1.f;
  118. }
  119. handle_audio_buffers(inBuffer[0], outBuffer[0], &handlePtr->lowpass[0], v1, frames);
  120. handle_audio_buffers(inBuffer[1], outBuffer[1], &handlePtr->lowpass[1], v2, frames);
  121. // front/rear
  122. if ((tmp = handlePtr->params[PARAM_FRONT_REAR]) < 0.f)
  123. {
  124. v1 = 1.f;
  125. v2 = 1.f - tmp * -0.01f;
  126. }
  127. else
  128. {
  129. v1 = 1.f - tmp * 0.01f;
  130. v2 = 1.f;
  131. }
  132. handle_audio_buffers(inBuffer[2], outBuffer[2], &handlePtr->lowpass[2], v1, frames);
  133. handle_audio_buffers(inBuffer[3], outBuffer[3], &handlePtr->lowpass[3], v2, frames);
  134. return;
  135. // unused
  136. (void)midiEvents;
  137. (void)midiEventCount;
  138. }
  139. static intptr_t quadpanner_dispatcher(NativePluginHandle handle, NativePluginDispatcherOpcode opcode, int32_t index, intptr_t value, void* ptr, float opt)
  140. {
  141. switch (opcode)
  142. {
  143. case NATIVE_PLUGIN_OPCODE_SAMPLE_RATE_CHANGED:
  144. for (unsigned i = 0; i < PARAM_COUNT * 2; ++i)
  145. set_filter_sample_rate(&handlePtr->lowpass[i], opt);
  146. break;
  147. default:
  148. break;
  149. }
  150. return 0;
  151. // unused
  152. (void)index;
  153. (void)value;
  154. (void)ptr;
  155. }
  156. static const char* quadpanner_get_buffer_port_name(NativePluginHandle handle, uint32_t index, bool isOutput)
  157. {
  158. static const char* const kInNames[4] = {
  159. "In-Left",
  160. "In-Right",
  161. "In-Front",
  162. "In-Rear",
  163. };
  164. static const char* const kOutNames[4] = {
  165. "Out-Left",
  166. "Out-Right",
  167. "Out-Front",
  168. "Out-Rear",
  169. };
  170. return isOutput ? kOutNames[index] : kInNames[index];
  171. // unused
  172. (void)handle;
  173. }
  174. // -----------------------------------------------------------------------
  175. static const NativePluginDescriptor quadpannerDesc = {
  176. .category = NATIVE_PLUGIN_CATEGORY_UTILITY,
  177. .hints = NATIVE_PLUGIN_IS_RTSAFE,
  178. .supports = NATIVE_PLUGIN_SUPPORTS_NOTHING,
  179. .audioIns = 4,
  180. .audioOuts = 4,
  181. .cvIns = 0,
  182. .cvOuts = 0,
  183. .midiIns = 0,
  184. .midiOuts = 0,
  185. .paramIns = PARAM_COUNT,
  186. .paramOuts = 0,
  187. .name = "Quad-Panner",
  188. .label = "quadpanner",
  189. .maker = "falkTX",
  190. .copyright = "GNU GPL v2+",
  191. .instantiate = quadpanner_instantiate,
  192. .cleanup = quadpanner_cleanup,
  193. .get_parameter_count = quadpanner_get_parameter_count,
  194. .get_parameter_info = quadpanner_get_parameter_info,
  195. .get_parameter_value = quadpanner_get_parameter_value,
  196. .get_midi_program_count = NULL,
  197. .get_midi_program_info = NULL,
  198. .set_parameter_value = quadpanner_set_parameter_value,
  199. .set_midi_program = NULL,
  200. .set_custom_data = NULL,
  201. .ui_show = NULL,
  202. .ui_idle = NULL,
  203. .ui_set_parameter_value = NULL,
  204. .ui_set_midi_program = NULL,
  205. .ui_set_custom_data = NULL,
  206. .activate = NULL,
  207. .deactivate = NULL,
  208. .process = quadpanner_process,
  209. .get_state = NULL,
  210. .set_state = NULL,
  211. .dispatcher = quadpanner_dispatcher,
  212. .render_inline_display = NULL,
  213. .get_buffer_port_name = quadpanner_get_buffer_port_name,
  214. .get_buffer_port_range = NULL,
  215. };
  216. // -----------------------------------------------------------------------
  217. void carla_register_native_plugin_quadpanner(void);
  218. void carla_register_native_plugin_quadpanner(void)
  219. {
  220. carla_register_native_plugin(&quadpannerDesc);
  221. }
  222. // -----------------------------------------------------------------------