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.

351 lines
10KB

  1. /*
  2. * XY Controller UI, taken from Cadence
  3. * Copyright (C) 2011-2022 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 "CarlaDefines.h"
  18. #include "CarlaMIDI.h"
  19. #include "CarlaNativeExtUI.hpp"
  20. #include "midi-queue.hpp"
  21. #include "water/text/StringArray.h"
  22. // -----------------------------------------------------------------------
  23. class XYControllerPlugin : public NativePluginAndUiClass
  24. {
  25. public:
  26. enum Parameters {
  27. kParamSmooth,
  28. kParamLinear,
  29. kParamSpeed,
  30. kParamReverseY,
  31. kParamInX,
  32. kParamInY,
  33. kParamOutX,
  34. kParamOutY,
  35. kParamCount,
  36. };
  37. XYControllerPlugin(const NativeHostDescriptor* const host)
  38. : NativePluginAndUiClass(host, "xycontroller-ui"),
  39. params(),
  40. channels(),
  41. mqueue(),
  42. mqueueRT()
  43. {
  44. carla_zeroStruct(params);
  45. params[kParamSpeed] = 8.0f;
  46. carla_zeroStruct(channels);
  47. channels[0] = true;
  48. }
  49. protected:
  50. // -------------------------------------------------------------------
  51. // Plugin parameter calls
  52. uint32_t getParameterCount() const override
  53. {
  54. return kParamCount;
  55. }
  56. const NativeParameter* getParameterInfo(const uint32_t index) const override
  57. {
  58. CARLA_SAFE_ASSERT_RETURN(index < kParamCount, nullptr);
  59. static NativeParameter param;
  60. int hints = NATIVE_PARAMETER_IS_ENABLED|NATIVE_PARAMETER_IS_AUTOMATABLE;
  61. param.name = nullptr;
  62. param.unit = "%";
  63. param.ranges.def = 0.0f;
  64. param.ranges.min = -100.0f;
  65. param.ranges.max = 100.0f;
  66. param.ranges.step = 1.0f;
  67. param.ranges.stepSmall = 0.01f;
  68. param.ranges.stepLarge = 10.0f;
  69. param.scalePointCount = 0;
  70. param.scalePoints = nullptr;
  71. switch (index)
  72. {
  73. case kParamInX:
  74. param.name = "X";
  75. break;
  76. case kParamInY:
  77. param.name = "Y";
  78. break;
  79. case kParamOutX:
  80. hints |= NATIVE_PARAMETER_IS_OUTPUT;
  81. param.name = "Out X";
  82. break;
  83. case kParamOutY:
  84. hints |= NATIVE_PARAMETER_IS_OUTPUT;
  85. param.name = "Out Y";
  86. break;
  87. case kParamSpeed:
  88. param.name = "Speed";
  89. param.unit = "px";
  90. param.ranges.def = 8.0f;
  91. param.ranges.min = 1.0f;
  92. break;
  93. }
  94. if (param.name == nullptr)
  95. {
  96. hints |= NATIVE_PARAMETER_IS_INTEGER | NATIVE_PARAMETER_USES_SCALEPOINTS;
  97. param.unit = nullptr;
  98. param.ranges.min = 0;
  99. param.ranges.max = 1;
  100. param.scalePointCount = 2;
  101. switch (index)
  102. {
  103. case kParamSmooth:
  104. param.name = "Smooth";
  105. {
  106. static const NativeParameterScalePoint scalePoints[2] = {
  107. { "Thru", 0 },
  108. { "Smooth", 1 }
  109. };
  110. param.scalePoints = scalePoints;
  111. }
  112. break;
  113. case kParamLinear:
  114. param.name = "Linear";
  115. {
  116. static const NativeParameterScalePoint scalePoints[2] = {
  117. { "Log", 0 },
  118. { "Linear", 1 }
  119. };
  120. param.scalePoints = scalePoints;
  121. }
  122. break;
  123. case kParamReverseY:
  124. param.name = "Rev Y";
  125. {
  126. static const NativeParameterScalePoint scalePoints[2] = {
  127. { "Top to Bottom", 0 },
  128. { "Bottom to Top", 1 }
  129. };
  130. param.scalePoints = scalePoints;
  131. }
  132. break;
  133. }
  134. }
  135. param.hints = static_cast<NativeParameterHints>(hints);
  136. return &param;
  137. }
  138. float getParameterValue(const uint32_t index) const override
  139. {
  140. CARLA_SAFE_ASSERT_RETURN(index < kParamCount, 0.0f);
  141. return params[index];
  142. }
  143. // -------------------------------------------------------------------
  144. // Plugin state calls
  145. void setParameterValue(const uint32_t index, const float value) override
  146. {
  147. switch (index)
  148. {
  149. case kParamSmooth:
  150. case kParamLinear:
  151. case kParamSpeed:
  152. case kParamReverseY:
  153. case kParamInX:
  154. case kParamInY:
  155. case kParamOutX:
  156. case kParamOutY:
  157. params[index] = value;
  158. break;
  159. }
  160. }
  161. void setCustomData(const char* const key, const char* const value) override
  162. {
  163. CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
  164. CARLA_SAFE_ASSERT_RETURN(value != nullptr,);
  165. if (std::strcmp(key, "channels") == 0)
  166. {
  167. const water::StringArray chans(water::StringArray::fromTokens(value, ",", ""));
  168. carla_zeroStruct(channels);
  169. for (const water::String *it=chans.begin(), *end=chans.end(); it != end; ++it)
  170. {
  171. const int ichan = std::atoi((*it).toRawUTF8());
  172. CARLA_SAFE_ASSERT_INT_CONTINUE(ichan >= 1 && ichan <= 16, ichan);
  173. channels[ichan-1] = true;
  174. }
  175. }
  176. }
  177. // -------------------------------------------------------------------
  178. // Plugin process calls
  179. void process(const float* const*, float**, const uint32_t,
  180. const NativeMidiEvent* const midiEvents, const uint32_t midiEventCount) override
  181. {
  182. // params[kParamOutX] = params[kParamInX];
  183. // params[kParamOutY] = params[kParamInY];
  184. if (mqueue.isNotEmpty() && mqueueRT.tryToCopyDataFrom(mqueue))
  185. {
  186. uint8_t d1, d2, d3;
  187. NativeMidiEvent ev = { 0, 0, 3, { 0, 0, 0, 0 } };
  188. while (mqueueRT.get(d1, d2, d3))
  189. {
  190. ev.data[0] = d1;
  191. ev.data[1] = d2;
  192. ev.data[2] = d3;
  193. writeMidiEvent(&ev);
  194. }
  195. }
  196. for (uint32_t i=0; i < midiEventCount; ++i)
  197. writeMidiEvent(&midiEvents[i]);
  198. }
  199. #ifndef CARLA_OS_WASM
  200. // -------------------------------------------------------------------
  201. // Pipe Server calls
  202. bool msgReceived(const char* const msg) noexcept override
  203. {
  204. if (NativePluginAndUiClass::msgReceived(msg))
  205. return true;
  206. if (std::strcmp(msg, "cc") == 0)
  207. {
  208. uint8_t cc, value;
  209. CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(cc), true);
  210. CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(value), true);
  211. const CarlaMutexLocker cml(mqueue.getMutex());
  212. for (int i=0; i<16; ++i)
  213. {
  214. if (channels[i])
  215. if (! mqueue.put(uint8_t(MIDI_STATUS_CONTROL_CHANGE | (i & MIDI_CHANNEL_BIT)), cc, value))
  216. break;
  217. }
  218. return true;
  219. }
  220. if (std::strcmp(msg, "cc2") == 0)
  221. {
  222. uint8_t cc1, value1, cc2, value2;
  223. CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(cc1), true);
  224. CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(value1), true);
  225. CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(cc2), true);
  226. CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(value2), true);
  227. const CarlaMutexLocker cml(mqueue.getMutex());
  228. for (int i=0; i<16; ++i)
  229. {
  230. if (channels[i])
  231. {
  232. if (! mqueue.put(uint8_t(MIDI_STATUS_CONTROL_CHANGE | (i & MIDI_CHANNEL_BIT)), cc1, value1))
  233. break;
  234. if (! mqueue.put(uint8_t(MIDI_STATUS_CONTROL_CHANGE | (i & MIDI_CHANNEL_BIT)), cc2, value2))
  235. break;
  236. }
  237. }
  238. return true;
  239. }
  240. if (std::strcmp(msg, "note") == 0)
  241. {
  242. bool onOff;
  243. uint8_t note;
  244. CARLA_SAFE_ASSERT_RETURN(readNextLineAsBool(onOff), true);
  245. CARLA_SAFE_ASSERT_RETURN(readNextLineAsByte(note), true);
  246. const uint8_t status = onOff ? MIDI_STATUS_NOTE_ON : MIDI_STATUS_NOTE_OFF;
  247. const uint8_t velocity = onOff ? 100 : 0;
  248. const CarlaMutexLocker cml(mqueue.getMutex());
  249. for (int i=0; i<16; ++i)
  250. {
  251. if (channels[i])
  252. if (! mqueue.put(uint8_t(status | (i & MIDI_CHANNEL_BIT)), note, velocity))
  253. break;
  254. }
  255. return true;
  256. }
  257. return false;
  258. }
  259. #endif
  260. private:
  261. float params[kParamCount];
  262. bool channels[16];
  263. MIDIEventQueue<128> mqueue, mqueueRT;
  264. PluginClassEND(XYControllerPlugin)
  265. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(XYControllerPlugin)
  266. };
  267. // -----------------------------------------------------------------------
  268. static const NativePluginDescriptor notesDesc = {
  269. /* category */ NATIVE_PLUGIN_CATEGORY_UTILITY,
  270. /* hints */ static_cast<NativePluginHints>(NATIVE_PLUGIN_IS_RTSAFE
  271. |NATIVE_PLUGIN_HAS_UI),
  272. /* supports */ NATIVE_PLUGIN_SUPPORTS_NOTHING,
  273. /* audioIns */ 0,
  274. /* audioOuts */ 0,
  275. /* midiIns */ 1,
  276. /* midiOuts */ 1,
  277. /* paramIns */ 6,
  278. /* paramOuts */ 2,
  279. /* name */ "XY Controller",
  280. /* label */ "xycontroller",
  281. /* maker */ "falkTX",
  282. /* copyright */ "GNU GPL v2+",
  283. PluginDescriptorFILL(XYControllerPlugin)
  284. };
  285. // -----------------------------------------------------------------------
  286. CARLA_API_EXPORT
  287. void carla_register_native_plugin_xycontroller();
  288. CARLA_API_EXPORT
  289. void carla_register_native_plugin_xycontroller()
  290. {
  291. carla_register_native_plugin(&notesDesc);
  292. }
  293. // -----------------------------------------------------------------------