@@ -112,7 +112,6 @@ c++/carla-native/zynaddsubfx/UI/VirKeyboard.h | |||||
_/ | _/ | ||||
all-recheck | all-recheck | ||||
c++/caitlyn/ | c++/caitlyn/ | ||||
c++/carla/ | |||||
c++/carla-backend/carla-dynamic.lv2/ | c++/carla-backend/carla-dynamic.lv2/ | ||||
c++/carla-backend/carla_backend_lv2.cpp | c++/carla-backend/carla_backend_lv2.cpp | ||||
c++/carla-backend/carla_backend_vst.cpp | c++/carla-backend/carla_backend_vst.cpp | ||||
@@ -20,10 +20,14 @@ | |||||
#include "carla_bridge_client.hpp" | #include "carla_bridge_client.hpp" | ||||
#include "carla_bridge_toolkit.hpp" | #include "carla_bridge_toolkit.hpp" | ||||
#include "carla_plugin.hpp" | #include "carla_plugin.hpp" | ||||
#include "../carla/Shared.hpp" | |||||
#include <set> | |||||
#include <QtCore/QDir> | #include <QtCore/QDir> | ||||
#include <QtCore/QFile> | #include <QtCore/QFile> | ||||
#include <QtCore/QTextStream> | #include <QtCore/QTextStream> | ||||
#include <QtCore/QThread> | |||||
#include <QtXml/QDomDocument> | |||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) | #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) | ||||
# include <QtWidgets/QApplication> | # include <QtWidgets/QApplication> | ||||
@@ -221,12 +225,12 @@ public: | |||||
m_uiShow = showUI; | m_uiShow = showUI; | ||||
} | } | ||||
void guiClosedCallback(); | |||||
protected: | protected: | ||||
QApplication* app; | QApplication* app; | ||||
CarlaBackend::CarlaPluginGUI* gui; | CarlaBackend::CarlaPluginGUI* gui; | ||||
void guiClosedCallback(); | |||||
private: | private: | ||||
bool m_hasUI; | bool m_hasUI; | ||||
bool m_uiQuit; | bool m_uiQuit; | ||||
@@ -253,6 +257,10 @@ public: | |||||
msgTimerGUI = 0; | msgTimerGUI = 0; | ||||
msgTimerOSC = 0; | msgTimerOSC = 0; | ||||
needsResize = 0; | |||||
nextWidth = 0; | |||||
nextHeight = 0; | |||||
engine = nullptr; | engine = nullptr; | ||||
plugin = nullptr; | plugin = nullptr; | ||||
} | } | ||||
@@ -276,15 +284,12 @@ public: | |||||
if (! plugin) | if (! plugin) | ||||
return; | return; | ||||
// create window if needed | |||||
bool guiResizable; | bool guiResizable; | ||||
CarlaBackend::GuiType guiType; | CarlaBackend::GuiType guiType; | ||||
plugin->getGuiInfo(&guiType, &guiResizable); | plugin->getGuiInfo(&guiType, &guiResizable); | ||||
CarlaBridgeToolkitPlugin* const plugToolkit = (CarlaBridgeToolkitPlugin*)m_toolkit; | CarlaBridgeToolkitPlugin* const plugToolkit = (CarlaBridgeToolkitPlugin*)m_toolkit; | ||||
qWarning("----------------------------------------------------- trying..., %s", CarlaBackend::GuiType2Str(guiType)); | |||||
if (guiType == CarlaBackend::GUI_INTERNAL_QT4 || guiType == CarlaBackend::GUI_INTERNAL_COCOA || guiType == CarlaBackend::GUI_INTERNAL_HWND || guiType == CarlaBackend::GUI_INTERNAL_X11) | if (guiType == CarlaBackend::GUI_INTERNAL_QT4 || guiType == CarlaBackend::GUI_INTERNAL_COCOA || guiType == CarlaBackend::GUI_INTERNAL_HWND || guiType == CarlaBackend::GUI_INTERNAL_X11) | ||||
{ | { | ||||
plugin->setGuiContainer(plugToolkit->getContainer()); | plugin->setGuiContainer(plugToolkit->getContainer()); | ||||
@@ -332,10 +337,264 @@ public: | |||||
CARLA_ASSERT(plugin); | CARLA_ASSERT(plugin); | ||||
this->plugin = plugin; | this->plugin = plugin; | ||||
// load carla plugin preset if possible | |||||
if (const char* const pName = plugin->name()) | |||||
{ | |||||
QFile pFile(QDir::currentPath() + QDir::separator() + pName + ".carxs"); | |||||
qDebug("Trying to load plugin preset file '%s'", pFile.fileName().toUtf8().constData()); | |||||
if (! /*(*/pFile.exists() /*&& pFile.isReadable())*/) | |||||
{ | |||||
qDebug("Plugin preset file doesn't exist or is not readable"); | |||||
return; | |||||
} | |||||
if (! pFile.open(QFile::ReadOnly)) | |||||
{ | |||||
qWarning("Plugin preset file read failed"); | |||||
return; | |||||
} | |||||
QDomDocument xml; | |||||
xml.setContent(pFile.readAll()); | |||||
QDomElement xmlNode(xml.documentElement()); | |||||
if (xmlNode.tagName() == "CARLA-PRESET") | |||||
{ | |||||
loadStateDict(getSaveStateDictFromXML(xmlNode)); | |||||
} | |||||
else | |||||
qWarning("Plugin preset file is not valid or corrupted"); | |||||
pFile.close(); | |||||
} | |||||
} | } | ||||
// --------------------------------------------------------------------- | // --------------------------------------------------------------------- | ||||
void loadStateDict(const CarlaSaveState* const content) | |||||
{ | |||||
CARLA_ASSERT(content); | |||||
if (! content) | |||||
return; | |||||
qDebug("Loading plugin state now..."); | |||||
// --------------------------------------------------------------------- | |||||
// Part 1 - set custom data (except chunks) | |||||
foreach (const CarlaStateCustomData& customData, content->customData) | |||||
{ | |||||
if (customData.type != CUSTOM_DATA_CHUNK) | |||||
{ | |||||
const char* const type = customData.type.toUtf8().constData(); | |||||
const char* const key = customData.key.toUtf8().constData(); | |||||
const char* const value = customData.value.toUtf8().constData(); | |||||
plugin->setCustomData(type, key, value, true); | |||||
} | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
// Part 2 - set program | |||||
int32_t programId = -1; | |||||
if (! content->currentProgramName.isEmpty()) | |||||
{ | |||||
const uint32_t programCount = plugin->programCount(); | |||||
char strBuf[STR_MAX] = { 0 }; | |||||
plugin->getProgramName(content->currentProgramIndex, strBuf); | |||||
QString testProgramName(strBuf); | |||||
// Program name matches | |||||
if (content->currentProgramName == testProgramName) | |||||
{ | |||||
programId = content->currentProgramIndex; | |||||
} | |||||
// index < count | |||||
else if (content->currentProgramIndex < (int32_t)programCount) | |||||
{ | |||||
programId = content->currentProgramIndex; | |||||
} | |||||
// index not valid, try to find by name | |||||
else | |||||
{ | |||||
for (uint32_t i=0; i < programCount; i++) | |||||
{ | |||||
plugin->getProgramName(i, strBuf); | |||||
testProgramName = QString(strBuf); | |||||
if (content->currentProgramName == testProgramName) | |||||
{ | |||||
programId = i; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
// set program now, if valid | |||||
if (programId >= 0) | |||||
{ | |||||
plugin->setProgram(programId, true, true, false, true); | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
// Part 3 - set midi program | |||||
if (content->currentMidiBank >= 0 and content->currentMidiProgram >= 0) | |||||
{ | |||||
const uint32_t midiProgramCount = plugin->midiProgramCount(); | |||||
for (uint32_t i=0; i < midiProgramCount; i++) | |||||
{ | |||||
const MidiProgramData* const midiProgramData = plugin->midiProgramData(i); | |||||
if ((int32_t)midiProgramData->bank == content->currentMidiBank && (int32_t)midiProgramData->program == content->currentMidiProgram) | |||||
{ | |||||
plugin->setMidiProgram(i, true, true, false, true); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
// Part 4a - get plugin parameter symbols | |||||
struct ParamSymbol { | |||||
uint32_t index; | |||||
QString symbol; | |||||
ParamSymbol() {} | |||||
ParamSymbol(uint32_t index_, const char* symbol_) | |||||
: index(index_), | |||||
symbol(symbol_) {} | |||||
}; | |||||
QVector<ParamSymbol> paramSymbols; | |||||
foreach (const CarlaStateParameter& parameter, content->parameters) | |||||
{ | |||||
if (! parameter.symbol.isEmpty()) | |||||
{ | |||||
char strBuf[STR_MAX] = { 0 }; | |||||
plugin->getParameterSymbol(parameter.index, strBuf); | |||||
if (strBuf[0] != 0) | |||||
{ | |||||
ParamSymbol ps(parameter.index, strBuf); | |||||
paramSymbols.append(ps); | |||||
} | |||||
} | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
// Part 4b - set parameter values (carefully) | |||||
const double sampleRate = engine->getSampleRate(); | |||||
foreach (const CarlaStateParameter& parameter, content->parameters) | |||||
{ | |||||
int32_t index = -1; | |||||
if (content->type == "LADSPA") | |||||
{ | |||||
// Try to set by symbol, otherwise use index | |||||
if (! parameter.symbol.isEmpty()) | |||||
{ | |||||
bool breaked = false; | |||||
foreach (const ParamSymbol& ps, paramSymbols) | |||||
{ | |||||
if (parameter.symbol == ps.symbol) | |||||
{ | |||||
index = ps.index; | |||||
breaked = true; | |||||
break; | |||||
} | |||||
} | |||||
if (! breaked) | |||||
index = parameter.index; | |||||
} | |||||
else | |||||
index = parameter.index; | |||||
} | |||||
else if (content->type == "LV2") | |||||
{ | |||||
// Symbol only | |||||
if (! parameter.symbol.isEmpty()) | |||||
{ | |||||
bool breaked = false; | |||||
foreach (const ParamSymbol& ps, paramSymbols) | |||||
{ | |||||
if (parameter.symbol == ps.symbol) | |||||
{ | |||||
index = ps.index; | |||||
breaked = true; | |||||
break; | |||||
} | |||||
} | |||||
if (! breaked) | |||||
qWarning("Failed to find LV2 parameter symbol for '%s')", parameter.symbol.toUtf8().constData()); | |||||
} | |||||
else | |||||
qWarning("LV2 Plugin parameter '%s' has no symbol", parameter.name.toUtf8().constData()); | |||||
} | |||||
else | |||||
{ | |||||
// Index only | |||||
index = parameter.index; | |||||
} | |||||
// Now set parameter | |||||
if (index >= 0) | |||||
{ | |||||
const ParameterData* const paramData = plugin->parameterData(index); | |||||
double value = parameter.value; | |||||
if (paramData->hints & PARAMETER_USES_SAMPLERATE) | |||||
value *= sampleRate; | |||||
plugin->setParameterValue(index, value, true, true, false); | |||||
plugin->setParameterMidiCC(index, parameter.midiCC, true, false); | |||||
plugin->setParameterMidiChannel(index, parameter.midiChannel-1, true, false); | |||||
} | |||||
else | |||||
qWarning("Could not set parameter data for '%s')", parameter.name.toUtf8().constData()); | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
// Part 5 - set chunk data | |||||
foreach (const CarlaStateCustomData& customData, content->customData) | |||||
{ | |||||
if (customData.type == CUSTOM_DATA_CHUNK) | |||||
{ | |||||
const char* const type = customData.type.toUtf8().constData(); | |||||
const char* const key = customData.key.toUtf8().constData(); | |||||
const char* const value = customData.value.toUtf8().constData(); | |||||
plugin->setCustomData(type, key, value, true); | |||||
} | |||||
} | |||||
if (! content->chunk.isEmpty()) | |||||
plugin->setChunkData(content->chunk.toUtf8().constData()); | |||||
qDebug("Loading plugin state now finished"); | |||||
} | |||||
void guiClosed() | void guiClosed() | ||||
{ | { | ||||
CARLA_ASSERT(engine); | CARLA_ASSERT(engine); | ||||
@@ -352,16 +611,6 @@ public: | |||||
plugin->showGui(yesNo); | plugin->showGui(yesNo); | ||||
} | } | ||||
// --------------------------------------------------------------------- | |||||
static void callback(void* const ptr, CarlaBackend::CallbackType const action, const unsigned short, const int value1, const int value2, const double value3, const char* const valueStr) | |||||
{ | |||||
CARLA_ASSERT(ptr); | |||||
if (CarlaPluginClient* const _this_ = (CarlaPluginClient*)ptr) | |||||
_this_->handleCallback(action, value1, value2, value3, valueStr); | |||||
} | |||||
// --------------------------------------------------------------------- | // --------------------------------------------------------------------- | ||||
// processing | // processing | ||||
@@ -534,19 +783,119 @@ public: | |||||
} | } | ||||
// --------------------------------------------------------------------- | // --------------------------------------------------------------------- | ||||
// callback | |||||
static void callback(void* const ptr, CarlaBackend::CallbackType const action, const unsigned short, const int value1, const int value2, const double value3, const char* const valueStr) | |||||
{ | |||||
CARLA_ASSERT(ptr); | |||||
if (CarlaPluginClient* const _this_ = (CarlaPluginClient*)ptr) | |||||
_this_->handleCallback(action, value1, value2, value3, valueStr); | |||||
} | |||||
// --------------------------------------------------------------------- | |||||
protected: | protected: | ||||
int msgTimerGUI, msgTimerOSC; | |||||
int msgTimerGUI; | |||||
int msgTimerOSC; | |||||
bool needsResize; | |||||
int nextWidth; | |||||
int nextHeight; | |||||
CarlaBackend::CarlaEngine* engine; | CarlaBackend::CarlaEngine* engine; | ||||
CarlaBackend::CarlaPlugin* plugin; | CarlaBackend::CarlaPlugin* plugin; | ||||
std::set<int32_t> parametersToUpdate; | |||||
void handleCallback(const CarlaBackend::CallbackType action, const int value1, const int value2, const double value3, const char* const valueStr) | void handleCallback(const CarlaBackend::CallbackType action, const int value1, const int value2, const double value3, const char* const valueStr) | ||||
{ | { | ||||
qDebug("CarlaPluginClient::handleCallback(%s, %i, %i, %g \"%s\")", CarlaBackend::CallbackType2Str(action), value1, value2, value3, valueStr); | |||||
CARLA_BACKEND_USE_NAMESPACE | |||||
qDebug("CarlaPluginClient::handleCallback(%s, %i, %i, %g \"%s\")", CallbackType2Str(action), value1, value2, value3, valueStr); | |||||
if (! engine) | if (! engine) | ||||
return; | return; | ||||
switch (action) | |||||
{ | |||||
case CALLBACK_DEBUG: | |||||
break; | |||||
case CALLBACK_PARAMETER_VALUE_CHANGED: | |||||
parametersToUpdate.insert(value1); | |||||
break; | |||||
case CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED: | |||||
// todo, unused? | |||||
break; | |||||
case CALLBACK_PARAMETER_MIDI_CC_CHANGED: | |||||
// todo, unused? | |||||
break; | |||||
case CALLBACK_PROGRAM_CHANGED: | |||||
engine->osc_send_bridge_set_program(value1); | |||||
break; | |||||
case CALLBACK_MIDI_PROGRAM_CHANGED: | |||||
engine->osc_send_bridge_set_midi_program(value1); | |||||
break; | |||||
case CALLBACK_NOTE_ON: | |||||
// todo | |||||
break; | |||||
case CALLBACK_NOTE_OFF: | |||||
// todo | |||||
break; | |||||
case CALLBACK_SHOW_GUI: | |||||
if (value1 == 0) | |||||
{ | |||||
CarlaBridgeToolkitPlugin* const plugToolkit = (CarlaBridgeToolkitPlugin*)m_toolkit; | |||||
plugToolkit->guiClosedCallback(); | |||||
} | |||||
break; | |||||
case CALLBACK_RESIZE_GUI: | |||||
nextWidth = value1; | |||||
nextHeight = value2; | |||||
needsResize = true; | |||||
break; | |||||
case CALLBACK_UPDATE: | |||||
// todo | |||||
break; | |||||
case CALLBACK_RELOAD_INFO: | |||||
// todo | |||||
break; | |||||
case CALLBACK_RELOAD_PARAMETERS: | |||||
// todo | |||||
break; | |||||
case CALLBACK_RELOAD_PROGRAMS: | |||||
// todo | |||||
break; | |||||
case CALLBACK_RELOAD_ALL: | |||||
// todo | |||||
break; | |||||
case CALLBACK_NSM_ANNOUNCE: | |||||
case CALLBACK_NSM_OPEN1: | |||||
case CALLBACK_NSM_OPEN2: | |||||
case CALLBACK_NSM_SAVE: | |||||
break; | |||||
case CALLBACK_ERROR: | |||||
break; | |||||
case CALLBACK_QUIT: | |||||
m_toolkit->quit(); | |||||
break; | |||||
} | |||||
} | } | ||||
void timerEvent(QTimerEvent* const event) | void timerEvent(QTimerEvent* const event) | ||||
@@ -579,7 +928,6 @@ protected: | |||||
void CarlaBridgeToolkitPlugin::show() | void CarlaBridgeToolkitPlugin::show() | ||||
{ | { | ||||
qDebug("----------------------------------------------------------------------------------------------------------"); | |||||
qDebug("CarlaBridgeToolkitPlugin::show()"); | qDebug("CarlaBridgeToolkitPlugin::show()"); | ||||
CARLA_ASSERT(gui); | CARLA_ASSERT(gui); | ||||
@@ -1,9 +1,9 @@ | |||||
# QtCreator project file | # QtCreator project file | ||||
contains(QT_VERSION, ^5.*) { | contains(QT_VERSION, ^5.*) { | ||||
QT = core widgets | |||||
QT = core widgets xml | |||||
} else { | } else { | ||||
QT = core gui | |||||
QT = core gui xml | |||||
} | } | ||||
CONFIG = debug link_pkgconfig qt warn_on | CONFIG = debug link_pkgconfig qt warn_on | ||||
@@ -25,6 +25,13 @@ HEADERS = \ | |||||
../carla_bridge_osc.hpp \ | ../carla_bridge_osc.hpp \ | ||||
../carla_bridge_toolkit.hpp \ | ../carla_bridge_toolkit.hpp \ | ||||
# carla | |||||
SOURCES += \ | |||||
../../carla/Shared.cpp | |||||
HEADERS += \ | |||||
../../carla/Shared.hpp | |||||
# carla-engine | # carla-engine | ||||
SOURCES += \ | SOURCES += \ | ||||
../../carla-engine/carla_engine.cpp \ | ../../carla-engine/carla_engine.cpp \ | ||||
@@ -0,0 +1,257 @@ | |||||
/* | |||||
* Carla (frontend) | |||||
* Copyright (C) 2012 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 COPYING file | |||||
*/ | |||||
#include "Shared.hpp" | |||||
//#define __STDC_LIMIT_MACROS | |||||
//#include <cstdint> | |||||
CARLA_BACKEND_USE_NAMESPACE | |||||
// ------------------------------------------------------------------------------------------------ | |||||
// Carla Host object | |||||
CarlaHostObject Carla; | |||||
// ------------------------------------------------------------------------------------------------------------ | |||||
// Carla XML helpers | |||||
const CarlaSaveState* getSaveStateDictFromXML(const QDomNode& xmlNode) | |||||
{ | |||||
static CarlaSaveState saveState; | |||||
saveState.reset(); | |||||
QDomNode node(xmlNode.firstChild()); | |||||
while (! node.isNull()) | |||||
{ | |||||
// ------------------------------------------------------ | |||||
// Info | |||||
if (node.toElement().tagName() == "Info") | |||||
{ | |||||
QDomNode xmlInfo(node.toElement().firstChild()); | |||||
while (! xmlInfo.isNull()) | |||||
{ | |||||
const QString tag = xmlInfo.toElement().tagName(); | |||||
const QString text = xmlInfo.toElement().text(); //.strip(); | |||||
if (tag == "Type") | |||||
saveState.type = text; | |||||
else if (tag == "Name") | |||||
saveState.name = xmlSafeString(text, false); | |||||
else if (tag == "Label" || tag == "URI") | |||||
saveState.label = xmlSafeString(text, false); | |||||
else if (tag == "Binary") | |||||
saveState.binary = xmlSafeString(text, false); | |||||
else if (tag == "UniqueID") | |||||
{ | |||||
bool ok; | |||||
long uniqueID = text.toLong(&ok); | |||||
if (ok) saveState.uniqueID = uniqueID; | |||||
} | |||||
xmlInfo = xmlInfo.nextSibling(); | |||||
} | |||||
} | |||||
// ------------------------------------------------------ | |||||
// Data | |||||
else if (node.toElement().tagName() == "Data") | |||||
{ | |||||
QDomNode xmlData(node.toElement().firstChild()); | |||||
while (! xmlData.isNull()) | |||||
{ | |||||
const QString tag = xmlData.toElement().tagName(); | |||||
const QString text = xmlData.toElement().text(); //.strip(); | |||||
// ---------------------------------------------- | |||||
// Internal Data | |||||
if (tag == "Active") | |||||
{ | |||||
saveState.active = bool(text == "Yes"); | |||||
} | |||||
else if (tag == "DryWet") | |||||
{ | |||||
bool ok; | |||||
double value = text.toDouble(&ok); | |||||
if (ok) saveState.dryWet = value; | |||||
} | |||||
else if (tag == "Volume") | |||||
{ | |||||
bool ok; | |||||
double value = text.toDouble(&ok); | |||||
if (ok) saveState.volume = value; | |||||
} | |||||
else if (tag == "Balance-Left") | |||||
{ | |||||
bool ok; | |||||
double value = text.toDouble(&ok); | |||||
if (ok) saveState.balanceLeft = value; | |||||
} | |||||
else if (tag == "Balance-Right") | |||||
{ | |||||
bool ok; | |||||
double value = text.toDouble(&ok); | |||||
if (ok) saveState.balanceRight = value; | |||||
} | |||||
// ---------------------------------------------- | |||||
// Program (current) | |||||
else if (tag == "CurrentProgramIndex") | |||||
{ | |||||
bool ok; | |||||
int value = text.toInt(&ok); | |||||
if (ok) saveState.currentProgramIndex = value; | |||||
} | |||||
else if (tag == "CurrentProgramName") | |||||
{ | |||||
saveState.currentProgramName = xmlSafeString(text, false); | |||||
} | |||||
// ---------------------------------------------- | |||||
// Midi Program (current) | |||||
else if (tag == "CurrentMidiBank") | |||||
{ | |||||
bool ok; | |||||
int value = text.toInt(&ok); | |||||
if (ok) saveState.currentMidiBank = value; | |||||
} | |||||
else if (tag == "CurrentMidiProgram") | |||||
{ | |||||
bool ok; | |||||
int value = text.toInt(&ok); | |||||
if (ok) saveState.currentMidiProgram = value; | |||||
} | |||||
// ---------------------------------------------- | |||||
// Parameters | |||||
else if (tag == "Parameter") | |||||
{ | |||||
CarlaStateParameter stateParameter; | |||||
QDomNode xmlSubData(xmlData.toElement().firstChild()); | |||||
while (! xmlSubData.isNull()) | |||||
{ | |||||
const QString pTag = xmlSubData.toElement().tagName(); | |||||
const QString pText = xmlSubData.toElement().text(); //.strip(); | |||||
if (pTag == "index") | |||||
{ | |||||
bool ok; | |||||
uint index = pText.toUInt(&ok); | |||||
if (ok) stateParameter.index = index; | |||||
} | |||||
else if (pTag == "name") | |||||
{ | |||||
stateParameter.name = xmlSafeString(pText, false); | |||||
} | |||||
else if (pTag == "symbol") | |||||
{ | |||||
stateParameter.symbol = xmlSafeString(pText, false); | |||||
} | |||||
else if (pTag == "value") | |||||
{ | |||||
bool ok; | |||||
double value = pText.toDouble(&ok); | |||||
if (ok) stateParameter.value = value; | |||||
} | |||||
else if (pTag == "midiChannel") | |||||
{ | |||||
bool ok; | |||||
uint channel = pText.toUInt(&ok); | |||||
if (ok && channel < 16) | |||||
stateParameter.midiChannel = channel; | |||||
} | |||||
else if (pTag == "midiCC") | |||||
{ | |||||
bool ok; | |||||
int cc = pText.toInt(&ok); | |||||
if (ok && cc < INT16_MAX) | |||||
stateParameter.midiCC = cc; | |||||
} | |||||
xmlSubData = xmlSubData.nextSibling(); | |||||
} | |||||
saveState.parameters.append(stateParameter); | |||||
} | |||||
// ---------------------------------------------- | |||||
// Custom Data | |||||
else if (tag == "CustomData") | |||||
{ | |||||
CarlaStateCustomData stateCustomData; | |||||
QDomNode xmlSubData(xmlData.toElement().firstChild()); | |||||
while (! xmlSubData.isNull()) | |||||
{ | |||||
const QString cTag = xmlSubData.toElement().tagName(); | |||||
const QString cText = xmlSubData.toElement().text(); //.strip(); | |||||
if (cTag == "type") | |||||
stateCustomData.type = xmlSafeString(cText, false); | |||||
else if (cTag == "key") | |||||
stateCustomData.key = xmlSafeString(cText, false); | |||||
else if (cTag == "value") | |||||
stateCustomData.value = xmlSafeString(cText, false); | |||||
xmlSubData = xmlSubData.nextSibling(); | |||||
} | |||||
saveState.customData.append(stateCustomData); | |||||
} | |||||
// ---------------------------------------------- | |||||
// Chunk | |||||
else if (tag == "Chunk") | |||||
{ | |||||
saveState.chunk = xmlSafeString(text, false); | |||||
} | |||||
// ---------------------------------------------- | |||||
xmlData = xmlData.nextSibling(); | |||||
} | |||||
} | |||||
// ------------------------------------------------------ | |||||
node = node.nextSibling(); | |||||
} | |||||
return &saveState; | |||||
} | |||||
QString xmlSafeString(QString string, const bool toXml) | |||||
{ | |||||
if (toXml) | |||||
return string.replace("&", "&").replace("<","<").replace(">",">").replace("'","'").replace("\"","""); | |||||
else | |||||
return string.replace("&", "&").replace("<","<").replace(">",">").replace("'","'").replace(""","\""); | |||||
} |
@@ -0,0 +1,151 @@ | |||||
/* | |||||
* Carla (frontend) | |||||
* Copyright (C) 2012 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 COPYING file | |||||
*/ | |||||
#ifndef SHARED_HPP | |||||
#define SHARED_HPP | |||||
#include "carla_backend.hpp" | |||||
#include <QtCore/QString> | |||||
#include <QtCore/QVector> | |||||
#include <QtXml/QDomNode> | |||||
CARLA_BACKEND_USE_NAMESPACE | |||||
#define VERSION "0.5.0" | |||||
// ------------------------------------------------------------------------------------------------ | |||||
// Carla Host object | |||||
struct CarlaHostObject { | |||||
void* host; | |||||
void* gui; | |||||
bool isControl; | |||||
ProcessMode processMode; | |||||
unsigned int maxParameters; | |||||
CarlaHostObject() | |||||
: host(nullptr), | |||||
gui(nullptr), | |||||
isControl(false), | |||||
processMode(PROCESS_MODE_CONTINUOUS_RACK), | |||||
maxParameters(MAX_PARAMETERS) {} | |||||
}; | |||||
extern CarlaHostObject Carla; | |||||
// ------------------------------------------------------------------------------------------------ | |||||
// Carla GUI stuff | |||||
#define ICON_STATE_NULL 0 | |||||
#define ICON_STATE_WAIT 1 | |||||
#define ICON_STATE_OFF 2 | |||||
#define ICON_STATE_ON 3 | |||||
#define PALETTE_COLOR_NONE 0 | |||||
#define PALETTE_COLOR_WHITE 1 | |||||
#define PALETTE_COLOR_RED 2 | |||||
#define PALETTE_COLOR_GREEN 3 | |||||
#define PALETTE_COLOR_BLUE 4 | |||||
#define PALETTE_COLOR_YELLOW 5 | |||||
#define PALETTE_COLOR_ORANGE 6 | |||||
#define PALETTE_COLOR_BROWN 7 | |||||
#define PALETTE_COLOR_PINK 8 | |||||
struct CarlaStateParameter { | |||||
uint32_t index; | |||||
QString name; | |||||
QString symbol; | |||||
double value; | |||||
uint8_t midiChannel; | |||||
int16_t midiCC; | |||||
CarlaStateParameter() | |||||
: index(0), | |||||
value(0.0), | |||||
midiChannel(1), | |||||
midiCC(-1) {} | |||||
}; | |||||
struct CarlaStateCustomData { | |||||
QString type; | |||||
QString key; | |||||
QString value; | |||||
CarlaStateCustomData() {} | |||||
}; | |||||
struct CarlaSaveState { | |||||
QString type; | |||||
QString name; | |||||
QString label; | |||||
QString binary; | |||||
long uniqueID; | |||||
bool active; | |||||
double dryWet; | |||||
double volume; | |||||
double balanceLeft; | |||||
double balanceRight; | |||||
QVector<CarlaStateParameter> parameters; | |||||
int32_t currentProgramIndex; | |||||
QString currentProgramName; | |||||
int32_t currentMidiBank; | |||||
int32_t currentMidiProgram; | |||||
QVector<CarlaStateCustomData> customData; | |||||
QString chunk; | |||||
CarlaSaveState() | |||||
: uniqueID(0), | |||||
active(false), | |||||
dryWet(1.0), | |||||
volume(1.0), | |||||
balanceLeft(-1.0), | |||||
balanceRight(1.0), | |||||
currentProgramIndex(-1), | |||||
currentMidiBank(-1), | |||||
currentMidiProgram(-1) {} | |||||
void reset() | |||||
{ | |||||
type.clear(); | |||||
name.clear(); | |||||
label.clear(); | |||||
binary.clear(); | |||||
uniqueID = 0; | |||||
active = false; | |||||
dryWet = 1.0; | |||||
volume = 1.0; | |||||
balanceLeft = -1.0; | |||||
balanceRight = 1.0; | |||||
parameters.clear(); | |||||
currentProgramIndex = -1; | |||||
currentProgramName.clear(); | |||||
currentMidiBank = -1; | |||||
currentMidiProgram = -1; | |||||
customData.clear(); | |||||
chunk.clear(); | |||||
} | |||||
}; | |||||
// ------------------------------------------------------------------------------------------------------------ | |||||
// Carla XML helpers | |||||
const CarlaSaveState* getSaveStateDictFromXML(const QDomNode& xmlNode); | |||||
QString xmlSafeString(QString string, const bool toXml); | |||||
#endif // SHARED_HPP |
@@ -12,6 +12,10 @@ GUI: | |||||
------------------- | ------------------- | ||||
- APPS - | - APPS - | ||||
Cadence: | |||||
- add freq info to systray tooltip | |||||
- add freq Hz change | |||||
Claudia: | Claudia: | ||||
- Cleanup DB | - Cleanup DB | ||||
- Handle sample-rate changes in JACK (made possible by switch-master) | - Handle sample-rate changes in JACK (made possible by switch-master) | ||||
@@ -267,6 +267,9 @@ CarlaSaveState = { | |||||
'Chunk': None | 'Chunk': None | ||||
} | } | ||||
# ------------------------------------------------------------------------------------------------------------ | |||||
# Carla XML helpers | |||||
def getSaveStateDictFromXML(xmlNode): | def getSaveStateDictFromXML(xmlNode): | ||||
saveState = deepcopy(CarlaSaveState) | saveState = deepcopy(CarlaSaveState) | ||||
@@ -378,7 +381,7 @@ def getSaveStateDictFromXML(xmlNode): | |||||
cText = xmlSubData.toElement().text().strip() | cText = xmlSubData.toElement().text().strip() | ||||
if cTag == "type": | if cTag == "type": | ||||
stateCustomData['type'] = cText | |||||
stateCustomData['type'] = xmlSafeString(cText, False) | |||||
elif cTag == "key": | elif cTag == "key": | ||||
stateCustomData['key'] = xmlSafeString(cText, False) | stateCustomData['key'] = xmlSafeString(cText, False) | ||||
elif cTag == "value": | elif cTag == "value": | ||||
@@ -1751,7 +1754,7 @@ class PluginWidget(QFrame, ui_carla_plugin.Ui_PluginWidget): | |||||
# Part 1 - set custom data (except binary/chunks) | # Part 1 - set custom data (except binary/chunks) | ||||
for customData in content['CustomData']: | for customData in content['CustomData']: | ||||
if customData['type'] not in (CUSTOM_DATA_BINARY, CUSTOM_DATA_CHUNK): | |||||
if customData['type'] != CUSTOM_DATA_CHUNK: | |||||
Carla.host.set_custom_data(self.m_pluginId, customData['type'], customData['key'], customData['value']) | Carla.host.set_custom_data(self.m_pluginId, customData['type'], customData['key'], customData['value']) | ||||
# --------------------------------------------------------------------- | # --------------------------------------------------------------------- | ||||
@@ -1763,7 +1766,7 @@ class PluginWidget(QFrame, ui_carla_plugin.Ui_PluginWidget): | |||||
programCount = Carla.host.get_program_count(self.m_pluginId) | programCount = Carla.host.get_program_count(self.m_pluginId) | ||||
testProgramName = cString(Carla.host.get_program_name(self.m_pluginId, content['CurrentProgramIndex'])) | testProgramName = cString(Carla.host.get_program_name(self.m_pluginId, content['CurrentProgramIndex'])) | ||||
# Program index and name matches | |||||
# Program name matches | |||||
if content['CurrentProgramName'] == testProgramName: | if content['CurrentProgramName'] == testProgramName: | ||||
programId = content['CurrentProgramIndex'] | programId = content['CurrentProgramIndex'] | ||||
@@ -1863,7 +1866,7 @@ class PluginWidget(QFrame, ui_carla_plugin.Ui_PluginWidget): | |||||
# Part 5 - set chunk data | # Part 5 - set chunk data | ||||
for customData in content['CustomData']: | for customData in content['CustomData']: | ||||
if customData['type'] in (CUSTOM_DATA_BINARY, CUSTOM_DATA_CHUNK): | |||||
if customData['type'] == CUSTOM_DATA_CHUNK: | |||||
Carla.host.set_custom_data(self.m_pluginId, customData['type'], customData['key'], customData['value']) | Carla.host.set_custom_data(self.m_pluginId, customData['type'], customData['key'], customData['value']) | ||||
if content['Chunk']: | if content['Chunk']: | ||||