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.

293 lines
7.7KB

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