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.

454 lines
14KB

  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-at1/button.cc"
  23. #include "zita-at1/guiclass.cc"
  24. #include "zita-at1/jclient.cc"
  25. #include "zita-at1/mainwin.cc"
  26. #include "zita-at1/png2img.cc"
  27. #include "zita-at1/retuner.cc"
  28. #include "zita-at1/rotary.cc"
  29. #include "zita-at1/styles.cc"
  30. #include "zita-at1/tmeter.cc"
  31. using juce::FloatVectorOperations;
  32. using juce::ScopedPointer;
  33. using namespace AT1;
  34. // -----------------------------------------------------------------------
  35. // AT1 Plugin
  36. class AT1Plugin : public NativePluginClass,
  37. private Mainwin::ValueChangedCallback
  38. {
  39. public:
  40. enum Parameters {
  41. // rotary knobs
  42. kParameterR_TUNE,
  43. kParameterR_FILT,
  44. kParameterR_BIAS,
  45. kParameterR_CORR,
  46. kParameterR_OFFS,
  47. // knob count
  48. kParameterNROTARY,
  49. // midi channel
  50. kParameterM_CHANNEL = kParameterNROTARY,
  51. // final count
  52. kNumParameters
  53. };
  54. AT1Plugin(const NativeHostDescriptor* const host)
  55. : NativePluginClass(host),
  56. fJackClient(),
  57. xresman(),
  58. jclient(nullptr),
  59. display(nullptr),
  60. rootwin(nullptr),
  61. mainwin(nullptr),
  62. handler(nullptr),
  63. handlerThread(),
  64. leakDetector_AT1Plugin()
  65. {
  66. CARLA_SAFE_ASSERT(host != nullptr);
  67. carla_zeroStruct(fJackClient);
  68. fJackClient.clientName = "at1";
  69. fJackClient.bufferSize = getBufferSize();
  70. fJackClient.sampleRate = getSampleRate();
  71. int argc = 1;
  72. char* argv[] = { (char*)"at1" };
  73. xresman.init(&argc, argv, (char*)"at1", nullptr, 0);
  74. jclient = new Jclient(xresman.rname(), &fJackClient);
  75. // set initial values
  76. fParameters[kParameterR_TUNE] = 440.0f;
  77. fParameters[kParameterR_FILT] = 0.1f;
  78. fParameters[kParameterR_BIAS] = 0.5f;
  79. fParameters[kParameterR_CORR] = 1.0f;
  80. fParameters[kParameterR_OFFS] = 0.0f;
  81. fParameters[kParameterM_CHANNEL] = 0.0f;
  82. Retuner* const retuner(jclient->retuner());
  83. jclient->set_midichan(-1);
  84. retuner->set_refpitch(fParameters[kParameterR_TUNE]);
  85. retuner->set_corrfilt(fParameters[kParameterR_FILT]);
  86. retuner->set_notebias(fParameters[kParameterR_BIAS]);
  87. retuner->set_corrgain(fParameters[kParameterR_CORR]);
  88. retuner->set_corroffs(fParameters[kParameterR_OFFS]);
  89. }
  90. // -------------------------------------------------------------------
  91. // Plugin parameter calls
  92. uint32_t getParameterCount() const override
  93. {
  94. return kNumParameters;
  95. }
  96. const NativeParameter* getParameterInfo(const uint32_t index) const override
  97. {
  98. CARLA_SAFE_ASSERT_RETURN(index < kNumParameters, nullptr);
  99. static NativeParameter param;
  100. static NativeParameterScalePoint scalePoints[17];
  101. int hints = NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_AUTOMABLE;
  102. // reset
  103. param.name = nullptr;
  104. param.unit = nullptr;
  105. param.ranges.def = 0.0f;
  106. param.ranges.min = 0.0f;
  107. param.ranges.max = 1.0f;
  108. param.ranges.step = 1.0f;
  109. param.ranges.stepSmall = 1.0f;
  110. param.ranges.stepLarge = 1.0f;
  111. param.scalePointCount = 0;
  112. param.scalePoints = nullptr;
  113. switch (index)
  114. {
  115. case kParameterR_TUNE:
  116. param.name = "Tuning";
  117. param.ranges.def = 440.0f;
  118. param.ranges.min = 400.0f;
  119. param.ranges.max = 480.0f;
  120. break;
  121. case kParameterR_FILT:
  122. hints |= NATIVE_PARAMETER_IS_LOGARITHMIC;
  123. param.name = "Filter";
  124. param.ranges.def = 0.1f;
  125. param.ranges.min = 0.02f;
  126. param.ranges.max = 0.5f;
  127. break;
  128. case kParameterR_BIAS:
  129. param.name = "Bias";
  130. param.ranges.def = 0.5f;
  131. param.ranges.min = 0.0f;
  132. param.ranges.max = 1.0f;
  133. break;
  134. case kParameterR_CORR:
  135. param.name = "Correction";
  136. param.ranges.def = 1.0f;
  137. param.ranges.min = 0.0f;
  138. param.ranges.max = 1.0f;
  139. break;
  140. case kParameterR_OFFS:
  141. param.name = "OfOfset";
  142. param.ranges.def = 0.0f;
  143. param.ranges.min = -2.0f;
  144. param.ranges.max = 2.0f;
  145. break;
  146. case kParameterM_CHANNEL:
  147. hints |= NATIVE_PARAMETER_USES_SCALEPOINTS;
  148. param.name = "MIDI Channel";
  149. param.ranges.def = 0.0f;
  150. param.ranges.min = 0.0f;
  151. param.ranges.max = 16.0f;
  152. param.scalePointCount = 17;
  153. param.scalePoints = scalePoints;
  154. scalePoints[ 0].value = 0.0f;
  155. scalePoints[ 0].label = "Omni";
  156. scalePoints[ 1].value = 1.0f;
  157. scalePoints[ 1].label = "1";
  158. scalePoints[ 2].value = 2.0f;
  159. scalePoints[ 2].label = "2";
  160. scalePoints[ 3].value = 3.0f;
  161. scalePoints[ 3].label = "3";
  162. scalePoints[ 4].value = 4.0f;
  163. scalePoints[ 4].label = "4";
  164. scalePoints[ 5].value = 5.0f;
  165. scalePoints[ 5].label = "5";
  166. scalePoints[ 6].value = 6.0f;
  167. scalePoints[ 6].label = "6";
  168. scalePoints[ 7].value = 7.0f;
  169. scalePoints[ 7].label = "7";
  170. scalePoints[ 8].value = 8.0f;
  171. scalePoints[ 8].label = "8";
  172. scalePoints[ 9].value = 9.0f;
  173. scalePoints[ 9].label = "9";
  174. scalePoints[10].value = 10.0f;
  175. scalePoints[10].label = "10";
  176. scalePoints[11].value = 11.0f;
  177. scalePoints[11].label = "11";
  178. scalePoints[12].value = 12.0f;
  179. scalePoints[12].label = "12";
  180. scalePoints[13].value = 13.0f;
  181. scalePoints[13].label = "13";
  182. scalePoints[14].value = 14.0f;
  183. scalePoints[14].label = "14";
  184. scalePoints[15].value = 15.0f;
  185. scalePoints[15].label = "15";
  186. scalePoints[16].value = 16.0f;
  187. scalePoints[16].label = "16";
  188. break;
  189. }
  190. param.hints = static_cast<NativeParameterHints>(hints);
  191. return &param;
  192. }
  193. float getParameterValue(const uint32_t index) const override
  194. {
  195. CARLA_SAFE_ASSERT_RETURN(index < kNumParameters, 0.0f);
  196. return fParameters[index];
  197. }
  198. // -------------------------------------------------------------------
  199. // Plugin state calls
  200. void setParameterValue(const uint32_t index, const float value) override
  201. {
  202. CARLA_SAFE_ASSERT_RETURN(index < kNumParameters,);
  203. fParameters[index] = value;
  204. Retuner* const retuner(jclient->retuner());
  205. switch (index)
  206. {
  207. case kParameterR_TUNE:
  208. retuner->set_refpitch(value);
  209. break;
  210. case kParameterR_FILT:
  211. retuner->set_corrfilt(value);
  212. break;
  213. case kParameterR_BIAS:
  214. retuner->set_notebias(value);
  215. break;
  216. case kParameterR_CORR:
  217. retuner->set_corrgain(value);
  218. break;
  219. case kParameterR_OFFS:
  220. retuner->set_corroffs(value);
  221. break;
  222. case kParameterM_CHANNEL:
  223. jclient->set_midichan(value-1.0f);
  224. break;
  225. }
  226. }
  227. // -------------------------------------------------------------------
  228. // Plugin process calls
  229. void process(float** const inBuffer, float** const outBuffer, const uint32_t frames,
  230. const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override
  231. {
  232. if (! fJackClient.active)
  233. {
  234. FloatVectorOperations::clear(outBuffer[0], static_cast<int>(frames));
  235. return;
  236. }
  237. fJackClient.portsAudioIn [0].buffer.audio = inBuffer [0];
  238. fJackClient.portsAudioOut[0].buffer.audio = outBuffer[0];
  239. fJackClient.portsMidiIn[0].buffer.midi.count = midiEventCount;
  240. fJackClient.portsMidiIn[0].buffer.midi.events = const_cast<NativeMidiEvent*>(midiEvents);
  241. fJackClient.processCallback(frames, fJackClient.processPtr);
  242. }
  243. // -------------------------------------------------------------------
  244. // Plugin UI calls
  245. void uiShow(const bool show) override
  246. {
  247. if (show)
  248. {
  249. if (display != nullptr)
  250. return;
  251. display = new X_display(nullptr);
  252. if (display->dpy() == nullptr)
  253. return hostUiUnavailable();
  254. styles_init(display, &xresman, getResourceDir());
  255. rootwin = new X_rootwin(display);
  256. mainwin = new Mainwin(rootwin, &xresman, 0, 0, jclient, this);
  257. rootwin->handle_event();
  258. mainwin->x_set_title(getUiName());
  259. handler = new X_handler(display, mainwin, EV_X11);
  260. if (const uintptr_t winId = getUiParentId())
  261. XSetTransientForHint(display->dpy(), mainwin->win(), static_cast<Window>(winId));
  262. handler->next_event();
  263. XFlush(display->dpy());
  264. handlerThread.setupAndRun(handler, rootwin, mainwin);
  265. }
  266. else
  267. {
  268. if (handlerThread.isThreadRunning())
  269. handlerThread.stopThread();
  270. handler = nullptr;
  271. mainwin = nullptr;
  272. rootwin = nullptr;
  273. display = nullptr;
  274. }
  275. }
  276. void uiIdle() override
  277. {
  278. if (mainwin == nullptr)
  279. return;
  280. if (handlerThread.wasClosed())
  281. {
  282. {
  283. const CarlaMutexLocker cml(handlerThread.getLock());
  284. handler = nullptr;
  285. mainwin = nullptr;
  286. rootwin = nullptr;
  287. display = nullptr;
  288. }
  289. uiClosed();
  290. }
  291. }
  292. void uiSetParameterValue(const uint32_t index, const float value) override
  293. {
  294. CARLA_SAFE_ASSERT_RETURN(index < kNumParameters,);
  295. if (mainwin == nullptr)
  296. return;
  297. const CarlaMutexLocker cml(handlerThread.getLock());
  298. if (index < kParameterNROTARY)
  299. {
  300. mainwin->_rotary[index]->set_value(value);
  301. return;
  302. }
  303. if (index == kParameterM_CHANNEL)
  304. {
  305. mainwin->setchan_ui(value);
  306. return;
  307. }
  308. }
  309. // -------------------------------------------------------------------
  310. // Plugin dispatcher calls
  311. void bufferSizeChanged(const uint32_t bufferSize) override
  312. {
  313. fJackClient.bufferSize = bufferSize;
  314. }
  315. void sampleRateChanged(const double sampleRate) override
  316. {
  317. fJackClient.sampleRate = sampleRate;
  318. }
  319. void uiNameChanged(const char* const uiName) override
  320. {
  321. CARLA_SAFE_ASSERT_RETURN(uiName != nullptr && uiName[0] != '\0',);
  322. if (mainwin == nullptr)
  323. return;
  324. const CarlaMutexLocker cml(handlerThread.getLock());
  325. mainwin->x_set_title(uiName);
  326. }
  327. // -------------------------------------------------------------------
  328. // Mainwin callbacks
  329. void valueChangedCallback(uint index, float value) override
  330. {
  331. fParameters[index] = value;
  332. uiParameterChanged(index, value);
  333. }
  334. // -------------------------------------------------------------------
  335. private:
  336. // Fake jack client
  337. jack_client_t fJackClient;
  338. // Zita stuff (core)
  339. X_resman xresman;
  340. ScopedPointer<Jclient> jclient;
  341. ScopedPointer<X_display> display;
  342. ScopedPointer<X_rootwin> rootwin;
  343. ScopedPointer<Mainwin> mainwin;
  344. ScopedPointer<X_handler> handler;
  345. X_handler_thread<Mainwin> handlerThread;
  346. float fParameters[kNumParameters];
  347. PluginClassEND(AT1Plugin)
  348. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AT1Plugin)
  349. };
  350. // -----------------------------------------------------------------------
  351. static const NativePluginDescriptor at1Desc = {
  352. /* category */ NATIVE_PLUGIN_CATEGORY_MODULATOR,
  353. /* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_RTSAFE
  354. |NATIVE_PLUGIN_HAS_UI
  355. |NATIVE_PLUGIN_NEEDS_FIXED_BUFFERS
  356. |NATIVE_PLUGIN_NEEDS_UI_MAIN_THREAD
  357. |NATIVE_PLUGIN_USES_PARENT_ID),
  358. /* supports */ static_cast<NativePluginSupports>(0x0),
  359. /* audioIns */ 1,
  360. /* audioOuts */ 1,
  361. /* midiIns */ 1,
  362. /* midiOuts */ 0,
  363. /* paramIns */ AT1Plugin::kNumParameters,
  364. /* paramOuts */ 0,
  365. /* name */ "AT1",
  366. /* label */ "at1",
  367. /* maker */ "falkTX, Fons Adriaensen",
  368. /* copyright */ "GPL v2+",
  369. PluginDescriptorFILL(AT1Plugin)
  370. };
  371. // -----------------------------------------------------------------------
  372. CARLA_EXPORT
  373. void carla_register_native_plugin_zita_at1();
  374. CARLA_EXPORT
  375. void carla_register_native_plugin_zita_at1()
  376. {
  377. carla_register_native_plugin(&at1Desc);
  378. }
  379. // -----------------------------------------------------------------------