/* * Carla Native Plugins * Copyright (C) 2012-2014 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * For a full copy of the GNU General Public License see the doc/GPL.txt file. */ #include "CarlaNative.hpp" #include "CarlaMutex.hpp" #include "dgl/StandaloneWindow.hpp" #include "dgl/Widget.hpp" #include "libprojectM/projectM.hpp" // ----------------------------------------------------------------------- static const projectM::Settings kSettings = { /* meshX */ 32, /* meshY */ 24, /* fps */ 35, /* textureSize */ 1024, /* windowWidth */ 512, /* windowHeight */ 512, /* presetURL */ "/usr/share/projectM/presets", /* titleFontURL */ "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", /* menuFontURL */ "/usr/share/fonts/truetype/ttf-dejavu/DejaVuSansMono.ttf", /* smoothPresetDuration */ 5, /* presetDuration */ 30, /* beatSensitivity */ 10.0f, /* aspectCorrection */ true, /* easterEgg */ 1.0f, /* shuffleEnabled */ true, /* softCutRatingsEnabled */ false }; // ----------------------------------------------------------------------- class ProjectMWidget : public DGL::Widget { public: ProjectMWidget(DGL::Window& parent) : DGL::Widget(parent), pm(nullptr) { } ~ProjectMWidget() { if (pm == nullptr) return; delete pm; pm = nullptr; } projectM* getPM() noexcept { return pm; } protected: void onDisplay() override { if (pm == nullptr) return; pm->renderFrame(); } void onClose() override { } bool onKeyboard(const bool press, const uint32_t key) override { if (pm == nullptr) return false; projectMKeycode pmKey = PROJECTM_K_NONE; projectMModifier pmMod = PROJECTM_KMOD_LSHIFT; if ((key >= PROJECTM_K_0 && key <= PROJECTM_K_9) || (key >= PROJECTM_K_A && key <= PROJECTM_K_Z) || (key >= PROJECTM_K_a && key <= PROJECTM_K_z)) { pmKey = static_cast(key); } else { switch (key) { case DGL::CHAR_BACKSPACE: pmKey = PROJECTM_K_BACKSPACE; break; case DGL::CHAR_ESCAPE: pmKey = PROJECTM_K_ESCAPE; break; case DGL::CHAR_DELETE: pmKey = PROJECTM_K_DELETE; break; } } if (pmKey == PROJECTM_K_NONE) return false; if (const int mod = getModifiers()) { if (mod & DGL::MODIFIER_CTRL) pmMod = PROJECTM_KMOD_LCTRL; } pm->key_handler(press ? PROJECTM_KEYUP : PROJECTM_KEYDOWN, pmKey, pmMod); return true; } bool onSpecial(const bool press, const DGL::Key key) override { if (pm == nullptr) return false; projectMKeycode pmKey = PROJECTM_K_NONE; projectMModifier pmMod = PROJECTM_KMOD_LSHIFT; switch (key) { case DGL::KEY_F1: pmKey = PROJECTM_K_F1; break; case DGL::KEY_F2: pmKey = PROJECTM_K_F2; break; case DGL::KEY_F3: pmKey = PROJECTM_K_F3; break; case DGL::KEY_F4: pmKey = PROJECTM_K_F4; break; case DGL::KEY_F5: pmKey = PROJECTM_K_F5; break; case DGL::KEY_F6: pmKey = PROJECTM_K_F6; break; case DGL::KEY_F7: pmKey = PROJECTM_K_F7; break; case DGL::KEY_F8: pmKey = PROJECTM_K_F8; break; case DGL::KEY_F9: pmKey = PROJECTM_K_F9; break; case DGL::KEY_F10: pmKey = PROJECTM_K_F10; break; case DGL::KEY_F11: pmKey = PROJECTM_K_F11; break; case DGL::KEY_F12: pmKey = PROJECTM_K_F12; break; case DGL::KEY_LEFT: pmKey = PROJECTM_K_LEFT; break; case DGL::KEY_UP: pmKey = PROJECTM_K_UP; break; case DGL::KEY_RIGHT: pmKey = PROJECTM_K_RIGHT; break; case DGL::KEY_DOWN: pmKey = PROJECTM_K_DOWN; break; case DGL::KEY_PAGE_UP: pmKey = PROJECTM_K_PAGEUP; break; case DGL::KEY_PAGE_DOWN: pmKey = PROJECTM_K_PAGEDOWN; break; case DGL::KEY_HOME: pmKey = PROJECTM_K_HOME; break; case DGL::KEY_END: pmKey = PROJECTM_K_END; break; case DGL::KEY_INSERT: pmKey = PROJECTM_K_INSERT; break; case DGL::KEY_SHIFT: pmKey = PROJECTM_K_LSHIFT; break; case DGL::KEY_CTRL: pmKey = PROJECTM_K_LCTRL; break; case DGL::KEY_ALT: case DGL::KEY_SUPER: break; } if (pmKey == PROJECTM_K_NONE) return false; if (const int mod = getModifiers()) { if (mod & DGL::MODIFIER_CTRL) pmMod = PROJECTM_KMOD_LCTRL; } pm->key_handler(press ? PROJECTM_KEYUP : PROJECTM_KEYDOWN, pmKey, pmMod); return true; } void onReshape(const int width, const int height) override { /* Our shading model--Gouraud (smooth). */ glShadeModel(GL_SMOOTH); /* Set the clear color. */ glClearColor(0, 0, 0, 0); /* Setup our viewport. */ glViewport(0, 0, width, height); /* * Change to the projection matrix and set * our viewing volume. */ glMatrixMode(GL_TEXTURE); glLoadIdentity(); //gluOrtho2D(0.0, (GLfloat) width, 0.0, (GLfloat) height); //glOrtho(0, width, height, 0, 0.0f, 1.0f); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glDrawBuffer(GL_BACK); glReadBuffer(GL_BACK); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_LINE_SMOOTH); glEnable(GL_POINT_SMOOTH); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); glLineStipple(2, 0xAAAA); if (pm == nullptr) pm = new projectM(kSettings); // std::string("/usr/share/projectM/config.inp")); pm->projectM_resetGL(width, height); } private: projectM* pm; CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ProjectMWidget) }; // ----------------------------------------------------------------------- class ProjectMUI : public DGL::StandaloneWindow { public: ProjectMUI(const NativeHostDescriptor* const host) : DGL::StandaloneWindow(), fWidget(getWindow()) { fWindow.setSize(kSettings.windowWidth, kSettings.windowHeight); fWindow.setTitle(host->uiName); if (host->uiParentId != 0) fWindow.setTransientWinId(host->uiParentId); fWindow.show(); } projectM* getPM() noexcept { return fWidget.getPM(); } void idle() { fApp.idle(); fWindow.repaint(); } void focus() { fWindow.focus(); } private: ProjectMWidget fWidget; CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ProjectMUI) }; // ----------------------------------------------------------------------- class ProjectMPlugin : public NativePluginClass { public: ProjectMPlugin(const NativeHostDescriptor* const host) : NativePluginClass(host), fPM(nullptr), fUI(nullptr) { } ~ProjectMPlugin() override { CARLA_SAFE_ASSERT(fUI == nullptr); } protected: // ------------------------------------------------------------------- // Plugin process calls void process(float** ins, float**, const uint32_t frames, const NativeMidiEvent* const, const uint32_t) override { const CarlaMutexLocker csm(fMutex); if (fPM == nullptr) return; fPM->pcm()->addPCMfloat(ins[0] ,frames); } // ------------------------------------------------------------------- // Plugin UI calls void uiShow(const bool show) override { if (show) { if (fUI == nullptr) fUI = new ProjectMUI(getHostHandle()); fUI->focus(); } else if (fUI != nullptr) { { const CarlaMutexLocker csm(fMutex); fPM = nullptr; } delete fUI; fUI = nullptr; } } void uiIdle() override { if (fUI == nullptr) return; fUI->idle(); if (fPM != nullptr) return; const CarlaMutexLocker csm(fMutex); fPM = fUI->getPM(); CARLA_SAFE_ASSERT(fPM != nullptr); } private: projectM* fPM; ProjectMUI* fUI; // need to ensure pm is valid CarlaMutex fMutex; PluginClassEND(ProjectMPlugin) CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ProjectMPlugin) }; // ----------------------------------------------------------------------- static const NativePluginDescriptor projectmDesc = { /* category */ PLUGIN_CATEGORY_UTILITY, /* hints */ static_cast(/*PLUGIN_IS_RTSAFE|*/PLUGIN_HAS_UI|PLUGIN_USES_PARENT_ID), /* supports */ static_cast(0x0), /* audioIns */ 1, /* audioOuts */ 0, /* midiIns */ 0, /* midiOuts */ 0, /* paramIns */ 0, /* paramOuts */ 0, /* name */ "ProjectM", /* label */ "projectm", /* maker */ "falkTX", /* copyright */ "GNU GPL v2+", PluginDescriptorFILL(ProjectMPlugin) }; // ----------------------------------------------------------------------- CARLA_EXPORT void carla_register_native_plugin_projectm() { carla_register_native_plugin(&projectmDesc); } // -----------------------------------------------------------------------