Signed-off-by: falkTX <falktx@falktx.com>pull/232/head
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -830,6 +830,13 @@ protected: | |||
virtual void initState(uint32_t index, String& stateKey, String& defaultStateValue) = 0; | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_STATEFILES | |||
/** | |||
TODO API under construction | |||
*/ | |||
virtual bool isStateFile(uint32_t index) = 0; | |||
#endif | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* Internal data */ | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -98,7 +98,7 @@ public: | |||
/** | |||
setParameterValue. | |||
Change a parameter value in the Plugin. | |||
*/ | |||
void setParameterValue(uint32_t index, float value); | |||
@@ -111,6 +111,19 @@ public: | |||
void setState(const char* key, const char* value); | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_STATEFILES | |||
/** | |||
Request a new file from the host, matching the properties of a state key.@n | |||
This will use the native host file browser if available, otherwise a DPF built-in file browser is used.@n | |||
Response will be sent asynchronously to stateChanged, with the matching key and the new file as the value.@n | |||
It is not possible to know if the action was cancelled by the user. | |||
@return Success if a file-browser was opened, otherwise false. | |||
@note You cannot request more than one file at a time. | |||
*/ | |||
bool requestStateFile(const char* key); | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
/** | |||
sendNote. | |||
@@ -77,6 +77,10 @@ | |||
# define DISTRHO_PLUGIN_WANT_STATE 0 | |||
#endif | |||
#ifndef DISTRHO_PLUGIN_WANT_STATEFILES | |||
# define DISTRHO_PLUGIN_WANT_STATEFILES 0 | |||
#endif | |||
#ifndef DISTRHO_PLUGIN_WANT_FULL_STATE | |||
# define DISTRHO_PLUGIN_WANT_FULL_STATE 0 | |||
#endif | |||
@@ -108,7 +112,7 @@ | |||
// Define DISTRHO_UI_URI if needed | |||
#ifndef DISTRHO_UI_URI | |||
# define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI" | |||
# define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#DPF_UI" | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
@@ -127,6 +131,14 @@ | |||
# error Synths need MIDI input to work! | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// Enable state if plugin wants state files | |||
#if DISTRHO_PLUGIN_WANT_STATEFILES && ! DISTRHO_PLUGIN_WANT_STATE | |||
# undef DISTRHO_PLUGIN_WANT_STATE | |||
# define DISTRHO_PLUGIN_WANT_STATE 1 | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
// Enable full state if plugin exports presets | |||
@@ -486,6 +486,15 @@ public: | |||
return fData->stateDefValues[index]; | |||
} | |||
# if DISTRHO_PLUGIN_WANT_STATEFILES | |||
bool isStateFile(const uint32_t index) const | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr && index < fData->stateCount, false); | |||
return fPlugin->isStateFile(index); | |||
} | |||
# endif | |||
# if DISTRHO_PLUGIN_WANT_FULL_STATE | |||
String getState(const char* key) const | |||
{ | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU Lesser General Public | |||
@@ -101,7 +101,15 @@ public: | |||
PluginJack(jack_client_t* const client) | |||
: fPlugin(this, writeMidiCallback), | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
fUI(this, 0, nullptr, setParameterValueCallback, setStateCallback, nullptr, setSizeCallback, getDesktopScaleFactor(), fPlugin.getInstancePointer()), | |||
fUI(this, 0, | |||
nullptr, // edit param | |||
setParameterValueCallback, | |||
setStateCallback, | |||
nullptr, // send note | |||
setSizeCallback, | |||
nullptr, // file request | |||
getDesktopScaleFactor(), | |||
fPlugin.getInstancePointer()), | |||
#endif | |||
fClient(client) | |||
{ | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -24,6 +24,7 @@ | |||
#include "lv2/midi.h" | |||
#include "lv2/options.h" | |||
#include "lv2/parameters.h" | |||
#include "lv2/patch.h" | |||
#include "lv2/state.h" | |||
#include "lv2/time.h" | |||
#include "lv2/urid.h" | |||
@@ -54,7 +55,8 @@ | |||
START_NAMESPACE_DISTRHO | |||
typedef std::map<const String, String> StringMap; | |||
typedef std::map<const String, String> StringToStringMap; | |||
typedef std::map<const LV2_URID, String> UridToStringMap; | |||
#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||
static const writeMidiFunc writeMidiCallback = nullptr; | |||
@@ -65,7 +67,10 @@ static const writeMidiFunc writeMidiCallback = nullptr; | |||
class PluginLv2 | |||
{ | |||
public: | |||
PluginLv2(const double sampleRate, const LV2_URID_Map* const uridMap, const LV2_Worker_Schedule* const worker, const bool usingNominal) | |||
PluginLv2(const double sampleRate, | |||
const LV2_URID_Map* const uridMap, | |||
const LV2_Worker_Schedule* const worker, | |||
const bool usingNominal) | |||
: fPlugin(this, writeMidiCallback), | |||
fUsingNominal(usingNominal), | |||
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD | |||
@@ -127,6 +132,15 @@ public: | |||
const String& dkey(fPlugin.getStateKey(i)); | |||
fStateMap[dkey] = fPlugin.getStateDefaultValue(i); | |||
# if DISTRHO_PLUGIN_WANT_STATEFILES | |||
if (fPlugin.isStateFile(i)) | |||
{ | |||
const String dpf_lv2_key(DISTRHO_PLUGIN_URI "#" + dkey); | |||
const LV2_URID urid = uridMap->map(uridMap->handle, dpf_lv2_key.buffer()); | |||
fUridStateFileMap[urid] = dkey; | |||
} | |||
# endif | |||
} | |||
} | |||
else | |||
@@ -521,16 +535,16 @@ public: | |||
} | |||
#endif | |||
// check for messages from UI | |||
#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI | |||
// check for messages from UI or files | |||
#if DISTRHO_PLUGIN_WANT_STATE && (DISTRHO_PLUGIN_HAS_UI || DISTRHO_PLUGIN_WANT_STATEFILES) | |||
LV2_ATOM_SEQUENCE_FOREACH(fPortEventsIn, event) | |||
{ | |||
if (event == nullptr) | |||
break; | |||
if (event->body.type == fURIDs.distrhoState && fWorker != nullptr) | |||
if (event->body.type == fURIDs.dpfKeyValue) | |||
{ | |||
const void* const data((const void*)(event + 1)); | |||
const void* const data = (const void*)(event + 1); | |||
// check if this is our special message | |||
if (std::strcmp((const char*)data, "__dpf_ui_data__") == 0) | |||
@@ -538,12 +552,28 @@ public: | |||
for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | |||
fNeededUiSends[i] = true; | |||
} | |||
else | |||
// no, send to DSP as usual | |||
else if (fWorker != nullptr) | |||
{ | |||
fWorker->schedule_work(fWorker->handle, event->body.size, data); | |||
fWorker->schedule_work(fWorker->handle, sizeof(LV2_Atom)+event->body.size, &event->body); | |||
} | |||
} | |||
# if DISTRHO_PLUGIN_WANT_STATEFILES | |||
else if (event->body.type == fURIDs.atomObject && fWorker != nullptr) | |||
{ | |||
const LV2_Atom_Object* const object = (const LV2_Atom_Object*)&event->body; | |||
const LV2_Atom* property = nullptr; | |||
const LV2_Atom* value = nullptr; | |||
lv2_atom_object_get(object, fURIDs.patchProperty, &property, fURIDs.patchValue, &value, nullptr); | |||
if (property != nullptr && property->type == fURIDs.atomURID && | |||
value != nullptr && value->type == fURIDs.atomPath) | |||
{ | |||
fWorker->schedule_work(fWorker->handle, sizeof(LV2_Atom)+event->body.size, &event->body); | |||
} | |||
} | |||
# endif | |||
} | |||
#endif | |||
@@ -653,19 +683,19 @@ public: | |||
if (! fNeededUiSends[i]) | |||
continue; | |||
const String& key = fPlugin.getStateKey(i); | |||
const String& curKey(fPlugin.getStateKey(i)); | |||
for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||
for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||
{ | |||
const String& curKey = cit->first; | |||
const String& key(cit->first); | |||
if (curKey != key) | |||
continue; | |||
const String& value = cit->second; | |||
const String& value(cit->second); | |||
// set msg size (key + value + separator + 2x null terminator) | |||
const size_t msgSize(key.length()+value.length()+3); | |||
const size_t msgSize = key.length()+value.length()+3; | |||
if (sizeof(LV2_Atom_Event) + msgSize > capacity - fEventsOutData.offset) | |||
{ | |||
@@ -673,21 +703,18 @@ public: | |||
break; | |||
} | |||
// reserve msg space | |||
// FIXME create a large enough buffer beforehand | |||
char msgBuf[msgSize]; | |||
std::memset(msgBuf, 0, msgSize); | |||
// write key and value in atom bufer | |||
std::memcpy(msgBuf, key.buffer(), key.length()+1); | |||
std::memcpy(msgBuf+(key.length()+1), value.buffer(), value.length()+1); | |||
// put data | |||
aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + fEventsOutData.offset); | |||
aev->time.frames = 0; | |||
aev->body.type = fURIDs.distrhoState; | |||
aev->body.type = fURIDs.dpfKeyValue; | |||
aev->body.size = msgSize; | |||
std::memcpy(LV2_ATOM_BODY(&aev->body), msgBuf, msgSize); | |||
uint8_t* const msgBuf = LV2_ATOM_BODY(&aev->body); | |||
std::memset(msgBuf, 0, msgSize); | |||
// write key and value in atom buffer | |||
std::memcpy(msgBuf, key.buffer(), key.length()+1); | |||
std::memcpy(msgBuf+(key.length()+1), value.buffer(), value.length()+1); | |||
fEventsOutData.growBy(lv2_atom_pad_size(sizeof(LV2_Atom_Event) + msgSize)); | |||
@@ -795,7 +822,7 @@ public: | |||
# if DISTRHO_PLUGIN_WANT_FULL_STATE | |||
// Update state | |||
for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||
for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||
{ | |||
const String& key = cit->first; | |||
fStateMap[key] = fPlugin.getState(key); | |||
@@ -811,22 +838,52 @@ public: | |||
{ | |||
# if DISTRHO_PLUGIN_WANT_FULL_STATE | |||
// Update current state | |||
for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||
for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||
{ | |||
const String& key = cit->first; | |||
fStateMap[key] = fPlugin.getState(key); | |||
} | |||
# endif | |||
for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||
String dpf_lv2_key; | |||
LV2_URID urid; | |||
for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | |||
{ | |||
const String& key = cit->first; | |||
const String& value = cit->second; | |||
const String& curKey(fPlugin.getStateKey(i)); | |||
for (StringToStringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) | |||
{ | |||
const String& key(cit->first); | |||
if (curKey != key) | |||
continue; | |||
const String& value(cit->second); | |||
const String urnKey(DISTRHO_PLUGIN_LV2_STATE_PREFIX + key); | |||
# if DISTRHO_PLUGIN_WANT_STATEFILES | |||
if (fPlugin.isStateFile(i)) | |||
{ | |||
dpf_lv2_key = DISTRHO_PLUGIN_URI "#"; | |||
urid = fURIDs.atomPath; | |||
} | |||
else | |||
# endif | |||
{ | |||
dpf_lv2_key = DISTRHO_PLUGIN_LV2_STATE_PREFIX; | |||
urid = fURIDs.atomString; | |||
} | |||
// some hosts need +1 for the null terminator, even though the type is string | |||
store(handle, fUridMap->map(fUridMap->handle, urnKey.buffer()), value.buffer(), value.length()+1, fURIDs.atomString, LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE); | |||
dpf_lv2_key += key; | |||
// some hosts need +1 for the null terminator, even though the type is string | |||
store(handle, | |||
fUridMap->map(fUridMap->handle, dpf_lv2_key.buffer()), | |||
value.buffer(), | |||
value.length()+1, | |||
urid, | |||
LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE); | |||
} | |||
} | |||
return LV2_STATE_SUCCESS; | |||
@@ -837,23 +894,42 @@ public: | |||
size_t size; | |||
uint32_t type, flags; | |||
String dpf_lv2_key; | |||
LV2_URID urid; | |||
for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | |||
{ | |||
const String& key(fPlugin.getStateKey(i)); | |||
const String urnKey(DISTRHO_PLUGIN_LV2_STATE_PREFIX + key); | |||
# if DISTRHO_PLUGIN_WANT_STATEFILES | |||
if (fPlugin.isStateFile(i)) | |||
{ | |||
dpf_lv2_key = DISTRHO_PLUGIN_URI "#"; | |||
urid = fURIDs.atomPath; | |||
} | |||
else | |||
# endif | |||
{ | |||
dpf_lv2_key = DISTRHO_PLUGIN_LV2_STATE_PREFIX; | |||
urid = fURIDs.atomString; | |||
} | |||
dpf_lv2_key += key; | |||
size = 0; | |||
type = 0; | |||
flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE; | |||
const void* data = retrieve(handle, fUridMap->map(fUridMap->handle, urnKey.buffer()), &size, &type, &flags); | |||
const void* data = retrieve(handle, | |||
fUridMap->map(fUridMap->handle, dpf_lv2_key.buffer()), | |||
&size, &type, &flags); | |||
if (data == nullptr || size == 0) | |||
continue; | |||
DISTRHO_SAFE_ASSERT_CONTINUE(type == fURIDs.atomString); | |||
DISTRHO_SAFE_ASSERT_CONTINUE(type == urid); | |||
const char* const value((const char*)data); | |||
const std::size_t length(std::strlen(value)); | |||
const char* const value = (const char*)data; | |||
const std::size_t length = std::strlen(value); | |||
DISTRHO_SAFE_ASSERT_CONTINUE(length == size || length+1 == size); | |||
setState(key, value); | |||
@@ -871,12 +947,55 @@ public: | |||
LV2_Worker_Status lv2_work(const void* const data) | |||
{ | |||
const char* const key((const char*)data); | |||
const char* const value(key+std::strlen(key)+1); | |||
const LV2_Atom* const eventBody = (const LV2_Atom*)data; | |||
setState(key, value); | |||
if (eventBody->type == fURIDs.dpfKeyValue) | |||
{ | |||
const char* const key = (const char*)(eventBody + 1); | |||
const char* const value = key + (std::strlen(key) + 1U); | |||
return LV2_WORKER_SUCCESS; | |||
setState(key, value); | |||
return LV2_WORKER_SUCCESS; | |||
} | |||
# if DISTRHO_PLUGIN_WANT_STATEFILES | |||
if (eventBody->type == fURIDs.atomObject) | |||
{ | |||
const LV2_Atom_Object* const object = (const LV2_Atom_Object*)eventBody; | |||
const LV2_Atom* property = nullptr; | |||
const LV2_Atom* value = nullptr; | |||
lv2_atom_object_get(object, fURIDs.patchProperty, &property, fURIDs.patchValue, &value, nullptr); | |||
DISTRHO_SAFE_ASSERT_RETURN(property != nullptr, LV2_WORKER_ERR_UNKNOWN); | |||
DISTRHO_SAFE_ASSERT_RETURN(property->type == fURIDs.atomURID, LV2_WORKER_ERR_UNKNOWN); | |||
DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, LV2_WORKER_ERR_UNKNOWN); | |||
DISTRHO_SAFE_ASSERT_RETURN(value->type == fURIDs.atomPath, LV2_WORKER_ERR_UNKNOWN); | |||
const LV2_URID urid = ((const LV2_Atom_URID*)property)->body; | |||
const char* const filename = (const char*)(value + 1); | |||
String key; | |||
try { | |||
key = fUridStateFileMap[urid]; | |||
} DISTRHO_SAFE_EXCEPTION_RETURN("lv2_work fUridStateFileMap[urid]", LV2_WORKER_ERR_UNKNOWN); | |||
setState(key, filename); | |||
for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) | |||
{ | |||
if (fPlugin.getStateKey(i) == key) | |||
{ | |||
fNeededUiSends[i] = true; | |||
break; | |||
} | |||
} | |||
return LV2_WORKER_SUCCESS; | |||
} | |||
# endif | |||
return LV2_WORKER_ERR_UNKNOWN; | |||
} | |||
LV2_Worker_Status lv2_work_response(uint32_t, const void*) | |||
@@ -995,16 +1114,21 @@ private: | |||
// LV2 URIDs | |||
struct URIDs { | |||
const LV2_URID_Map* _uridMap; | |||
LV2_URID atomBlank; | |||
LV2_URID atomObject; | |||
LV2_URID atomDouble; | |||
LV2_URID atomFloat; | |||
LV2_URID atomInt; | |||
LV2_URID atomLong; | |||
LV2_URID atomPath; | |||
LV2_URID atomSequence; | |||
LV2_URID atomString; | |||
LV2_URID distrhoState; | |||
LV2_URID atomURID; | |||
LV2_URID dpfKeyValue; | |||
LV2_URID midiEvent; | |||
LV2_URID patchProperty; | |||
LV2_URID patchValue; | |||
LV2_URID timePosition; | |||
LV2_URID timeBar; | |||
LV2_URID timeBarBeat; | |||
@@ -1016,25 +1140,35 @@ private: | |||
LV2_URID timeSpeed; | |||
URIDs(const LV2_URID_Map* const uridMap) | |||
: atomBlank(uridMap->map(uridMap->handle, LV2_ATOM__Blank)), | |||
atomObject(uridMap->map(uridMap->handle, LV2_ATOM__Object)), | |||
atomDouble(uridMap->map(uridMap->handle, LV2_ATOM__Double)), | |||
atomFloat(uridMap->map(uridMap->handle, LV2_ATOM__Float)), | |||
atomInt(uridMap->map(uridMap->handle, LV2_ATOM__Int)), | |||
atomLong(uridMap->map(uridMap->handle, LV2_ATOM__Long)), | |||
atomSequence(uridMap->map(uridMap->handle, LV2_ATOM__Sequence)), | |||
atomString(uridMap->map(uridMap->handle, LV2_ATOM__String)), | |||
distrhoState(uridMap->map(uridMap->handle, DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")), | |||
midiEvent(uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent)), | |||
timePosition(uridMap->map(uridMap->handle, LV2_TIME__Position)), | |||
timeBar(uridMap->map(uridMap->handle, LV2_TIME__bar)), | |||
timeBarBeat(uridMap->map(uridMap->handle, LV2_TIME__barBeat)), | |||
timeBeatUnit(uridMap->map(uridMap->handle, LV2_TIME__beatUnit)), | |||
timeBeatsPerBar(uridMap->map(uridMap->handle, LV2_TIME__beatsPerBar)), | |||
timeBeatsPerMinute(uridMap->map(uridMap->handle, LV2_TIME__beatsPerMinute)), | |||
timeTicksPerBeat(uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat)), | |||
timeFrame(uridMap->map(uridMap->handle, LV2_TIME__frame)), | |||
timeSpeed(uridMap->map(uridMap->handle, LV2_TIME__speed)) {} | |||
: _uridMap(uridMap), | |||
atomBlank(map(LV2_ATOM__Blank)), | |||
atomObject(map(LV2_ATOM__Object)), | |||
atomDouble(map(LV2_ATOM__Double)), | |||
atomFloat(map(LV2_ATOM__Float)), | |||
atomInt(map(LV2_ATOM__Int)), | |||
atomLong(map(LV2_ATOM__Long)), | |||
atomPath(map(LV2_ATOM__Path)), | |||
atomSequence(map(LV2_ATOM__Sequence)), | |||
atomString(map(LV2_ATOM__String)), | |||
atomURID(map(LV2_ATOM__URID)), | |||
dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")), | |||
midiEvent(map(LV2_MIDI__MidiEvent)), | |||
patchProperty(map(LV2_PATCH__property)), | |||
patchValue(map(LV2_PATCH__value)), | |||
timePosition(map(LV2_TIME__Position)), | |||
timeBar(map(LV2_TIME__bar)), | |||
timeBarBeat(map(LV2_TIME__barBeat)), | |||
timeBeatUnit(map(LV2_TIME__beatUnit)), | |||
timeBeatsPerBar(map(LV2_TIME__beatsPerBar)), | |||
timeBeatsPerMinute(map(LV2_TIME__beatsPerMinute)), | |||
timeTicksPerBeat(map(LV2_KXSTUDIO_PROPERTIES__TimePositionTicksPerBeat)), | |||
timeFrame(map(LV2_TIME__frame)), | |||
timeSpeed(map(LV2_TIME__speed)) {} | |||
inline LV2_URID map(const char* const uri) const | |||
{ | |||
return _uridMap->map(_uridMap->handle, uri); | |||
} | |||
} fURIDs; | |||
// LV2 features | |||
@@ -1042,7 +1176,7 @@ private: | |||
const LV2_Worker_Schedule* const fWorker; | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
StringMap fStateMap; | |||
StringToStringMap fStateMap; | |||
bool* fNeededUiSends; | |||
void setState(const char* const key, const char* const newValue) | |||
@@ -1054,7 +1188,7 @@ private: | |||
return; | |||
// check if key already exists | |||
for (StringMap::iterator it=fStateMap.begin(), ite=fStateMap.end(); it != ite; ++it) | |||
for (StringToStringMap::iterator it=fStateMap.begin(), ite=fStateMap.end(); it != ite; ++it) | |||
{ | |||
const String& dkey(it->first); | |||
@@ -1067,6 +1201,10 @@ private: | |||
d_stderr("Failed to find plugin state with key \"%s\"", key); | |||
} | |||
# if DISTRHO_PLUGIN_WANT_STATEFILES | |||
UridToStringMap fUridStateFileMap; | |||
# endif | |||
#endif | |||
void updateParameterOutputsAndTriggers() | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -22,6 +22,7 @@ | |||
#include "lv2/instance-access.h" | |||
#include "lv2/midi.h" | |||
#include "lv2/options.h" | |||
#include "lv2/patch.h" | |||
#include "lv2/port-props.h" | |||
#include "lv2/presets.h" | |||
#include "lv2/resize-port.h" | |||
@@ -45,6 +46,10 @@ | |||
# error DISTRHO_PLUGIN_URI undefined! | |||
#endif | |||
#ifndef DISTRHO_PLUGIN_LV2_STATE_PREFIX | |||
# define DISTRHO_PLUGIN_LV2_STATE_PREFIX "urn:distrho:" | |||
#endif | |||
#ifndef DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE | |||
# define DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE 2048 | |||
#endif | |||
@@ -139,6 +144,9 @@ static const char* const lv2ManifestUiOptionalFeatures[] = | |||
"ui:parent", | |||
"ui:resize", | |||
"ui:touch", | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_STATEFILES | |||
"ui:requestValue", | |||
#endif | |||
nullptr | |||
}; | |||
@@ -220,8 +228,8 @@ void lv2_generate_ttl(const char* const basename) | |||
d_lastBufferSize = 0; | |||
d_lastSampleRate = 0.0; | |||
String pluginDLL(basename); | |||
String pluginTTL(pluginDLL + ".ttl"); | |||
const String pluginDLL(basename); | |||
const String pluginTTL(pluginDLL + ".ttl"); | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
String pluginUI(pluginDLL); | |||
@@ -317,26 +325,45 @@ void lv2_generate_ttl(const char* const basename) | |||
// header | |||
#if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT | |||
pluginString += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; | |||
pluginString += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; | |||
#endif | |||
pluginString += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"; | |||
pluginString += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"; | |||
pluginString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |||
pluginString += "@prefix doap: <http://usefulinc.com/ns/doap#> .\n"; | |||
pluginString += "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n"; | |||
pluginString += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; | |||
#ifdef DISTRHO_PLUGIN_BRAND | |||
pluginString += "@prefix mod: <http://moddevices.com/ns/mod#> .\n"; | |||
pluginString += "@prefix mod: <http://moddevices.com/ns/mod#> .\n"; | |||
#endif | |||
pluginString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n"; | |||
pluginString += "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"; | |||
pluginString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"; | |||
pluginString += "@prefix opts: <" LV2_OPTIONS_PREFIX "> .\n"; | |||
pluginString += "@prefix patch: <" LV2_PATCH_PREFIX "> .\n"; | |||
pluginString += "@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .\n"; | |||
pluginString += "@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n"; | |||
#if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT | |||
pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n"; | |||
pluginString += "@prefix rsz: <" LV2_RESIZE_PORT_PREFIX "> .\n"; | |||
#endif | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | |||
pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; | |||
#endif | |||
pluginString += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n"; | |||
pluginString += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n"; | |||
pluginString += "\n"; | |||
#if DISTRHO_PLUGIN_WANT_STATEFILES | |||
// define writable states as lv2 parameters | |||
bool hasStateFiles = false; | |||
for (uint32_t i=0, count=plugin.getStateCount(); i < count; ++i) | |||
{ | |||
if (! plugin.isStateFile(i)) | |||
continue; | |||
const String& key(plugin.getStateKey(i)); | |||
pluginString += "<" DISTRHO_PLUGIN_URI "#" + key + ">\n"; | |||
pluginString += " a lv2:Parameter ;\n"; | |||
pluginString += " rdfs:label \"" + key + "\" ;\n"; | |||
pluginString += " rdfs:range atom:Path .\n\n"; | |||
hasStateFiles = true; | |||
} | |||
#endif | |||
// plugin | |||
pluginString += "<" DISTRHO_PLUGIN_URI ">\n"; | |||
#ifdef DISTRHO_PLUGIN_LV2_CATEGORY | |||
@@ -353,6 +380,21 @@ void lv2_generate_ttl(const char* const basename) | |||
addAttribute(pluginString, "lv2:requiredFeature", lv2ManifestPluginRequiredFeatures, 4); | |||
addAttribute(pluginString, "opts:supportedOption", lv2ManifestPluginSupportedOptions, 4); | |||
#if DISTRHO_PLUGIN_WANT_STATEFILES | |||
if (hasStateFiles) | |||
{ | |||
for (uint32_t i=0, count=plugin.getStateCount(); i < count; ++i) | |||
{ | |||
if (! plugin.isStateFile(i)) | |||
continue; | |||
const String& key(plugin.getStateKey(i)); | |||
pluginString += " patch:writable <" DISTRHO_PLUGIN_URI "#" + key + ">;\n"; | |||
} | |||
pluginString += "\n"; | |||
} | |||
#endif | |||
// UI | |||
#if DISTRHO_PLUGIN_HAS_UI | |||
pluginString += " ui:ui <" DISTRHO_UI_URI "> ;\n"; | |||
@@ -582,33 +624,36 @@ void lv2_generate_ttl(const char* const basename) | |||
// unit | |||
const String& unit(plugin.getParameterUnit(i)); | |||
if (! unit.isEmpty()) | |||
if (unit.isNotEmpty() && ! unit.contains(" ")) | |||
{ | |||
if (unit == "db" || unit == "dB") | |||
String lunit(unit); | |||
lunit.toLower(); | |||
/**/ if (lunit == "db") | |||
{ | |||
pluginString += " unit:unit unit:db ;\n"; | |||
} | |||
else if (unit == "hz" || unit == "Hz") | |||
else if (lunit == "hz") | |||
{ | |||
pluginString += " unit:unit unit:hz ;\n"; | |||
} | |||
else if (unit == "khz" || unit == "kHz") | |||
else if (lunit == "khz") | |||
{ | |||
pluginString += " unit:unit unit:khz ;\n"; | |||
} | |||
else if (unit == "mhz" || unit == "mHz") | |||
else if (lunit == "mhz") | |||
{ | |||
pluginString += " unit:unit unit:mhz ;\n"; | |||
} | |||
else if (unit == "ms") | |||
else if (lunit == "ms") | |||
{ | |||
pluginString += " unit:unit unit:ms ;\n"; | |||
} | |||
else if (unit == "s") | |||
else if (lunit == "s") | |||
{ | |||
pluginString += " unit:unit unit:s ;\n"; | |||
} | |||
else if (unit == "%") | |||
else if (lunit == "%") | |||
{ | |||
pluginString += " unit:unit unit:pc ;\n"; | |||
} | |||
@@ -796,7 +841,7 @@ void lv2_generate_ttl(const char* const basename) | |||
const String key = plugin.getStateKey(j); | |||
const String value = plugin.getState(key); | |||
presetString += " <urn:distrho:" + key + ">"; | |||
presetString += " <" DISTRHO_PLUGIN_LV2_STATE_PREFIX + key + ">"; | |||
if (value.length() < 10) | |||
presetString += " \"" + value + "\" ;\n"; | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2018 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -161,12 +161,24 @@ public: | |||
class UIVst | |||
{ | |||
public: | |||
UIVst(const audioMasterCallback audioMaster, AEffect* const effect, ParameterCheckHelper* const uiHelper, PluginExporter* const plugin, const intptr_t winId, const float scaleFactor) | |||
UIVst(const audioMasterCallback audioMaster, | |||
AEffect* const effect, | |||
ParameterCheckHelper* const uiHelper, | |||
PluginExporter* const plugin, | |||
const intptr_t winId, const float scaleFactor) | |||
: fAudioMaster(audioMaster), | |||
fEffect(effect), | |||
fUiHelper(uiHelper), | |||
fPlugin(plugin), | |||
fUI(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, scaleFactor, plugin->getInstancePointer()), | |||
fUI(this, winId, | |||
editParameterCallback, | |||
setParameterCallback, | |||
setStateCallback, | |||
sendNoteCallback, | |||
setSizeCallback, | |||
nullptr, // TODO file request | |||
scaleFactor, | |||
plugin->getInstancePointer()), | |||
fShouldCaptureVstKeys(false) | |||
{ | |||
// FIXME only needed for windows? | |||
@@ -581,7 +593,9 @@ public: | |||
// TODO | |||
const float scaleFactor = 1.0f; | |||
UIExporter tmpUI(nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr, scaleFactor, fPlugin.getInstancePointer()); | |||
UIExporter tmpUI(nullptr, 0, | |||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, | |||
scaleFactor, fPlugin.getInstancePointer()); | |||
fVstRect.right = tmpUI.getWidth(); | |||
fVstRect.bottom = tmpUI.getHeight(); | |||
tmpUI.quit(); | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -103,6 +103,13 @@ void UI::setState(const char* key, const char* value) | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_STATEFILES | |||
bool UI::requestStateFile(const char* key) | |||
{ | |||
return pData->fileRequestCallback(key); | |||
} | |||
#endif | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity) | |||
{ | |||
@@ -97,7 +97,7 @@ class UIDssi | |||
{ | |||
public: | |||
UIDssi(const OscData& oscData, const char* const uiTitle) | |||
: fUI(this, 0, nullptr, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback), | |||
: fUI(this, 0, nullptr, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, nullptr), | |||
fHostClosed(false), | |||
fOscData(oscData) | |||
{ | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -48,11 +48,12 @@ extern Window* d_lastUiWindow; | |||
// ----------------------------------------------------------------------- | |||
// UI callbacks | |||
typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started); | |||
typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value); | |||
typedef void (*setStateFunc) (void* ptr, const char* key, const char* value); | |||
typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo); | |||
typedef void (*setSizeFunc) (void* ptr, uint width, uint height); | |||
typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started); | |||
typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value); | |||
typedef void (*setStateFunc) (void* ptr, const char* key, const char* value); | |||
typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo); | |||
typedef void (*setSizeFunc) (void* ptr, uint width, uint height); | |||
typedef bool (*fileRequestFunc) (void* ptr, const char* key); | |||
// ----------------------------------------------------------------------- | |||
// UI private data | |||
@@ -72,12 +73,13 @@ struct UI::PrivateData { | |||
uint minHeight; | |||
// Callbacks | |||
void* callbacksPtr; | |||
editParamFunc editParamCallbackFunc; | |||
setParamFunc setParamCallbackFunc; | |||
setStateFunc setStateCallbackFunc; | |||
sendNoteFunc sendNoteCallbackFunc; | |||
setSizeFunc setSizeCallbackFunc; | |||
void* callbacksPtr; | |||
editParamFunc editParamCallbackFunc; | |||
setParamFunc setParamCallbackFunc; | |||
setStateFunc setStateCallbackFunc; | |||
sendNoteFunc sendNoteCallbackFunc; | |||
setSizeFunc setSizeCallbackFunc; | |||
fileRequestFunc fileRequestCallbackFunc; | |||
PrivateData() noexcept | |||
: sampleRate(d_lastUiSampleRate), | |||
@@ -94,7 +96,8 @@ struct UI::PrivateData { | |||
setParamCallbackFunc(nullptr), | |||
setStateCallbackFunc(nullptr), | |||
sendNoteCallbackFunc(nullptr), | |||
setSizeCallbackFunc(nullptr) | |||
setSizeCallbackFunc(nullptr), | |||
fileRequestCallbackFunc(nullptr) | |||
{ | |||
DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate)); | |||
@@ -144,6 +147,16 @@ struct UI::PrivateData { | |||
if (setSizeCallbackFunc != nullptr) | |||
setSizeCallbackFunc(callbacksPtr, width, height); | |||
} | |||
bool fileRequestCallback(const char* key) | |||
{ | |||
if (fileRequestCallbackFunc != nullptr) | |||
return fileRequestCallbackFunc(callbacksPtr, key); | |||
// TODO use old style DPF dialog here | |||
return false; | |||
} | |||
}; | |||
// ----------------------------------------------------------------------- | |||
@@ -258,6 +271,7 @@ public: | |||
const setStateFunc setStateCall, | |||
const sendNoteFunc sendNoteCall, | |||
const setSizeFunc setSizeCall, | |||
const fileRequestFunc fileRequestCall, | |||
const float scaleFactor = 1.0f, | |||
void* const dspPtr = nullptr, | |||
const char* const bundlePath = nullptr) | |||
@@ -274,12 +288,13 @@ public: | |||
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); | |||
DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); | |||
fData->callbacksPtr = callbacksPtr; | |||
fData->editParamCallbackFunc = editParamCall; | |||
fData->setParamCallbackFunc = setParamCall; | |||
fData->setStateCallbackFunc = setStateCall; | |||
fData->sendNoteCallbackFunc = sendNoteCall; | |||
fData->setSizeCallbackFunc = setSizeCall; | |||
fData->callbacksPtr = callbacksPtr; | |||
fData->editParamCallbackFunc = editParamCall; | |||
fData->setParamCallbackFunc = setParamCall; | |||
fData->setStateCallbackFunc = setStateCall; | |||
fData->sendNoteCallbackFunc = sendNoteCall; | |||
fData->setSizeCallbackFunc = setSizeCall; | |||
fData->fileRequestCallbackFunc = fileRequestCall; | |||
#if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
// unused | |||
@@ -1,6 +1,6 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2012-2020 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
@@ -19,12 +19,14 @@ | |||
#include "../extra/String.hpp" | |||
#include "lv2/atom.h" | |||
#include "lv2/atom-forge.h" | |||
#include "lv2/atom-util.h" | |||
#include "lv2/data-access.h" | |||
#include "lv2/instance-access.h" | |||
#include "lv2/midi.h" | |||
#include "lv2/options.h" | |||
#include "lv2/parameters.h" | |||
#include "lv2/patch.h" | |||
#include "lv2/ui.h" | |||
#include "lv2/urid.h" | |||
#include "lv2/lv2_kxstudio_properties.h" | |||
@@ -47,22 +49,46 @@ static const sendNoteFunc sendNoteCallback = nullptr; | |||
// ----------------------------------------------------------------------- | |||
template <class LV2F> | |||
static const LV2F* getLv2Feature(const LV2_Feature* const* features, const char* const uri) | |||
{ | |||
for (int i=0; features[i] != nullptr; ++i) | |||
{ | |||
if (std::strcmp(features[i]->URI, uri) == 0) | |||
return (const LV2F*)features[i]->data; | |||
} | |||
return nullptr; | |||
} | |||
class UiLv2 | |||
{ | |||
public: | |||
UiLv2(const char* const bundlePath, const intptr_t winId, | |||
const LV2_Options_Option* options, const LV2_URID_Map* const uridMap, const LV2UI_Resize* const uiResz, const LV2UI_Touch* uiTouch, | |||
const LV2UI_Controller controller, const LV2UI_Write_Function writeFunc, | |||
const float scaleFactor, LV2UI_Widget* const widget, void* const dspPtr) | |||
: fUI(this, winId, editParameterCallback, setParameterCallback, setStateCallback, sendNoteCallback, setSizeCallback, scaleFactor, dspPtr, bundlePath), | |||
UiLv2(const char* const bundlePath, | |||
const intptr_t winId, | |||
const LV2_Options_Option* options, | |||
const LV2_URID_Map* const uridMap, | |||
const LV2_Feature* const* const features, | |||
const LV2UI_Controller controller, | |||
const LV2UI_Write_Function writeFunc, | |||
const float scaleFactor, | |||
LV2UI_Widget* const widget, | |||
void* const dspPtr) | |||
: fUI(this, winId, | |||
editParameterCallback, | |||
setParameterCallback, | |||
setStateCallback, | |||
sendNoteCallback, | |||
setSizeCallback, | |||
fileRequestCallback, | |||
scaleFactor, dspPtr, bundlePath), | |||
fUridMap(uridMap), | |||
fUiResize(uiResz), | |||
fUiTouch(uiTouch), | |||
fUiRequestValue(getLv2Feature<LV2UI_Request_Value>(features, LV2_UI__requestValue)), | |||
fUiResize(getLv2Feature<LV2UI_Resize>(features, LV2_UI__resize)), | |||
fUiTouch(getLv2Feature<LV2UI_Touch>(features, LV2_UI__touch)), | |||
fController(controller), | |||
fWriteFunction(writeFunc), | |||
fEventTransferURID(uridMap->map(uridMap->handle, LV2_ATOM__eventTransfer)), | |||
fMidiEventURID(uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent)), | |||
fKeyValueURID(uridMap->map(uridMap->handle, DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")), | |||
fURIDs(uridMap), | |||
fWinIdWasNull(winId == 0) | |||
{ | |||
if (fUiResize != nullptr && winId != 0) | |||
@@ -82,8 +108,8 @@ public: | |||
// if winId == 0 then options must not be null | |||
DISTRHO_SAFE_ASSERT_RETURN(options != nullptr,); | |||
const LV2_URID uridWindowTitle(uridMap->map(uridMap->handle, LV2_UI__windowTitle)); | |||
const LV2_URID uridTransientWinId(uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TransientWindowId)); | |||
const LV2_URID uridWindowTitle = uridMap->map(uridMap->handle, LV2_UI__windowTitle); | |||
const LV2_URID uridTransientWinId = uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TransientWindowId); | |||
bool hasTitle = false; | |||
@@ -91,7 +117,7 @@ public: | |||
{ | |||
if (options[i].key == uridTransientWinId) | |||
{ | |||
if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Long)) | |||
if (options[i].type == fURIDs.atomLong) | |||
{ | |||
if (const int64_t transientWinId = *(const int64_t*)options[i].value) | |||
fUI.setWindowTransientWinId(static_cast<intptr_t>(transientWinId)); | |||
@@ -101,7 +127,7 @@ public: | |||
} | |||
else if (options[i].key == uridWindowTitle) | |||
{ | |||
if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__String)) | |||
if (options[i].type == fURIDs.atomString) | |||
{ | |||
if (const char* const windowTitle = (const char*)options[i].value) | |||
{ | |||
@@ -124,27 +150,32 @@ public: | |||
{ | |||
if (format == 0) | |||
{ | |||
const uint32_t parameterOffset(fUI.getParameterOffset()); | |||
const uint32_t parameterOffset = fUI.getParameterOffset(); | |||
if (rindex < parameterOffset) | |||
return; | |||
DISTRHO_SAFE_ASSERT_RETURN(bufferSize == sizeof(float),) | |||
const float value(*(const float*)buffer); | |||
const float value = *(const float*)buffer; | |||
fUI.parameterChanged(rindex-parameterOffset, value); | |||
} | |||
#if DISTRHO_PLUGIN_WANT_STATE | |||
else if (format == fEventTransferURID) | |||
else if (format == fURIDs.atomEventTransfer) | |||
{ | |||
const LV2_Atom* const atom((const LV2_Atom*)buffer); | |||
DISTRHO_SAFE_ASSERT_RETURN(atom->type == fKeyValueURID,); | |||
const LV2_Atom* const atom = (const LV2_Atom*)buffer; | |||
const char* const key = (const char*)LV2_ATOM_BODY_CONST(atom); | |||
const char* const value = key+(std::strlen(key)+1); | |||
if (atom->type == fURIDs.dpfKeyValue) | |||
{ | |||
const char* const key = (const char*)LV2_ATOM_BODY_CONST(atom); | |||
const char* const value = key+(std::strlen(key)+1); | |||
fUI.stateChanged(key, value); | |||
fUI.stateChanged(key, value); | |||
} | |||
else | |||
{ | |||
d_stdout("received atom not dpfKeyValue"); | |||
} | |||
} | |||
#endif | |||
} | |||
@@ -187,11 +218,11 @@ public: | |||
{ | |||
for (int i=0; options[i].key != 0; ++i) | |||
{ | |||
if (options[i].key == fUridMap->map(fUridMap->handle, LV2_PARAMETERS__sampleRate)) | |||
if (options[i].key == fURIDs.paramSampleRate) | |||
{ | |||
if (options[i].type == fUridMap->map(fUridMap->handle, LV2_ATOM__Float)) | |||
if (options[i].type == fURIDs.atomFloat) | |||
{ | |||
const float sampleRate(*(const float*)options[i].value); | |||
const float sampleRate = *(const float*)options[i].value; | |||
fUI.setSampleRate(sampleRate); | |||
continue; | |||
} | |||
@@ -211,7 +242,7 @@ public: | |||
#if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
void lv2ui_select_program(const uint32_t bank, const uint32_t program) | |||
{ | |||
const uint32_t realProgram(bank * 128 + program); | |||
const uint32_t realProgram = bank * 128 + program; | |||
fUI.programLoaded(realProgram); | |||
} | |||
@@ -237,7 +268,7 @@ protected: | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); | |||
const uint32_t eventInPortIndex(DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS); | |||
const uint32_t eventInPortIndex = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS; | |||
// join key and value | |||
String tmpStr; | |||
@@ -248,23 +279,23 @@ protected: | |||
tmpStr[std::strlen(key)] = '\0'; | |||
// set msg size (key + separator + value + null terminator) | |||
const size_t msgSize(tmpStr.length()+1); | |||
const size_t msgSize = tmpStr.length() + 1U; | |||
// reserve atom space | |||
const size_t atomSize(sizeof(LV2_Atom) + msgSize); | |||
const size_t atomSize = sizeof(LV2_Atom) + msgSize; | |||
char atomBuf[atomSize]; | |||
std::memset(atomBuf, 0, atomSize); | |||
// set atom info | |||
LV2_Atom* const atom((LV2_Atom*)atomBuf); | |||
LV2_Atom* const atom = (LV2_Atom*)atomBuf; | |||
atom->size = msgSize; | |||
atom->type = fKeyValueURID; | |||
atom->type = fURIDs.dpfKeyValue; | |||
// set atom data | |||
std::memcpy(atomBuf + sizeof(LV2_Atom), tmpStr.buffer(), msgSize); | |||
// send to DSP side | |||
fWriteFunction(fController, eventInPortIndex, atomSize, fEventTransferURID, atom); | |||
fWriteFunction(fController, eventInPortIndex, atomSize, fURIDs.atomEventTransfer, atom); | |||
} | |||
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT | |||
@@ -275,7 +306,7 @@ protected: | |||
if (channel > 0xF) | |||
return; | |||
const uint32_t eventInPortIndex(DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS); | |||
const uint32_t eventInPortIndex = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS; | |||
LV2_Atom_MidiEvent atomMidiEvent; | |||
atomMidiEvent.atom.size = 3; | |||
@@ -286,7 +317,8 @@ protected: | |||
atomMidiEvent.data[2] = velocity; | |||
// send to DSP side | |||
fWriteFunction(fController, eventInPortIndex, lv2_atom_total_size(&atomMidiEvent.atom), fEventTransferURID, &atomMidiEvent); | |||
fWriteFunction(fController, eventInPortIndex, lv2_atom_total_size(&atomMidiEvent.atom), | |||
fURIDs.atomEventTransfer, &atomMidiEvent); | |||
} | |||
#endif | |||
@@ -298,22 +330,68 @@ protected: | |||
fUiResize->ui_resize(fUiResize->handle, width, height); | |||
} | |||
bool fileRequest(const char* const key) | |||
{ | |||
d_stdout("UI file request %s %p", key, fUiRequestValue); | |||
if (fUiRequestValue == nullptr) | |||
return false; | |||
String dpf_lv2_key(DISTRHO_PLUGIN_URI "#"); | |||
dpf_lv2_key += key; | |||
const int r = fUiRequestValue->request(fUiRequestValue->handle, | |||
fUridMap->map(fUridMap->handle, dpf_lv2_key.buffer()), | |||
fURIDs.atomPath, | |||
nullptr); | |||
d_stdout("UI file request %s %p => %s %i", key, fUiRequestValue, dpf_lv2_key.buffer(), r); | |||
return r == LV2UI_REQUEST_VALUE_SUCCESS; | |||
} | |||
private: | |||
UIExporter fUI; | |||
// LV2 features | |||
const LV2_URID_Map* const fUridMap; | |||
const LV2UI_Resize* const fUiResize; | |||
const LV2UI_Touch* const fUiTouch; | |||
const LV2_URID_Map* const fUridMap; | |||
const LV2UI_Request_Value* const fUiRequestValue; | |||
const LV2UI_Resize* const fUiResize; | |||
const LV2UI_Touch* const fUiTouch; | |||
// LV2 UI stuff | |||
const LV2UI_Controller fController; | |||
const LV2UI_Write_Function fWriteFunction; | |||
// Need to save this | |||
const LV2_URID fEventTransferURID; | |||
const LV2_URID fMidiEventURID; | |||
const LV2_URID fKeyValueURID; | |||
// LV2 URIDs | |||
const struct URIDs { | |||
const LV2_URID_Map* _uridMap; | |||
LV2_URID dpfKeyValue; | |||
LV2_URID atomEventTransfer; | |||
LV2_URID atomFloat; | |||
LV2_URID atomLong; | |||
LV2_URID atomPath; | |||
LV2_URID atomString; | |||
LV2_URID midiEvent; | |||
LV2_URID paramSampleRate; | |||
LV2_URID patchSet; | |||
URIDs(const LV2_URID_Map* const uridMap) | |||
: _uridMap(uridMap), | |||
dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")), | |||
atomEventTransfer(map(LV2_ATOM__eventTransfer)), | |||
atomFloat(map(LV2_ATOM__Float)), | |||
atomLong(map(LV2_ATOM__Long)), | |||
atomPath(map(LV2_ATOM__Path)), | |||
atomString(map(LV2_ATOM__String)), | |||
midiEvent(map(LV2_MIDI__MidiEvent)), | |||
paramSampleRate(map(LV2_PARAMETERS__sampleRate)), | |||
patchSet(map(LV2_PATCH__Set)) {} | |||
inline LV2_URID map(const char* const uri) const | |||
{ | |||
return _uridMap->map(_uridMap->handle, uri); | |||
} | |||
} fURIDs; | |||
// using ui:showInterface if true | |||
bool fWinIdWasNull; | |||
@@ -350,13 +428,23 @@ private: | |||
uiPtr->setSize(width, height); | |||
} | |||
static bool fileRequestCallback(void* ptr, const char* key) | |||
{ | |||
return uiPtr->fileRequest(key); | |||
} | |||
#undef uiPtr | |||
}; | |||
// ----------------------------------------------------------------------- | |||
static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, const char* bundlePath, | |||
LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features) | |||
static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, | |||
const char* const uri, | |||
const char* const bundlePath, | |||
const LV2UI_Write_Function writeFunction, | |||
const LV2UI_Controller controller, | |||
LV2UI_Widget* const widget, | |||
const LV2_Feature* const* const features) | |||
{ | |||
if (uri == nullptr || std::strcmp(uri, DISTRHO_PLUGIN_URI) != 0) | |||
{ | |||
@@ -364,12 +452,10 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, | |||
return nullptr; | |||
} | |||
const LV2_Options_Option* options = nullptr; | |||
const LV2_URID_Map* uridMap = nullptr; | |||
const LV2UI_Resize* uiResize = nullptr; | |||
const LV2UI_Touch* uiTouch = nullptr; | |||
void* parentId = nullptr; | |||
void* instance = nullptr; | |||
const LV2_Options_Option* options = nullptr; | |||
const LV2_URID_Map* uridMap = nullptr; | |||
void* parentId = nullptr; | |||
void* instance = nullptr; | |||
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
struct LV2_DirectAccess_Interface { | |||
@@ -380,16 +466,12 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, | |||
for (int i=0; features[i] != nullptr; ++i) | |||
{ | |||
if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0) | |||
/**/ if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0) | |||
options = (const LV2_Options_Option*)features[i]->data; | |||
else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0) | |||
uridMap = (const LV2_URID_Map*)features[i]->data; | |||
else if (std::strcmp(features[i]->URI, LV2_UI__resize) == 0) | |||
uiResize = (const LV2UI_Resize*)features[i]->data; | |||
else if (std::strcmp(features[i]->URI, LV2_UI__parent) == 0) | |||
parentId = features[i]->data; | |||
else if (std::strcmp(features[i]->URI, LV2_UI__touch) == 0) | |||
uiTouch = (const LV2UI_Touch*)features[i]->data; | |||
#if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS | |||
else if (std::strcmp(features[i]->URI, LV2_DATA_ACCESS_URI) == 0) | |||
extData = (const LV2_Extension_Data_Feature*)features[i]->data; | |||
@@ -439,9 +521,9 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, | |||
if (options != nullptr) | |||
{ | |||
const LV2_URID uridAtomFloat(uridMap->map(uridMap->handle, LV2_ATOM__Float)); | |||
const LV2_URID uridSampleRate(uridMap->map(uridMap->handle, LV2_PARAMETERS__sampleRate)); | |||
const LV2_URID uridScaleFactor(uridMap->map(uridMap->handle, LV2_UI__scaleFactor)); | |||
const LV2_URID uridAtomFloat = uridMap->map(uridMap->handle, LV2_ATOM__Float); | |||
const LV2_URID uridSampleRate = uridMap->map(uridMap->handle, LV2_PARAMETERS__sampleRate); | |||
const LV2_URID uridScaleFactor = uridMap->map(uridMap->handle, LV2_UI__scaleFactor); | |||
for (int i=0; options[i].key != 0; ++i) | |||
{ | |||
@@ -468,7 +550,8 @@ static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char* uri, | |||
d_lastUiSampleRate = 44100.0; | |||
} | |||
return new UiLv2(bundlePath, winId, options, uridMap, uiResize, uiTouch, controller, writeFunction, scaleFactor, widget, instance); | |||
return new UiLv2(bundlePath, winId, options, uridMap, features, | |||
controller, writeFunction, scaleFactor, widget, instance); | |||
} | |||
#define uiPtr ((UiLv2*)ui) | |||