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.

zita-at1.cpp 14KB

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