Carla: Implement bridge state restore
This commit is contained in:
parent
aeee5dac92
commit
7044f9288e
|
@ -112,7 +112,6 @@ c++/carla-native/zynaddsubfx/UI/VirKeyboard.h
|
|||
_/
|
||||
all-recheck
|
||||
c++/caitlyn/
|
||||
c++/carla/
|
||||
c++/carla-backend/carla-dynamic.lv2/
|
||||
c++/carla-backend/carla_backend_lv2.cpp
|
||||
c++/carla-backend/carla_backend_vst.cpp
|
||||
|
|
|
@ -20,10 +20,14 @@
|
|||
#include "carla_bridge_client.hpp"
|
||||
#include "carla_bridge_toolkit.hpp"
|
||||
#include "carla_plugin.hpp"
|
||||
#include "../carla/Shared.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QTextStream>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtXml/QDomDocument>
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
|
||||
# include <QtWidgets/QApplication>
|
||||
|
@ -221,12 +225,12 @@ public:
|
|||
m_uiShow = showUI;
|
||||
}
|
||||
|
||||
void guiClosedCallback();
|
||||
|
||||
protected:
|
||||
QApplication* app;
|
||||
CarlaBackend::CarlaPluginGUI* gui;
|
||||
|
||||
void guiClosedCallback();
|
||||
|
||||
private:
|
||||
bool m_hasUI;
|
||||
bool m_uiQuit;
|
||||
|
@ -253,6 +257,10 @@ public:
|
|||
msgTimerGUI = 0;
|
||||
msgTimerOSC = 0;
|
||||
|
||||
needsResize = 0;
|
||||
nextWidth = 0;
|
||||
nextHeight = 0;
|
||||
|
||||
engine = nullptr;
|
||||
plugin = nullptr;
|
||||
}
|
||||
|
@ -276,15 +284,12 @@ public:
|
|||
if (! plugin)
|
||||
return;
|
||||
|
||||
// create window if needed
|
||||
bool guiResizable;
|
||||
CarlaBackend::GuiType guiType;
|
||||
plugin->getGuiInfo(&guiType, &guiResizable);
|
||||
|
||||
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)
|
||||
{
|
||||
plugin->setGuiContainer(plugToolkit->getContainer());
|
||||
|
@ -332,10 +337,264 @@ public:
|
|||
CARLA_ASSERT(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()
|
||||
{
|
||||
CARLA_ASSERT(engine);
|
||||
|
@ -352,16 +611,6 @@ public:
|
|||
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
|
||||
|
||||
|
@ -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:
|
||||
int msgTimerGUI, msgTimerOSC;
|
||||
int msgTimerGUI;
|
||||
int msgTimerOSC;
|
||||
|
||||
bool needsResize;
|
||||
int nextWidth;
|
||||
int nextHeight;
|
||||
|
||||
CarlaBackend::CarlaEngine* engine;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
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)
|
||||
|
@ -579,7 +928,6 @@ protected:
|
|||
|
||||
void CarlaBridgeToolkitPlugin::show()
|
||||
{
|
||||
qDebug("----------------------------------------------------------------------------------------------------------");
|
||||
qDebug("CarlaBridgeToolkitPlugin::show()");
|
||||
CARLA_ASSERT(gui);
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
# QtCreator project file
|
||||
|
||||
contains(QT_VERSION, ^5.*) {
|
||||
QT = core widgets
|
||||
QT = core widgets xml
|
||||
} else {
|
||||
QT = core gui
|
||||
QT = core gui xml
|
||||
}
|
||||
|
||||
CONFIG = debug link_pkgconfig qt warn_on
|
||||
|
@ -25,6 +25,13 @@ HEADERS = \
|
|||
../carla_bridge_osc.hpp \
|
||||
../carla_bridge_toolkit.hpp \
|
||||
|
||||
# carla
|
||||
SOURCES += \
|
||||
../../carla/Shared.cpp
|
||||
|
||||
HEADERS += \
|
||||
../../carla/Shared.hpp
|
||||
|
||||
# carla-engine
|
||||
SOURCES += \
|
||||
../../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
|
4
doc/TODO
4
doc/TODO
|
@ -12,6 +12,10 @@ GUI:
|
|||
-------------------
|
||||
- APPS -
|
||||
|
||||
Cadence:
|
||||
- add freq info to systray tooltip
|
||||
- add freq Hz change
|
||||
|
||||
Claudia:
|
||||
- Cleanup DB
|
||||
- Handle sample-rate changes in JACK (made possible by switch-master)
|
||||
|
|
|
@ -267,6 +267,9 @@ CarlaSaveState = {
|
|||
'Chunk': None
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------------------------------------
|
||||
# Carla XML helpers
|
||||
|
||||
def getSaveStateDictFromXML(xmlNode):
|
||||
saveState = deepcopy(CarlaSaveState)
|
||||
|
||||
|
@ -378,7 +381,7 @@ def getSaveStateDictFromXML(xmlNode):
|
|||
cText = xmlSubData.toElement().text().strip()
|
||||
|
||||
if cTag == "type":
|
||||
stateCustomData['type'] = cText
|
||||
stateCustomData['type'] = xmlSafeString(cText, False)
|
||||
elif cTag == "key":
|
||||
stateCustomData['key'] = xmlSafeString(cText, False)
|
||||
elif cTag == "value":
|
||||
|
@ -1751,7 +1754,7 @@ class PluginWidget(QFrame, ui_carla_plugin.Ui_PluginWidget):
|
|||
# Part 1 - set custom data (except binary/chunks)
|
||||
|
||||
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'])
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
|
@ -1763,7 +1766,7 @@ class PluginWidget(QFrame, ui_carla_plugin.Ui_PluginWidget):
|
|||
programCount = Carla.host.get_program_count(self.m_pluginId)
|
||||
testProgramName = cString(Carla.host.get_program_name(self.m_pluginId, content['CurrentProgramIndex']))
|
||||
|
||||
# Program index and name matches
|
||||
# Program name matches
|
||||
if content['CurrentProgramName'] == testProgramName:
|
||||
programId = content['CurrentProgramIndex']
|
||||
|
||||
|
@ -1863,7 +1866,7 @@ class PluginWidget(QFrame, ui_carla_plugin.Ui_PluginWidget):
|
|||
# Part 5 - set chunk data
|
||||
|
||||
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'])
|
||||
|
||||
if content['Chunk']:
|
||||
|
|
Loading…
Reference in New Issue