@@ -352,6 +352,11 @@ static const char* const CUSTOM_DATA_TYPE_BOOLEAN = "http://kxstudio.sf.net/ns/c | |||
*/ | |||
static const char* const CUSTOM_DATA_TYPE_CHUNK = "http://kxstudio.sf.net/ns/carla/chunk"; | |||
/*! | |||
* Property type URI. | |||
*/ | |||
static const char* const CUSTOM_DATA_TYPE_PROPERTY = "http://kxstudio.sf.net/ns/carla/property"; | |||
/*! | |||
* String type URI. | |||
*/ | |||
@@ -370,11 +375,6 @@ static const char* const CUSTOM_DATA_TYPE_STRING = "http://kxstudio.sf.net/ns/ca | |||
* @{ | |||
*/ | |||
/*! | |||
* Plugin options key. | |||
*/ | |||
static const char* const CUSTOM_DATA_KEY_PLUGIN_OPTIONS = "CarlaPluginOptions"; | |||
/*! | |||
* UI position key. | |||
*/ | |||
@@ -889,6 +889,35 @@ protected: | |||
fUiServer.flushMessages(); | |||
} | |||
void uiServerSendPluginProperties(CarlaPlugin* const plugin) | |||
{ | |||
const CarlaMutexLocker cml(fUiServer.getPipeLock()); | |||
const uint pluginId(plugin->getId()); | |||
uint32_t count = plugin->getCustomDataCount(); | |||
std::sprintf(fTmpBuf, "CUSTOM_DATA_COUNT_%i:%i\n", pluginId, count); | |||
fUiServer.writeMessage(fTmpBuf); | |||
for (uint32_t i=0; i<count; ++i) | |||
{ | |||
const CustomData& customData(plugin->getCustomData(i)); | |||
CARLA_SAFE_ASSERT_CONTINUE(customData.isValid()); | |||
if (std::strcmp(customData.type, CUSTOM_DATA_TYPE_PROPERTY) != 0) | |||
continue; | |||
std::sprintf(fTmpBuf, "CUSTOM_DATA_%i:%i\n", pluginId, i); | |||
fUiServer.writeMessage(fTmpBuf); | |||
fUiServer.writeAndFixMessage(customData.type); | |||
fUiServer.writeAndFixMessage(customData.key); | |||
fUiServer.writeAndFixMessage(customData.value); | |||
} | |||
fUiServer.flushMessages(); | |||
} | |||
void uiServerCallback(const EngineCallbackOpcode action, const uint pluginId, const int value1, const int value2, const float value3, const char* const valueStr) | |||
{ | |||
if (! fIsRunning) | |||
@@ -900,6 +929,16 @@ protected: | |||
switch (action) | |||
{ | |||
case ENGINE_CALLBACK_UPDATE: | |||
plugin = getPlugin(pluginId); | |||
if (plugin != nullptr && plugin->isEnabled()) | |||
{ | |||
CARLA_SAFE_ASSERT_BREAK(plugin->getId() == pluginId); | |||
uiServerSendPluginProperties(plugin); | |||
} | |||
break; | |||
case ENGINE_CALLBACK_RELOAD_INFO: | |||
plugin = getPlugin(pluginId); | |||
@@ -940,6 +979,7 @@ protected: | |||
uiServerSendPluginInfo(plugin); | |||
uiServerSendPluginParameters(plugin); | |||
uiServerSendPluginPrograms(plugin); | |||
uiServerSendPluginProperties(plugin); | |||
} | |||
break; | |||
@@ -622,6 +622,7 @@ void CarlaPlugin::loadStateSave(const CarlaStateSave& stateSave) | |||
CARLA_SAFE_ASSERT_CONTINUE(stateCustomData != nullptr); | |||
CARLA_SAFE_ASSERT_CONTINUE(stateCustomData->isValid()); | |||
const char* const type(stateCustomData->type); | |||
const char* const key(stateCustomData->key); | |||
if (getType() == PLUGIN_DSSI && (std::strcmp(key, "reloadprograms") == 0 || std::strcmp(key, "load") == 0 || std::strncmp(key, "patches", 7) == 0)) | |||
@@ -629,7 +630,7 @@ void CarlaPlugin::loadStateSave(const CarlaStateSave& stateSave) | |||
if (usesMultiProgs && std::strcmp(key, "midiPrograms") == 0) | |||
continue; | |||
setCustomData(stateCustomData->type, stateCustomData->key, stateCustomData->value, true); | |||
setCustomData(type, key, stateCustomData->value, true); | |||
} | |||
// --------------------------------------------------------------- | |||
@@ -797,6 +798,7 @@ void CarlaPlugin::loadStateSave(const CarlaStateSave& stateSave) | |||
CARLA_SAFE_ASSERT_CONTINUE(stateCustomData != nullptr); | |||
CARLA_SAFE_ASSERT_CONTINUE(stateCustomData->isValid()); | |||
const char* const type(stateCustomData->type); | |||
const char* const key(stateCustomData->key); | |||
if (getType() == PLUGIN_DSSI && (std::strcmp(key, "reloadprograms") == 0 || std::strcmp(key, "load") == 0 || std::strncmp(key, "patches", 7) == 0)) | |||
@@ -804,7 +806,7 @@ void CarlaPlugin::loadStateSave(const CarlaStateSave& stateSave) | |||
if (usesMultiProgs && std::strcmp(key, "midiPrograms") == 0) | |||
continue; | |||
setCustomData(stateCustomData->type, stateCustomData->key, stateCustomData->value, true); | |||
setCustomData(type, key, stateCustomData->value, true); | |||
} | |||
// --------------------------------------------------------------- | |||
@@ -845,6 +847,8 @@ void CarlaPlugin::loadStateSave(const CarlaStateSave& stateSave) | |||
setCtrlChannel(stateSave.ctrlChannel, true, true); | |||
setActive(stateSave.active, true, true); | |||
#endif | |||
pData->engine->callback(ENGINE_CALLBACK_UPDATE, pData->id, 0, 0, 0.0f, nullptr); | |||
} | |||
bool CarlaPlugin::saveStateToFile(const char* const filename) | |||
@@ -1080,6 +1080,9 @@ public: | |||
CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',); | |||
CARLA_SAFE_ASSERT_RETURN(value != nullptr,); | |||
if (std::strcmp(type, CUSTOM_DATA_TYPE_PROPERTY) == 0) | |||
return CarlaPlugin::setCustomData(type, key, value, sendGui); | |||
if (std::strcmp(type, CUSTOM_DATA_TYPE_STRING) == 0 && std::strcmp(key, "__CarlaPingOnOff__") == 0) | |||
{ | |||
const CarlaMutexLocker _cml(fShmNonRtClientControl.mutex); | |||
@@ -567,6 +567,9 @@ public: | |||
CARLA_SAFE_ASSERT_RETURN(value != nullptr,); | |||
carla_debug("CarlaPluginDSSI::setCustomData(%s, %s, %s, %s)", type, key, value, bool2str(sendGui)); | |||
if (std::strcmp(type, CUSTOM_DATA_TYPE_PROPERTY) == 0) | |||
return CarlaPlugin::setCustomData(type, key, value, sendGui); | |||
if (std::strcmp(type, CUSTOM_DATA_TYPE_STRING) != 0) | |||
return carla_stderr2("CarlaPluginDSSI::setCustomData(\"%s\", \"%s\", \"%s\", %s) - type is not string", type, key, value, bool2str(sendGui)); | |||
@@ -462,6 +462,9 @@ public: | |||
CARLA_SAFE_ASSERT_RETURN(value != nullptr && value[0] != '\0',); | |||
carla_debug("CarlaPluginFluidSynth::setCustomData(%s, \"%s\", \"%s\", %s)", type, key, value, bool2str(sendGui)); | |||
if (std::strcmp(type, CUSTOM_DATA_TYPE_PROPERTY) == 0) | |||
return CarlaPlugin::setCustomData(type, key, value, sendGui); | |||
if (std::strcmp(type, CUSTOM_DATA_TYPE_STRING) != 0) | |||
return carla_stderr2("CarlaPluginFluidSynth::setCustomData(\"%s\", \"%s\", \"%s\", %s) - type is not string", type, key, value, bool2str(sendGui)); | |||
@@ -1135,6 +1135,9 @@ public: | |||
CARLA_SAFE_ASSERT_RETURN(value != nullptr,); | |||
carla_debug("CarlaPluginLV2::setCustomData(%s, %s, %s, %s)", type, key, value, bool2str(sendGui)); | |||
if (std::strcmp(type, CUSTOM_DATA_TYPE_PROPERTY) == 0) | |||
return CarlaPlugin::setCustomData(type, key, value, sendGui); | |||
// we should only call state restore once | |||
// so inject this in CarlaPlugin::loadSaveState | |||
if (std::strcmp(type, CUSTOM_DATA_TYPE_STRING) == 0 && std::strcmp(key, "CarlaLoadLv2StateNow") == 0 && std::strcmp(value, "true") == 0) | |||
@@ -429,6 +429,9 @@ public: | |||
CARLA_SAFE_ASSERT_RETURN(value != nullptr && value[0] != '\0',); | |||
carla_debug("CarlaPluginLinuxSampler::setCustomData(%s, \"%s\", \"%s\", %s)", type, key, value, bool2str(sendGui)); | |||
if (std::strcmp(type, CUSTOM_DATA_TYPE_PROPERTY) == 0) | |||
return CarlaPlugin::setCustomData(type, key, value, sendGui); | |||
if (std::strcmp(type, CUSTOM_DATA_TYPE_STRING) != 0) | |||
return carla_stderr2("CarlaPluginLinuxSampler::setCustomData(\"%s\", \"%s\", \"%s\", %s) - type is not string", type, key, value, bool2str(sendGui)); | |||
@@ -607,6 +607,9 @@ public: | |||
CARLA_SAFE_ASSERT_RETURN(value != nullptr,); | |||
carla_debug("CarlaPluginNative::setCustomData(%s, %s, %s, %s)", type, key, value, bool2str(sendGui)); | |||
if (std::strcmp(type, CUSTOM_DATA_TYPE_PROPERTY) == 0) | |||
return CarlaPlugin::setCustomData(type, key, value, sendGui); | |||
if (std::strcmp(type, CUSTOM_DATA_TYPE_STRING) != 0 && std::strcmp(type, CUSTOM_DATA_TYPE_CHUNK) != 0) | |||
return carla_stderr2("CarlaPluginNative::setCustomData(\"%s\", \"%s\", \"%s\", %s) - type is invalid", type, key, value, bool2str(sendGui)); | |||
@@ -312,6 +312,9 @@ CUSTOM_DATA_TYPE_BOOLEAN = "http://kxstudio.sf.net/ns/carla/boolean" | |||
# Chunk type URI. | |||
CUSTOM_DATA_TYPE_CHUNK = "http://kxstudio.sf.net/ns/carla/chunk" | |||
# Property type URI. | |||
CUSTOM_DATA_TYPE_PROPERTY = "http://kxstudio.sf.net/ns/carla/property" | |||
# String type URI. | |||
CUSTOM_DATA_TYPE_STRING = "http://kxstudio.sf.net/ns/carla/string" | |||
@@ -2641,6 +2644,8 @@ class PluginStoreInfo(object): | |||
'midiProgramCount', | |||
'midiProgramCurrent', | |||
'midiProgramData', | |||
'customDataCount', | |||
'customData', | |||
'peaks' | |||
] | |||
@@ -2814,7 +2819,7 @@ class CarlaHostPlugin(CarlaHostMeta): | |||
return self.fPluginsInfo[pluginId].midiProgramData[midiProgramId] | |||
def get_custom_data(self, pluginId, customDataId): | |||
return PyCustomData | |||
return self.fPluginsInfo[pluginId].customData[customDataId] | |||
def get_chunk_data(self, pluginId): | |||
return "" | |||
@@ -2829,7 +2834,7 @@ class CarlaHostPlugin(CarlaHostMeta): | |||
return self.fPluginsInfo[pluginId].midiProgramCount | |||
def get_custom_data_count(self, pluginId): | |||
return 0 | |||
return self.fPluginsInfo[pluginId].customDataCount | |||
def get_parameter_text(self, pluginId, parameterId): | |||
return "" | |||
@@ -2978,6 +2983,8 @@ class CarlaHostPlugin(CarlaHostMeta): | |||
info.midiProgramCount = 0 | |||
info.midiProgramCurrent = -1 | |||
info.midiProgramData = [] | |||
info.customDataCount = 0 | |||
info.customData = [] | |||
info.peaks = [0.0, 0.0, 0.0, 0.0] | |||
self.fPluginsInfo.append(info) | |||
@@ -3037,6 +3044,16 @@ class CarlaHostPlugin(CarlaHostMeta): | |||
for x in range(count): | |||
self.fPluginsInfo[pluginId].midiProgramData.append(PyMidiProgramData) | |||
def _set_customDataCount(self, pluginId, count): | |||
self.fPluginsInfo[pluginId].customDataCount = count | |||
# clear | |||
self.fPluginsInfo[pluginId].customData = [] | |||
# add placeholders | |||
for x in range(count): | |||
self.fPluginsInfo[pluginId].customData.append(PyCustomData) | |||
def _set_parameterInfo(self, pluginId, paramIndex, info): | |||
if pluginId < len(self.fPluginsInfo) and paramIndex < self.fPluginsInfo[pluginId].parameterCount: | |||
self.fPluginsInfo[pluginId].parameterInfo[paramIndex] = info | |||
@@ -3079,6 +3096,10 @@ class CarlaHostPlugin(CarlaHostMeta): | |||
if mpIndex < self.fPluginsInfo[pluginId].midiProgramCount: | |||
self.fPluginsInfo[pluginId].midiProgramData[mpIndex] = data | |||
def _set_customData(self, pluginId, cdIndex, data): | |||
if cdIndex < self.fPluginsInfo[pluginId].customDataCount: | |||
self.fPluginsInfo[pluginId].customData[cdIndex] = data | |||
def _set_peaks(self, pluginId, in1, in2, out1, out2): | |||
self.fPluginsInfo[pluginId].peaks = [in1, in2, out1, out2] | |||
@@ -386,6 +386,8 @@ class HostWindow(QMainWindow): | |||
host.NoteOnCallback.connect(self.slot_handleNoteOnCallback) | |||
host.NoteOffCallback.connect(self.slot_handleNoteOffCallback) | |||
host.UpdateCallback.connect(self.slot_handleUpdateCallback) | |||
host.PatchbayClientAddedCallback.connect(self.slot_handlePatchbayClientAddedCallback) | |||
host.PatchbayClientRemovedCallback.connect(self.slot_handlePatchbayClientRemovedCallback) | |||
host.PatchbayClientRenamedCallback.connect(self.slot_handlePatchbayClientRenamedCallback) | |||
@@ -1495,6 +1497,32 @@ class HostWindow(QMainWindow): | |||
if pluginId in self.fSelectedPlugins: | |||
self.ui.keyboard.sendNoteOff(note, False) | |||
# -------------------------------------------------------------------------------------------------------- | |||
@pyqtSlot(int) | |||
def slot_handleUpdateCallback(self, pluginId): | |||
pitem = self.getPluginItem(pluginId) | |||
if pitem is None: | |||
return | |||
wasCompacted = pitem.isCompacted() | |||
isCompacted = wasCompacted | |||
for i in range(self.host.get_custom_data_count(pluginId)): | |||
cdata = self.host.get_custom_data(pluginId, i) | |||
if cdata['type'] == CUSTOM_DATA_TYPE_PROPERTY and cdata['key'] == "CarlaSkinIsCompacted": | |||
isCompacted = bool(cdata['value'] == "true") | |||
break | |||
else: | |||
return | |||
if wasCompacted == isCompacted: | |||
return | |||
pitem.recreateWidget(True) | |||
# -------------------------------------------------------------------------------------------------------- | |||
# MiniCanvas stuff | |||
@@ -1581,7 +1609,6 @@ class HostWindow(QMainWindow): | |||
if pitem is None: | |||
return | |||
self.ui.listWidget.customClearSelection() | |||
pitem.recreateWidget() | |||
# -------------------------------------------------------------------------------------------------------- | |||
@@ -312,19 +312,17 @@ class CarlaMiniW(ExternalUI, HostWindow): | |||
name = self.readlineblock().replace("\r", "\n") | |||
self.host._set_midiProgramData(pluginId, midiProgId, {'bank': bank, 'program': program, 'name': name}) | |||
elif msg == "complete-license": | |||
license = self.readlineblock().replace("\r", "\n") | |||
self.host.fCompleteLicenseText = license | |||
elif msg == "juce-version": | |||
version = self.readlineblock().replace("\r", "\n") | |||
self.host.fJuceVersion = version | |||
elif msg == "file-exts": | |||
exts = self.readlineblock().replace("\r", "\n") | |||
self.host.fSupportedFileExts = exts | |||
# only now we know the supported extensions | |||
self.fDirModel.setNameFilters(exts.split(";")) | |||
elif msg.startswith("CUSTOM_DATA_COUNT_"): | |||
pluginId, count = [int(i) for i in msg.replace("CUSTOM_DATA_COUNT_", "").split(":")] | |||
self.host._set_customDataCount(pluginId, count) | |||
elif msg.startswith("CUSTOM_DATA_"): | |||
pluginId, customDataId = [int(i) for i in msg.replace("CUSTOM_DATA_", "").split(":")] | |||
type_ = self.readlineblock().replace("\r", "\n") | |||
key = self.readlineblock().replace("\r", "\n") | |||
value = self.readlineblock().replace("\r", "\n") | |||
self.host._set_customData(pluginId, customDataId, {'type': type_, 'key': key, 'value': value}) | |||
elif msg == "max-plugin-number": | |||
maxnum = int(self.readlineblock()) | |||
@@ -67,13 +67,19 @@ class RackListItem(QListWidgetItem): | |||
'useSkins': useSkins | |||
} | |||
for i in range(self.host.get_custom_data_count(pluginId)): | |||
cdata = self.host.get_custom_data(pluginId, i) | |||
if cdata['type'] == CUSTOM_DATA_TYPE_PROPERTY and cdata['key'] == "CarlaSkinIsCompacted": | |||
self.fOptions['compact'] = bool(cdata['value'] == "true") | |||
break | |||
self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled) | |||
#self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled|Qt.ItemIsDragEnabled) | |||
# ---------------------------------------------------------------------------------------------------- | |||
# Set-up GUI | |||
self.recreateWidget() | |||
self.recreateWidget(firstInit = True) | |||
# -------------------------------------------------------------------------------------------------------- | |||
@@ -97,6 +103,9 @@ class RackListItem(QListWidgetItem): | |||
widget.deleteLater() | |||
del widget | |||
def isCompacted(self): | |||
return self.fOptions['compact'] | |||
def getEditDialog(self): | |||
if self.fWidget is None: | |||
return None | |||
@@ -125,7 +134,7 @@ class RackListItem(QListWidgetItem): | |||
# -------------------------------------------------------------------------------------------------------- | |||
def recreateWidget(self, invertCompactOption = False): | |||
def recreateWidget(self, invertCompactOption = False, firstInit = False): | |||
if invertCompactOption: | |||
self.fOptions['compact'] = not self.fOptions['compact'] | |||
@@ -138,6 +147,10 @@ class RackListItem(QListWidgetItem): | |||
self.fParent.setItemWidget(self, self.fWidget) | |||
if not firstInit: | |||
self.host.set_custom_data(self.fPluginId, CUSTOM_DATA_TYPE_PROPERTY, | |||
"CarlaSkinIsCompacted", "true" if self.fOptions['compact'] else "false") | |||
# ------------------------------------------------------------------------------------------------------------ | |||
# Rack Widget | |||