|
|
@@ -0,0 +1,424 @@ |
|
|
|
/* |
|
|
|
* Carla Native Plugins |
|
|
|
* Copyright (C) 2012-2014 Filipe Coelho <falktx@falktx.com> |
|
|
|
* |
|
|
|
* 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<projectMKeycode>(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<NativePluginHints>(/*PLUGIN_IS_RTSAFE|*/PLUGIN_HAS_UI|PLUGIN_USES_PARENT_ID), |
|
|
|
/* supports */ static_cast<NativePluginSupports>(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); |
|
|
|
} |
|
|
|
|
|
|
|
// ----------------------------------------------------------------------- |