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.

578 lines
18KB

  1. /*
  2. * Carla Native Plugins
  3. * Copyright (C) 2012-2015 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #include "CarlaNative.hpp"
  18. #include "CarlaMathUtils.hpp"
  19. #include "CarlaJuceUtils.hpp"
  20. #include "juce_audio_basics.h"
  21. #include "zita-common.hpp"
  22. #include "zita-rev1/guiclass.cc"
  23. #include "zita-rev1/jclient.cc"
  24. #include "zita-rev1/mainwin.cc"
  25. #include "zita-rev1/pareq.cc"
  26. #include "zita-rev1/png2img.cc"
  27. #include "zita-rev1/reverb.cc"
  28. #include "zita-rev1/rotary.cc"
  29. #include "zita-rev1/styles.cc"
  30. using juce::FloatVectorOperations;
  31. using juce::ScopedPointer;
  32. using namespace REV1;
  33. // -----------------------------------------------------------------------
  34. // REV1 Plugin
  35. class REV1Plugin : public NativePluginClass,
  36. private Mainwin::ValueChangedCallback
  37. {
  38. public:
  39. enum Parameters {
  40. kParameterDELAY,
  41. kParameterXOVER,
  42. kParameterRTLOW,
  43. kParameterRTMID,
  44. kParameterFDAMP,
  45. kParameterEQ1FR,
  46. kParameterEQ1GN,
  47. kParameterEQ2FR,
  48. kParameterEQ2GN,
  49. kParameterOPMIXorRGXYZ,
  50. kParameterNROTARY
  51. };
  52. REV1Plugin(const NativeHostDescriptor* const host, const bool isAmbisonic)
  53. : NativePluginClass(host),
  54. kIsAmbisonic(isAmbisonic),
  55. kNumInputs(2),
  56. kNumOutputs(isAmbisonic ? 4 : 2),
  57. fJackClient(),
  58. xresman(),
  59. jclient(nullptr),
  60. display(nullptr),
  61. rootwin(nullptr),
  62. mainwin(nullptr),
  63. handler(nullptr),
  64. handlerThread(),
  65. leakDetector_REV1Plugin()
  66. {
  67. CARLA_SAFE_ASSERT(host != nullptr);
  68. carla_zeroStruct(fJackClient);
  69. fJackClient.clientName = "rev1";
  70. fJackClient.bufferSize = getBufferSize();
  71. fJackClient.sampleRate = getSampleRate();
  72. int argc = 1;
  73. char* argv[] = { (char*)"rev1" };
  74. xresman.init(&argc, argv, (char*)"rev1", nullptr, 0);
  75. jclient = new Jclient(xresman.rname(), &fJackClient, isAmbisonic);
  76. // set initial values
  77. fParameters[kParameterDELAY] = 0.04f;
  78. fParameters[kParameterXOVER] = 200.0f;
  79. fParameters[kParameterRTLOW] = 3.0f;
  80. fParameters[kParameterRTMID] = 2.0f;
  81. fParameters[kParameterFDAMP] = 6.0e3;
  82. fParameters[kParameterEQ1FR] = 160.0f;
  83. fParameters[kParameterEQ1GN] = 0.0f;
  84. fParameters[kParameterEQ2FR] = 2.5e3;
  85. fParameters[kParameterEQ2GN] = 0.0f;
  86. if (isAmbisonic)
  87. fParameters[kParameterOPMIXorRGXYZ] = 0.0f;
  88. else
  89. fParameters[kParameterOPMIXorRGXYZ] = 0.5f;
  90. Reverb* const reverb(jclient->reverb());
  91. reverb->set_delay(fParameters[kParameterDELAY]);
  92. reverb->set_xover(fParameters[kParameterXOVER]);
  93. reverb->set_rtlow(fParameters[kParameterRTLOW]);
  94. reverb->set_rtmid(fParameters[kParameterRTMID]);
  95. reverb->set_fdamp(fParameters[kParameterFDAMP]);
  96. if (isAmbisonic)
  97. {
  98. reverb->set_opmix(0.5);
  99. reverb->set_rgxyz(fParameters[kParameterOPMIXorRGXYZ]);
  100. }
  101. else
  102. {
  103. reverb->set_opmix(fParameters[kParameterOPMIXorRGXYZ]);
  104. reverb->set_rgxyz(0.0);
  105. }
  106. reverb->set_eq1(fParameters[kParameterEQ1FR], fParameters[kParameterEQ1GN]);
  107. reverb->set_eq2(fParameters[kParameterEQ2FR], fParameters[kParameterEQ2GN]);
  108. }
  109. // -------------------------------------------------------------------
  110. // Plugin parameter calls
  111. uint32_t getParameterCount() const override
  112. {
  113. return kParameterNROTARY;
  114. }
  115. const NativeParameter* getParameterInfo(const uint32_t index) const override
  116. {
  117. CARLA_SAFE_ASSERT_RETURN(index < kParameterNROTARY, nullptr);
  118. static NativeParameter param;
  119. int hints = NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_AUTOMABLE;
  120. // reset
  121. param.name = nullptr;
  122. param.unit = nullptr;
  123. param.ranges.def = 0.0f;
  124. param.ranges.min = 0.0f;
  125. param.ranges.max = 1.0f;
  126. param.ranges.step = 1.0f;
  127. param.ranges.stepSmall = 1.0f;
  128. param.ranges.stepLarge = 1.0f;
  129. param.scalePointCount = 0;
  130. param.scalePoints = nullptr;
  131. switch (index)
  132. {
  133. case kParameterDELAY:
  134. param.name = "Delay";
  135. param.ranges.def = 0.04f;
  136. param.ranges.min = 0.02f;
  137. param.ranges.max = 0.100f;
  138. break;
  139. case kParameterXOVER:
  140. hints |= NATIVE_PARAMETER_IS_LOGARITHMIC;
  141. param.name = "Crossover";
  142. param.ranges.def = 200.0f;
  143. param.ranges.min = 50.0f;
  144. param.ranges.max = 1000.0f;
  145. break;
  146. case kParameterRTLOW:
  147. hints |= NATIVE_PARAMETER_IS_LOGARITHMIC;
  148. param.name = "RT60 Low";
  149. param.ranges.def = 3.0f;
  150. param.ranges.min = 1.0f;
  151. param.ranges.max = 8.0f;
  152. break;
  153. case kParameterRTMID:
  154. hints |= NATIVE_PARAMETER_IS_LOGARITHMIC;
  155. param.name = "RT60 Mid";
  156. param.ranges.def = 2.0f;
  157. param.ranges.min = 1.0f;
  158. param.ranges.max = 8.0f;
  159. break;
  160. case kParameterFDAMP:
  161. hints |= NATIVE_PARAMETER_IS_LOGARITHMIC;
  162. param.name = "HF Damping";
  163. param.ranges.def = 6.0e3;
  164. param.ranges.min = 1.5e3;
  165. param.ranges.max = 24.0e3;
  166. break;
  167. case kParameterEQ1FR:
  168. hints |= NATIVE_PARAMETER_IS_LOGARITHMIC;
  169. param.name = "Eq1 Frequency";
  170. param.ranges.def = 160.0f;
  171. param.ranges.min = 40.0f;
  172. param.ranges.max = 2.5e3;
  173. break;
  174. case kParameterEQ1GN:
  175. param.name = "Eq1 Gain";
  176. param.ranges.def = 0.0f;
  177. param.ranges.min = -15.0;
  178. param.ranges.max = 15.0f;
  179. break;
  180. case kParameterEQ2FR:
  181. hints |= NATIVE_PARAMETER_IS_LOGARITHMIC;
  182. param.name = "Eq2 Frequency";
  183. param.ranges.def = 2.5e3;
  184. param.ranges.min = 160.0;
  185. param.ranges.max = 10e3;
  186. break;
  187. case kParameterEQ2GN:
  188. param.name = "Eq2 Gain";
  189. param.ranges.def = 0.0f;
  190. param.ranges.min = -15.0;
  191. param.ranges.max = 15.0f;
  192. break;
  193. case kParameterOPMIXorRGXYZ:
  194. if (kIsAmbisonic)
  195. {
  196. param.name = "XYZ gain";
  197. param.ranges.def = 0.0f;
  198. param.ranges.min = -9.0f;
  199. param.ranges.max = 9.0f;
  200. }
  201. else
  202. {
  203. param.name = "Dry/wet mix";
  204. param.ranges.def = 0.5f;
  205. param.ranges.min = 0.0f;
  206. param.ranges.max = 1.0f;
  207. }
  208. break;
  209. }
  210. param.hints = static_cast<NativeParameterHints>(hints);
  211. return &param;
  212. }
  213. float getParameterValue(const uint32_t index) const override
  214. {
  215. CARLA_SAFE_ASSERT_RETURN(index < kParameterNROTARY, 0.0f);
  216. return fParameters[index];
  217. }
  218. // -------------------------------------------------------------------
  219. // Plugin state calls
  220. void setParameterValue(const uint32_t index, const float value) override
  221. {
  222. CARLA_SAFE_ASSERT_RETURN(index < kParameterNROTARY,);
  223. Reverb* const reverb(jclient->reverb());
  224. fParameters[index] = value;
  225. switch (index)
  226. {
  227. case kParameterDELAY:
  228. reverb->set_delay(value);
  229. break;
  230. case kParameterXOVER:
  231. reverb->set_xover(value);
  232. break;
  233. case kParameterRTLOW:
  234. reverb->set_rtlow(value);
  235. break;
  236. case kParameterRTMID:
  237. reverb->set_rtmid(value);
  238. break;
  239. case kParameterFDAMP:
  240. reverb->set_fdamp(value);
  241. break;
  242. case kParameterEQ1FR:
  243. reverb->set_eq1(value, fParameters[kParameterEQ1GN]);
  244. break;
  245. case kParameterEQ1GN:
  246. reverb->set_eq1(fParameters[kParameterEQ1FR], value);
  247. break;
  248. case kParameterEQ2FR:
  249. reverb->set_eq2(value, fParameters[kParameterEQ2GN]);
  250. break;
  251. case kParameterEQ2GN:
  252. reverb->set_eq2(fParameters[kParameterEQ2FR], value);
  253. break;
  254. case kParameterOPMIXorRGXYZ:
  255. if (kIsAmbisonic)
  256. reverb->set_rgxyz(value);
  257. else
  258. reverb->set_opmix(value);
  259. break;
  260. }
  261. }
  262. // -------------------------------------------------------------------
  263. // Plugin process calls
  264. void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const, const uint32_t) override
  265. {
  266. if (! fJackClient.active)
  267. {
  268. const int iframes(static_cast<int>(frames));
  269. for (uint32_t i=0; i<kNumInputs; ++i)
  270. FloatVectorOperations::clear(outBuffer[i], iframes);
  271. return;
  272. }
  273. for (uint32_t i=0; i<kNumInputs; ++i)
  274. fJackClient.portsAudioIn[i].buffer.audio = inBuffer[i];
  275. for (uint32_t i=0; i<kNumOutputs; ++i)
  276. fJackClient.portsAudioOut[i].buffer.audio = outBuffer[i];
  277. fJackClient.processCallback(frames, fJackClient.processPtr);
  278. }
  279. // -------------------------------------------------------------------
  280. // Plugin UI calls
  281. void uiShow(const bool show) override
  282. {
  283. if (show)
  284. {
  285. if (display != nullptr)
  286. return;
  287. display = new X_display(nullptr);
  288. if (display->dpy() == nullptr)
  289. return hostUiUnavailable();
  290. styles_init(display, &xresman, getResourceDir());
  291. rootwin = new X_rootwin(display);
  292. mainwin = new Mainwin(rootwin, &xresman, 0, 0, jclient, this);
  293. rootwin->handle_event();
  294. mainwin->x_set_title(getUiName());
  295. handler = new X_handler(display, mainwin, EV_X11);
  296. if (const uintptr_t winId = getUiParentId())
  297. XSetTransientForHint(display->dpy(), mainwin->win(), static_cast<Window>(winId));
  298. handler->next_event();
  299. XFlush(display->dpy());
  300. handlerThread.setupAndRun(handler, rootwin, mainwin);
  301. }
  302. else
  303. {
  304. if (handlerThread.isThreadRunning())
  305. handlerThread.stopThread();
  306. handler = nullptr;
  307. mainwin = nullptr;
  308. rootwin = nullptr;
  309. display = nullptr;
  310. }
  311. }
  312. void uiIdle() override
  313. {
  314. if (mainwin == nullptr)
  315. return;
  316. if (handlerThread.wasClosed())
  317. {
  318. {
  319. const CarlaMutexLocker cml(handlerThread.getLock());
  320. handler = nullptr;
  321. mainwin = nullptr;
  322. rootwin = nullptr;
  323. display = nullptr;
  324. }
  325. uiClosed();
  326. }
  327. }
  328. void uiSetParameterValue(const uint32_t index, const float value) override
  329. {
  330. CARLA_SAFE_ASSERT_RETURN(index < kParameterNROTARY,);
  331. if (mainwin == nullptr)
  332. return;
  333. uint32_t rindex = index;
  334. if (kIsAmbisonic && index == kParameterOPMIXorRGXYZ)
  335. rindex += 1;
  336. const CarlaMutexLocker cml(handlerThread.getLock());
  337. mainwin->_rotary[rindex]->set_value(value);
  338. }
  339. // -------------------------------------------------------------------
  340. // Plugin dispatcher calls
  341. void bufferSizeChanged(const uint32_t bufferSize) override
  342. {
  343. fJackClient.bufferSize = bufferSize;
  344. }
  345. void sampleRateChanged(const double sampleRate) override
  346. {
  347. fJackClient.sampleRate = sampleRate;
  348. }
  349. void uiNameChanged(const char* const uiName) override
  350. {
  351. CARLA_SAFE_ASSERT_RETURN(uiName != nullptr && uiName[0] != '\0',);
  352. if (mainwin == nullptr)
  353. return;
  354. const CarlaMutexLocker cml(handlerThread.getLock());
  355. mainwin->x_set_title(uiName);
  356. }
  357. // -------------------------------------------------------------------
  358. // Mainwin callbacks
  359. void valueChangedCallback(uint rindex, double value) override
  360. {
  361. uint32_t index = rindex;
  362. if (kIsAmbisonic && rindex == Mainwin::NROTARY)
  363. index = kParameterOPMIXorRGXYZ;
  364. fParameters[index] = value;
  365. uiParameterChanged(index, value);
  366. }
  367. // -------------------------------------------------------------------
  368. private:
  369. const bool kIsAmbisonic;
  370. const uint32_t kNumInputs;
  371. const uint32_t kNumOutputs;
  372. // Fake jack client
  373. jack_client_t fJackClient;
  374. // Zita stuff (core)
  375. X_resman xresman;
  376. ScopedPointer<Jclient> jclient;
  377. ScopedPointer<X_display> display;
  378. ScopedPointer<X_rootwin> rootwin;
  379. ScopedPointer<Mainwin> mainwin;
  380. ScopedPointer<X_handler> handler;
  381. X_handler_thread<Mainwin> handlerThread;
  382. float fParameters[kParameterNROTARY];
  383. public:
  384. static NativePluginHandle _instantiateAmbisonic(const NativeHostDescriptor* host)
  385. {
  386. return (host != nullptr) ? new REV1Plugin(host, true) : nullptr;
  387. }
  388. static NativePluginHandle _instantiateStereo(const NativeHostDescriptor* host)
  389. {
  390. return (host != nullptr) ? new REV1Plugin(host, false) : nullptr;
  391. }
  392. static void _cleanup(NativePluginHandle handle)
  393. {
  394. delete (REV1Plugin*)handle;
  395. }
  396. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(REV1Plugin)
  397. };
  398. // -----------------------------------------------------------------------
  399. static const NativePluginDescriptor rev1AmbisonicDesc = {
  400. /* category */ NATIVE_PLUGIN_CATEGORY_DELAY,
  401. /* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_RTSAFE
  402. |NATIVE_PLUGIN_HAS_UI
  403. |NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS
  404. |NATIVE_PLUGIN_NEEDS_UI_MAIN_THREAD
  405. |NATIVE_PLUGIN_USES_PARENT_ID),
  406. /* supports */ static_cast<NativePluginSupports>(0x0),
  407. /* audioIns */ 2,
  408. /* audioOuts */ 4,
  409. /* midiIns */ 0,
  410. /* midiOuts */ 0,
  411. /* paramIns */ REV1Plugin::kParameterNROTARY,
  412. /* paramOuts */ 0,
  413. /* name */ "REV1 (Ambisonic)",
  414. /* label */ "rev1-ambisonic",
  415. /* maker */ "falkTX, Fons Adriaensen",
  416. /* copyright */ "GPL v2+",
  417. REV1Plugin::_instantiateAmbisonic,
  418. REV1Plugin::_cleanup,
  419. REV1Plugin::_get_parameter_count,
  420. REV1Plugin::_get_parameter_info,
  421. REV1Plugin::_get_parameter_value,
  422. REV1Plugin::_get_parameter_text,
  423. REV1Plugin::_get_midi_program_count,
  424. REV1Plugin::_get_midi_program_info,
  425. REV1Plugin::_set_parameter_value,
  426. REV1Plugin::_set_midi_program,
  427. REV1Plugin::_set_custom_data,
  428. REV1Plugin::_ui_show,
  429. REV1Plugin::_ui_idle,
  430. REV1Plugin::_ui_set_parameter_value,
  431. REV1Plugin::_ui_set_midi_program,
  432. REV1Plugin::_ui_set_custom_data,
  433. REV1Plugin::_activate,
  434. REV1Plugin::_deactivate,
  435. REV1Plugin::_process,
  436. REV1Plugin::_get_state,
  437. REV1Plugin::_set_state,
  438. REV1Plugin::_dispatcher
  439. };
  440. static const NativePluginDescriptor rev1StereoDesc = {
  441. /* category */ NATIVE_PLUGIN_CATEGORY_DELAY,
  442. /* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_RTSAFE
  443. |NATIVE_PLUGIN_HAS_UI
  444. |NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS
  445. |NATIVE_PLUGIN_NEEDS_UI_MAIN_THREAD
  446. |NATIVE_PLUGIN_USES_PARENT_ID),
  447. /* supports */ static_cast<NativePluginSupports>(0x0),
  448. /* audioIns */ 2,
  449. /* audioOuts */ 2,
  450. /* midiIns */ 0,
  451. /* midiOuts */ 0,
  452. /* paramIns */ REV1Plugin::kParameterNROTARY,
  453. /* paramOuts */ 0,
  454. /* name */ "REV1 (Stereo)",
  455. /* label */ "rev1-stereo",
  456. /* maker */ "falkTX, Fons Adriaensen",
  457. /* copyright */ "GPL v2+",
  458. REV1Plugin::_instantiateStereo,
  459. REV1Plugin::_cleanup,
  460. REV1Plugin::_get_parameter_count,
  461. REV1Plugin::_get_parameter_info,
  462. REV1Plugin::_get_parameter_value,
  463. REV1Plugin::_get_parameter_text,
  464. REV1Plugin::_get_midi_program_count,
  465. REV1Plugin::_get_midi_program_info,
  466. REV1Plugin::_set_parameter_value,
  467. REV1Plugin::_set_midi_program,
  468. REV1Plugin::_set_custom_data,
  469. REV1Plugin::_ui_show,
  470. REV1Plugin::_ui_idle,
  471. REV1Plugin::_ui_set_parameter_value,
  472. REV1Plugin::_ui_set_midi_program,
  473. REV1Plugin::_ui_set_custom_data,
  474. REV1Plugin::_activate,
  475. REV1Plugin::_deactivate,
  476. REV1Plugin::_process,
  477. REV1Plugin::_get_state,
  478. REV1Plugin::_set_state,
  479. REV1Plugin::_dispatcher
  480. };
  481. // -----------------------------------------------------------------------
  482. CARLA_EXPORT
  483. void carla_register_native_plugin_zita_rev1();
  484. CARLA_EXPORT
  485. void carla_register_native_plugin_zita_rev1()
  486. {
  487. carla_register_native_plugin(&rev1AmbisonicDesc);
  488. carla_register_native_plugin(&rev1StereoDesc);
  489. }
  490. // -----------------------------------------------------------------------