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.

425 lines
11KB

  1. /*
  2. * Carla Native Plugins
  3. * Copyright (C) 2012-2014 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 "CarlaMutex.hpp"
  19. #include "dgl/StandaloneWindow.hpp"
  20. #include "dgl/Widget.hpp"
  21. #include "libprojectM/projectM.hpp"
  22. // -----------------------------------------------------------------------
  23. static const projectM::Settings kSettings = {
  24. /* meshX */ 32,
  25. /* meshY */ 24,
  26. /* fps */ 35,
  27. /* textureSize */ 1024,
  28. /* windowWidth */ 512,
  29. /* windowHeight */ 512,
  30. /* presetURL */ "/usr/share/projectM/presets",
  31. /* titleFontURL */ "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf",
  32. /* menuFontURL */ "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf",
  33. /* smoothPresetDuration */ 5,
  34. /* presetDuration */ 30,
  35. /* beatSensitivity */ 10.0f,
  36. /* aspectCorrection */ true,
  37. /* easterEgg */ 1.0f,
  38. /* shuffleEnabled */ true,
  39. /* softCutRatingsEnabled */ false
  40. };
  41. // -----------------------------------------------------------------------
  42. class ProjectMWidget : public DGL::Widget
  43. {
  44. public:
  45. ProjectMWidget(DGL::Window& parent)
  46. : DGL::Widget(parent),
  47. pm(nullptr)
  48. {
  49. }
  50. ~ProjectMWidget()
  51. {
  52. if (pm == nullptr)
  53. return;
  54. delete pm;
  55. pm = nullptr;
  56. }
  57. projectM* getPM() noexcept
  58. {
  59. return pm;
  60. }
  61. protected:
  62. void onDisplay() override
  63. {
  64. if (pm == nullptr)
  65. return;
  66. pm->renderFrame();
  67. }
  68. void onClose() override
  69. {
  70. }
  71. bool onKeyboard(const bool press, const uint32_t key) override
  72. {
  73. if (pm == nullptr)
  74. return false;
  75. projectMKeycode pmKey = PROJECTM_K_NONE;
  76. projectMModifier pmMod = PROJECTM_KMOD_LSHIFT;
  77. if ((key >= PROJECTM_K_0 && key <= PROJECTM_K_9) ||
  78. (key >= PROJECTM_K_A && key <= PROJECTM_K_Z) ||
  79. (key >= PROJECTM_K_a && key <= PROJECTM_K_z))
  80. {
  81. pmKey = static_cast<projectMKeycode>(key);
  82. }
  83. else
  84. {
  85. switch (key)
  86. {
  87. case DGL::CHAR_BACKSPACE:
  88. pmKey = PROJECTM_K_BACKSPACE;
  89. break;
  90. case DGL::CHAR_ESCAPE:
  91. pmKey = PROJECTM_K_ESCAPE;
  92. break;
  93. case DGL::CHAR_DELETE:
  94. pmKey = PROJECTM_K_DELETE;
  95. break;
  96. }
  97. }
  98. if (pmKey == PROJECTM_K_NONE)
  99. return false;
  100. if (const int mod = getModifiers())
  101. {
  102. if (mod & DGL::MODIFIER_CTRL)
  103. pmMod = PROJECTM_KMOD_LCTRL;
  104. }
  105. pm->key_handler(press ? PROJECTM_KEYUP : PROJECTM_KEYDOWN, pmKey, pmMod);
  106. return true;
  107. }
  108. bool onSpecial(const bool press, const DGL::Key key) override
  109. {
  110. if (pm == nullptr)
  111. return false;
  112. projectMKeycode pmKey = PROJECTM_K_NONE;
  113. projectMModifier pmMod = PROJECTM_KMOD_LSHIFT;
  114. switch (key)
  115. {
  116. case DGL::KEY_F1:
  117. pmKey = PROJECTM_K_F1;
  118. break;
  119. case DGL::KEY_F2:
  120. pmKey = PROJECTM_K_F2;
  121. break;
  122. case DGL::KEY_F3:
  123. pmKey = PROJECTM_K_F3;
  124. break;
  125. case DGL::KEY_F4:
  126. pmKey = PROJECTM_K_F4;
  127. break;
  128. case DGL::KEY_F5:
  129. pmKey = PROJECTM_K_F5;
  130. break;
  131. case DGL::KEY_F6:
  132. pmKey = PROJECTM_K_F6;
  133. break;
  134. case DGL::KEY_F7:
  135. pmKey = PROJECTM_K_F7;
  136. break;
  137. case DGL::KEY_F8:
  138. pmKey = PROJECTM_K_F8;
  139. break;
  140. case DGL::KEY_F9:
  141. pmKey = PROJECTM_K_F9;
  142. break;
  143. case DGL::KEY_F10:
  144. pmKey = PROJECTM_K_F10;
  145. break;
  146. case DGL::KEY_F11:
  147. pmKey = PROJECTM_K_F11;
  148. break;
  149. case DGL::KEY_F12:
  150. pmKey = PROJECTM_K_F12;
  151. break;
  152. case DGL::KEY_LEFT:
  153. pmKey = PROJECTM_K_LEFT;
  154. break;
  155. case DGL::KEY_UP:
  156. pmKey = PROJECTM_K_UP;
  157. break;
  158. case DGL::KEY_RIGHT:
  159. pmKey = PROJECTM_K_RIGHT;
  160. break;
  161. case DGL::KEY_DOWN:
  162. pmKey = PROJECTM_K_DOWN;
  163. break;
  164. case DGL::KEY_PAGE_UP:
  165. pmKey = PROJECTM_K_PAGEUP;
  166. break;
  167. case DGL::KEY_PAGE_DOWN:
  168. pmKey = PROJECTM_K_PAGEDOWN;
  169. break;
  170. case DGL::KEY_HOME:
  171. pmKey = PROJECTM_K_HOME;
  172. break;
  173. case DGL::KEY_END:
  174. pmKey = PROJECTM_K_END;
  175. break;
  176. case DGL::KEY_INSERT:
  177. pmKey = PROJECTM_K_INSERT;
  178. break;
  179. case DGL::KEY_SHIFT:
  180. pmKey = PROJECTM_K_LSHIFT;
  181. break;
  182. case DGL::KEY_CTRL:
  183. pmKey = PROJECTM_K_LCTRL;
  184. break;
  185. case DGL::KEY_ALT:
  186. case DGL::KEY_SUPER:
  187. break;
  188. }
  189. if (pmKey == PROJECTM_K_NONE)
  190. return false;
  191. if (const int mod = getModifiers())
  192. {
  193. if (mod & DGL::MODIFIER_CTRL)
  194. pmMod = PROJECTM_KMOD_LCTRL;
  195. }
  196. pm->key_handler(press ? PROJECTM_KEYUP : PROJECTM_KEYDOWN, pmKey, pmMod);
  197. return true;
  198. }
  199. void onReshape(const int width, const int height) override
  200. {
  201. /* Our shading model--Gouraud (smooth). */
  202. glShadeModel(GL_SMOOTH);
  203. /* Set the clear color. */
  204. glClearColor(0, 0, 0, 0);
  205. /* Setup our viewport. */
  206. glViewport(0, 0, width, height);
  207. /*
  208. * Change to the projection matrix and set
  209. * our viewing volume.
  210. */
  211. glMatrixMode(GL_TEXTURE);
  212. glLoadIdentity();
  213. //gluOrtho2D(0.0, (GLfloat) width, 0.0, (GLfloat) height);
  214. //glOrtho(0, width, height, 0, 0.0f, 1.0f);
  215. glMatrixMode(GL_PROJECTION);
  216. glLoadIdentity();
  217. glMatrixMode(GL_MODELVIEW);
  218. glLoadIdentity();
  219. glDrawBuffer(GL_BACK);
  220. glReadBuffer(GL_BACK);
  221. glEnable(GL_BLEND);
  222. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  223. glEnable(GL_LINE_SMOOTH);
  224. glEnable(GL_POINT_SMOOTH);
  225. glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  226. glClear(GL_COLOR_BUFFER_BIT);
  227. glLineStipple(2, 0xAAAA);
  228. if (pm == nullptr)
  229. pm = new projectM(kSettings); // std::string("/usr/share/projectM/config.inp"));
  230. pm->projectM_resetGL(width, height);
  231. }
  232. private:
  233. projectM* pm;
  234. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ProjectMWidget)
  235. };
  236. // -----------------------------------------------------------------------
  237. class ProjectMUI : public DGL::StandaloneWindow
  238. {
  239. public:
  240. ProjectMUI(const NativeHostDescriptor* const host)
  241. : DGL::StandaloneWindow(),
  242. fWidget(getWindow())
  243. {
  244. fWindow.setSize(kSettings.windowWidth, kSettings.windowHeight);
  245. fWindow.setTitle(host->uiName);
  246. if (host->uiParentId != 0)
  247. fWindow.setTransientWinId(host->uiParentId);
  248. fWindow.show();
  249. }
  250. projectM* getPM() noexcept
  251. {
  252. return fWidget.getPM();
  253. }
  254. void idle()
  255. {
  256. fApp.idle();
  257. fWindow.repaint();
  258. }
  259. void focus()
  260. {
  261. fWindow.focus();
  262. }
  263. private:
  264. ProjectMWidget fWidget;
  265. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ProjectMUI)
  266. };
  267. // -----------------------------------------------------------------------
  268. class ProjectMPlugin : public NativePluginClass
  269. {
  270. public:
  271. ProjectMPlugin(const NativeHostDescriptor* const host)
  272. : NativePluginClass(host),
  273. fPM(nullptr),
  274. fUI(nullptr)
  275. {
  276. }
  277. ~ProjectMPlugin() override
  278. {
  279. CARLA_SAFE_ASSERT(fUI == nullptr);
  280. }
  281. protected:
  282. // -------------------------------------------------------------------
  283. // Plugin process calls
  284. void process(float** ins, float**, const uint32_t frames, const NativeMidiEvent* const, const uint32_t) override
  285. {
  286. const CarlaMutexLocker csm(fMutex);
  287. if (fPM == nullptr)
  288. return;
  289. fPM->pcm()->addPCMfloat(ins[0] ,frames);
  290. }
  291. // -------------------------------------------------------------------
  292. // Plugin UI calls
  293. void uiShow(const bool show) override
  294. {
  295. if (show)
  296. {
  297. if (fUI == nullptr)
  298. fUI = new ProjectMUI(getHostHandle());
  299. fUI->focus();
  300. }
  301. else if (fUI != nullptr)
  302. {
  303. {
  304. const CarlaMutexLocker csm(fMutex);
  305. fPM = nullptr;
  306. }
  307. delete fUI;
  308. fUI = nullptr;
  309. }
  310. }
  311. void uiIdle() override
  312. {
  313. if (fUI == nullptr)
  314. return;
  315. fUI->idle();
  316. if (fPM != nullptr)
  317. return;
  318. const CarlaMutexLocker csm(fMutex);
  319. fPM = fUI->getPM();
  320. CARLA_SAFE_ASSERT(fPM != nullptr);
  321. }
  322. private:
  323. projectM* fPM;
  324. ProjectMUI* fUI;
  325. // need to ensure pm is valid
  326. CarlaMutex fMutex;
  327. PluginClassEND(ProjectMPlugin)
  328. CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ProjectMPlugin)
  329. };
  330. // -----------------------------------------------------------------------
  331. static const NativePluginDescriptor projectmDesc = {
  332. /* category */ PLUGIN_CATEGORY_UTILITY,
  333. /* hints */ static_cast<NativePluginHints>(/*PLUGIN_IS_RTSAFE|*/PLUGIN_HAS_UI|PLUGIN_USES_PARENT_ID),
  334. /* supports */ static_cast<NativePluginSupports>(0x0),
  335. /* audioIns */ 1,
  336. /* audioOuts */ 0,
  337. /* midiIns */ 0,
  338. /* midiOuts */ 0,
  339. /* paramIns */ 0,
  340. /* paramOuts */ 0,
  341. /* name */ "ProjectM",
  342. /* label */ "projectm",
  343. /* maker */ "falkTX",
  344. /* copyright */ "GNU GPL v2+",
  345. PluginDescriptorFILL(ProjectMPlugin)
  346. };
  347. // -----------------------------------------------------------------------
  348. CARLA_EXPORT
  349. void carla_register_native_plugin_projectm()
  350. {
  351. carla_register_native_plugin(&projectmDesc);
  352. }
  353. // -----------------------------------------------------------------------