Browse Source

Start implementation of state files; rework some code where sane

Signed-off-by: falkTX <falktx@falktx.com>
pull/232/head
falkTX 5 years ago
parent
commit
9d09b5e15b
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
12 changed files with 529 additions and 178 deletions
  1. +8
    -1
      distrho/DistrhoPlugin.hpp
  2. +15
    -2
      distrho/DistrhoUI.hpp
  3. +13
    -1
      distrho/src/DistrhoPluginChecks.h
  4. +9
    -0
      distrho/src/DistrhoPluginInternal.hpp
  5. +10
    -2
      distrho/src/DistrhoPluginJack.cpp
  6. +202
    -64
      distrho/src/DistrhoPluginLV2.cpp
  7. +68
    -23
      distrho/src/DistrhoPluginLV2export.cpp
  8. +18
    -4
      distrho/src/DistrhoPluginVST.cpp
  9. +8
    -1
      distrho/src/DistrhoUI.cpp
  10. +1
    -1
      distrho/src/DistrhoUIDSSI.cpp
  11. +34
    -19
      distrho/src/DistrhoUIInternal.hpp
  12. +143
    -60
      distrho/src/DistrhoUILV2.cpp

+ 8
- 1
distrho/DistrhoPlugin.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * 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 * 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 * 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; virtual void initState(uint32_t index, String& stateKey, String& defaultStateValue) = 0;
#endif #endif


#if DISTRHO_PLUGIN_WANT_STATEFILES
/**
TODO API under construction
*/
virtual bool isStateFile(uint32_t index) = 0;
#endif

/* -------------------------------------------------------------------------------------------------------- /* --------------------------------------------------------------------------------------------------------
* Internal data */ * Internal data */




+ 15
- 2
distrho/DistrhoUI.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * 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 * 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 * or without fee is hereby granted, provided that the above copyright notice and this
@@ -98,7 +98,7 @@ public:


/** /**
setParameterValue. setParameterValue.
Change a parameter value in the Plugin. Change a parameter value in the Plugin.
*/ */
void setParameterValue(uint32_t index, float value); void setParameterValue(uint32_t index, float value);
@@ -111,6 +111,19 @@ public:
void setState(const char* key, const char* value); void setState(const char* key, const char* value);
#endif #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 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
/** /**
sendNote. sendNote.


+ 13
- 1
distrho/src/DistrhoPluginChecks.h View File

@@ -77,6 +77,10 @@
# define DISTRHO_PLUGIN_WANT_STATE 0 # define DISTRHO_PLUGIN_WANT_STATE 0
#endif #endif


#ifndef DISTRHO_PLUGIN_WANT_STATEFILES
# define DISTRHO_PLUGIN_WANT_STATEFILES 0
#endif

#ifndef DISTRHO_PLUGIN_WANT_FULL_STATE #ifndef DISTRHO_PLUGIN_WANT_FULL_STATE
# define DISTRHO_PLUGIN_WANT_FULL_STATE 0 # define DISTRHO_PLUGIN_WANT_FULL_STATE 0
#endif #endif
@@ -108,7 +112,7 @@
// Define DISTRHO_UI_URI if needed // Define DISTRHO_UI_URI if needed


#ifndef DISTRHO_UI_URI #ifndef DISTRHO_UI_URI
# define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#UI"
# define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#DPF_UI"
#endif #endif


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@@ -127,6 +131,14 @@
# error Synths need MIDI input to work! # error Synths need MIDI input to work!
#endif #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 // Enable full state if plugin exports presets




+ 9
- 0
distrho/src/DistrhoPluginInternal.hpp View File

@@ -486,6 +486,15 @@ public:
return fData->stateDefValues[index]; 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 # if DISTRHO_PLUGIN_WANT_FULL_STATE
String getState(const char* key) const String getState(const char* key) const
{ {


+ 10
- 2
distrho/src/DistrhoPluginJack.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@@ -101,7 +101,15 @@ public:
PluginJack(jack_client_t* const client) PluginJack(jack_client_t* const client)
: fPlugin(this, writeMidiCallback), : fPlugin(this, writeMidiCallback),
#if DISTRHO_PLUGIN_HAS_UI #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 #endif
fClient(client) fClient(client)
{ {


+ 202
- 64
distrho/src/DistrhoPluginLV2.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * 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 * 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 * or without fee is hereby granted, provided that the above copyright notice and this
@@ -24,6 +24,7 @@
#include "lv2/midi.h" #include "lv2/midi.h"
#include "lv2/options.h" #include "lv2/options.h"
#include "lv2/parameters.h" #include "lv2/parameters.h"
#include "lv2/patch.h"
#include "lv2/state.h" #include "lv2/state.h"
#include "lv2/time.h" #include "lv2/time.h"
#include "lv2/urid.h" #include "lv2/urid.h"
@@ -54,7 +55,8 @@


START_NAMESPACE_DISTRHO 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 #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
static const writeMidiFunc writeMidiCallback = nullptr; static const writeMidiFunc writeMidiCallback = nullptr;
@@ -65,7 +67,10 @@ static const writeMidiFunc writeMidiCallback = nullptr;
class PluginLv2 class PluginLv2
{ {
public: 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), : fPlugin(this, writeMidiCallback),
fUsingNominal(usingNominal), fUsingNominal(usingNominal),
#ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD #ifdef DISTRHO_PLUGIN_LICENSED_FOR_MOD
@@ -127,6 +132,15 @@ public:


const String& dkey(fPlugin.getStateKey(i)); const String& dkey(fPlugin.getStateKey(i));
fStateMap[dkey] = fPlugin.getStateDefaultValue(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 else
@@ -521,16 +535,16 @@ public:
} }
#endif #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) LV2_ATOM_SEQUENCE_FOREACH(fPortEventsIn, event)
{ {
if (event == nullptr) if (event == nullptr)
break; 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 // check if this is our special message
if (std::strcmp((const char*)data, "__dpf_ui_data__") == 0) 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) for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i)
fNeededUiSends[i] = true; fNeededUiSends[i] = true;
} }
else
// no, send to DSP as usual // 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 #endif


@@ -653,19 +683,19 @@ public:
if (! fNeededUiSends[i]) if (! fNeededUiSends[i])
continue; 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) if (curKey != key)
continue; continue;


const String& value = cit->second;
const String& value(cit->second);


// set msg size (key + value + separator + 2x null terminator) // 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) if (sizeof(LV2_Atom_Event) + msgSize > capacity - fEventsOutData.offset)
{ {
@@ -673,21 +703,18 @@ public:
break; 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 // put data
aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + fEventsOutData.offset); aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fEventsOutData.port) + fEventsOutData.offset);
aev->time.frames = 0; aev->time.frames = 0;
aev->body.type = fURIDs.distrhoState;
aev->body.type = fURIDs.dpfKeyValue;
aev->body.size = msgSize; 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)); fEventsOutData.growBy(lv2_atom_pad_size(sizeof(LV2_Atom_Event) + msgSize));


@@ -795,7 +822,7 @@ public:


# if DISTRHO_PLUGIN_WANT_FULL_STATE # if DISTRHO_PLUGIN_WANT_FULL_STATE
// Update 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; const String& key = cit->first;
fStateMap[key] = fPlugin.getState(key); fStateMap[key] = fPlugin.getState(key);
@@ -811,22 +838,52 @@ public:
{ {
# if DISTRHO_PLUGIN_WANT_FULL_STATE # if DISTRHO_PLUGIN_WANT_FULL_STATE
// Update current 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; const String& key = cit->first;
fStateMap[key] = fPlugin.getState(key); fStateMap[key] = fPlugin.getState(key);
} }
# endif # 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; return LV2_STATE_SUCCESS;
@@ -837,23 +894,42 @@ public:
size_t size; size_t size;
uint32_t type, flags; uint32_t type, flags;


String dpf_lv2_key;
LV2_URID urid;

for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i) for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i)
{ {
const String& key(fPlugin.getStateKey(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; size = 0;
type = 0; type = 0;
flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE; 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) if (data == nullptr || size == 0)
continue; 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); DISTRHO_SAFE_ASSERT_CONTINUE(length == size || length+1 == size);


setState(key, value); setState(key, value);
@@ -871,12 +947,55 @@ public:


LV2_Worker_Status lv2_work(const void* const data) 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*) LV2_Worker_Status lv2_work_response(uint32_t, const void*)
@@ -995,16 +1114,21 @@ private:


// LV2 URIDs // LV2 URIDs
struct URIDs { struct URIDs {
const LV2_URID_Map* _uridMap;
LV2_URID atomBlank; LV2_URID atomBlank;
LV2_URID atomObject; LV2_URID atomObject;
LV2_URID atomDouble; LV2_URID atomDouble;
LV2_URID atomFloat; LV2_URID atomFloat;
LV2_URID atomInt; LV2_URID atomInt;
LV2_URID atomLong; LV2_URID atomLong;
LV2_URID atomPath;
LV2_URID atomSequence; LV2_URID atomSequence;
LV2_URID atomString; LV2_URID atomString;
LV2_URID distrhoState;
LV2_URID atomURID;
LV2_URID dpfKeyValue;
LV2_URID midiEvent; LV2_URID midiEvent;
LV2_URID patchProperty;
LV2_URID patchValue;
LV2_URID timePosition; LV2_URID timePosition;
LV2_URID timeBar; LV2_URID timeBar;
LV2_URID timeBarBeat; LV2_URID timeBarBeat;
@@ -1016,25 +1140,35 @@ private:
LV2_URID timeSpeed; LV2_URID timeSpeed;


URIDs(const LV2_URID_Map* const uridMap) 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; } fURIDs;


// LV2 features // LV2 features
@@ -1042,7 +1176,7 @@ private:
const LV2_Worker_Schedule* const fWorker; const LV2_Worker_Schedule* const fWorker;


#if DISTRHO_PLUGIN_WANT_STATE #if DISTRHO_PLUGIN_WANT_STATE
StringMap fStateMap;
StringToStringMap fStateMap;
bool* fNeededUiSends; bool* fNeededUiSends;


void setState(const char* const key, const char* const newValue) void setState(const char* const key, const char* const newValue)
@@ -1054,7 +1188,7 @@ private:
return; return;


// check if key already exists // 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); const String& dkey(it->first);


@@ -1067,6 +1201,10 @@ private:


d_stderr("Failed to find plugin state with key \"%s\"", key); d_stderr("Failed to find plugin state with key \"%s\"", key);
} }

# if DISTRHO_PLUGIN_WANT_STATEFILES
UridToStringMap fUridStateFileMap;
# endif
#endif #endif


void updateParameterOutputsAndTriggers() void updateParameterOutputsAndTriggers()


+ 68
- 23
distrho/src/DistrhoPluginLV2export.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * 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 * 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 * 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/instance-access.h"
#include "lv2/midi.h" #include "lv2/midi.h"
#include "lv2/options.h" #include "lv2/options.h"
#include "lv2/patch.h"
#include "lv2/port-props.h" #include "lv2/port-props.h"
#include "lv2/presets.h" #include "lv2/presets.h"
#include "lv2/resize-port.h" #include "lv2/resize-port.h"
@@ -45,6 +46,10 @@
# error DISTRHO_PLUGIN_URI undefined! # error DISTRHO_PLUGIN_URI undefined!
#endif #endif


#ifndef DISTRHO_PLUGIN_LV2_STATE_PREFIX
# define DISTRHO_PLUGIN_LV2_STATE_PREFIX "urn:distrho:"
#endif

#ifndef DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE #ifndef DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE
# define DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE 2048 # define DISTRHO_PLUGIN_MINIMUM_BUFFER_SIZE 2048
#endif #endif
@@ -139,6 +144,9 @@ static const char* const lv2ManifestUiOptionalFeatures[] =
"ui:parent", "ui:parent",
"ui:resize", "ui:resize",
"ui:touch", "ui:touch",
#endif
#if DISTRHO_PLUGIN_WANT_STATEFILES
"ui:requestValue",
#endif #endif
nullptr nullptr
}; };
@@ -220,8 +228,8 @@ void lv2_generate_ttl(const char* const basename)
d_lastBufferSize = 0; d_lastBufferSize = 0;
d_lastSampleRate = 0.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 #if DISTRHO_PLUGIN_HAS_UI
String pluginUI(pluginDLL); String pluginUI(pluginDLL);
@@ -317,26 +325,45 @@ void lv2_generate_ttl(const char* const basename)


// header // header
#if DISTRHO_LV2_USE_EVENTS_IN || DISTRHO_LV2_USE_EVENTS_OUT #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 #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 #ifdef DISTRHO_PLUGIN_BRAND
pluginString += "@prefix mod: <http://moddevices.com/ns/mod#> .\n";
pluginString += "@prefix mod: <http://moddevices.com/ns/mod#> .\n";
#endif #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 #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 #endif
#if DISTRHO_PLUGIN_HAS_UI #if DISTRHO_PLUGIN_HAS_UI
pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n";
pluginString += "@prefix ui: <" LV2_UI_PREFIX "> .\n";
#endif #endif
pluginString += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n";
pluginString += "@prefix unit: <" LV2_UNITS_PREFIX "> .\n";
pluginString += "\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 // plugin
pluginString += "<" DISTRHO_PLUGIN_URI ">\n"; pluginString += "<" DISTRHO_PLUGIN_URI ">\n";
#ifdef DISTRHO_PLUGIN_LV2_CATEGORY #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, "lv2:requiredFeature", lv2ManifestPluginRequiredFeatures, 4);
addAttribute(pluginString, "opts:supportedOption", lv2ManifestPluginSupportedOptions, 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 // UI
#if DISTRHO_PLUGIN_HAS_UI #if DISTRHO_PLUGIN_HAS_UI
pluginString += " ui:ui <" DISTRHO_UI_URI "> ;\n"; pluginString += " ui:ui <" DISTRHO_UI_URI "> ;\n";
@@ -582,33 +624,36 @@ void lv2_generate_ttl(const char* const basename)
// unit // unit
const String& unit(plugin.getParameterUnit(i)); 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"; pluginString += " unit:unit unit:db ;\n";
} }
else if (unit == "hz" || unit == "Hz")
else if (lunit == "hz")
{ {
pluginString += " unit:unit unit:hz ;\n"; pluginString += " unit:unit unit:hz ;\n";
} }
else if (unit == "khz" || unit == "kHz")
else if (lunit == "khz")
{ {
pluginString += " unit:unit unit:khz ;\n"; pluginString += " unit:unit unit:khz ;\n";
} }
else if (unit == "mhz" || unit == "mHz")
else if (lunit == "mhz")
{ {
pluginString += " unit:unit unit:mhz ;\n"; pluginString += " unit:unit unit:mhz ;\n";
} }
else if (unit == "ms")
else if (lunit == "ms")
{ {
pluginString += " unit:unit unit:ms ;\n"; pluginString += " unit:unit unit:ms ;\n";
} }
else if (unit == "s")
else if (lunit == "s")
{ {
pluginString += " unit:unit unit:s ;\n"; pluginString += " unit:unit unit:s ;\n";
} }
else if (unit == "%")
else if (lunit == "%")
{ {
pluginString += " unit:unit unit:pc ;\n"; 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 key = plugin.getStateKey(j);
const String value = plugin.getState(key); const String value = plugin.getState(key);


presetString += " <urn:distrho:" + key + ">";
presetString += " <" DISTRHO_PLUGIN_LV2_STATE_PREFIX + key + ">";


if (value.length() < 10) if (value.length() < 10)
presetString += " \"" + value + "\" ;\n"; presetString += " \"" + value + "\" ;\n";


+ 18
- 4
distrho/src/DistrhoPluginVST.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * 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 * 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 * or without fee is hereby granted, provided that the above copyright notice and this
@@ -161,12 +161,24 @@ public:
class UIVst class UIVst
{ {
public: 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), : fAudioMaster(audioMaster),
fEffect(effect), fEffect(effect),
fUiHelper(uiHelper), fUiHelper(uiHelper),
fPlugin(plugin), 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) fShouldCaptureVstKeys(false)
{ {
// FIXME only needed for windows? // FIXME only needed for windows?
@@ -581,7 +593,9 @@ public:
// TODO // TODO
const float scaleFactor = 1.0f; 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.right = tmpUI.getWidth();
fVstRect.bottom = tmpUI.getHeight(); fVstRect.bottom = tmpUI.getHeight();
tmpUI.quit(); tmpUI.quit();


+ 8
- 1
distrho/src/DistrhoUI.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * 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 * 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 * 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 #endif


#if DISTRHO_PLUGIN_WANT_STATEFILES
bool UI::requestStateFile(const char* key)
{
return pData->fileRequestCallback(key);
}
#endif

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity) void UI::sendNote(uint8_t channel, uint8_t note, uint8_t velocity)
{ {


+ 1
- 1
distrho/src/DistrhoUIDSSI.cpp View File

@@ -97,7 +97,7 @@ class UIDssi
{ {
public: public:
UIDssi(const OscData& oscData, const char* const uiTitle) 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), fHostClosed(false),
fOscData(oscData) fOscData(oscData)
{ {


+ 34
- 19
distrho/src/DistrhoUIInternal.hpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * 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 * 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 * or without fee is hereby granted, provided that the above copyright notice and this
@@ -48,11 +48,12 @@ extern Window* d_lastUiWindow;
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// UI callbacks // 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 // UI private data
@@ -72,12 +73,13 @@ struct UI::PrivateData {
uint minHeight; uint minHeight;


// Callbacks // 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 PrivateData() noexcept
: sampleRate(d_lastUiSampleRate), : sampleRate(d_lastUiSampleRate),
@@ -94,7 +96,8 @@ struct UI::PrivateData {
setParamCallbackFunc(nullptr), setParamCallbackFunc(nullptr),
setStateCallbackFunc(nullptr), setStateCallbackFunc(nullptr),
sendNoteCallbackFunc(nullptr), sendNoteCallbackFunc(nullptr),
setSizeCallbackFunc(nullptr)
setSizeCallbackFunc(nullptr),
fileRequestCallbackFunc(nullptr)
{ {
DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate)); DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate));


@@ -144,6 +147,16 @@ struct UI::PrivateData {
if (setSizeCallbackFunc != nullptr) if (setSizeCallbackFunc != nullptr)
setSizeCallbackFunc(callbacksPtr, width, height); 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 setStateFunc setStateCall,
const sendNoteFunc sendNoteCall, const sendNoteFunc sendNoteCall,
const setSizeFunc setSizeCall, const setSizeFunc setSizeCall,
const fileRequestFunc fileRequestCall,
const float scaleFactor = 1.0f, const float scaleFactor = 1.0f,
void* const dspPtr = nullptr, void* const dspPtr = nullptr,
const char* const bundlePath = nullptr) const char* const bundlePath = nullptr)
@@ -274,12 +288,13 @@ public:
DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(fData != 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 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
// unused // unused


+ 143
- 60
distrho/src/DistrhoUILV2.cpp View File

@@ -1,6 +1,6 @@
/* /*
* DISTRHO Plugin Framework (DPF) * 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 * 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 * or without fee is hereby granted, provided that the above copyright notice and this
@@ -19,12 +19,14 @@
#include "../extra/String.hpp" #include "../extra/String.hpp"


#include "lv2/atom.h" #include "lv2/atom.h"
#include "lv2/atom-forge.h"
#include "lv2/atom-util.h" #include "lv2/atom-util.h"
#include "lv2/data-access.h" #include "lv2/data-access.h"
#include "lv2/instance-access.h" #include "lv2/instance-access.h"
#include "lv2/midi.h" #include "lv2/midi.h"
#include "lv2/options.h" #include "lv2/options.h"
#include "lv2/parameters.h" #include "lv2/parameters.h"
#include "lv2/patch.h"
#include "lv2/ui.h" #include "lv2/ui.h"
#include "lv2/urid.h" #include "lv2/urid.h"
#include "lv2/lv2_kxstudio_properties.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 class UiLv2
{ {
public: 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), 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), fController(controller),
fWriteFunction(writeFunc), 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) fWinIdWasNull(winId == 0)
{ {
if (fUiResize != nullptr && winId != 0) if (fUiResize != nullptr && winId != 0)
@@ -82,8 +108,8 @@ public:
// if winId == 0 then options must not be null // if winId == 0 then options must not be null
DISTRHO_SAFE_ASSERT_RETURN(options != nullptr,); 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; bool hasTitle = false;


@@ -91,7 +117,7 @@ public:
{ {
if (options[i].key == uridTransientWinId) 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) if (const int64_t transientWinId = *(const int64_t*)options[i].value)
fUI.setWindowTransientWinId(static_cast<intptr_t>(transientWinId)); fUI.setWindowTransientWinId(static_cast<intptr_t>(transientWinId));
@@ -101,7 +127,7 @@ public:
} }
else if (options[i].key == uridWindowTitle) 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) if (const char* const windowTitle = (const char*)options[i].value)
{ {
@@ -124,27 +150,32 @@ public:
{ {
if (format == 0) if (format == 0)
{ {
const uint32_t parameterOffset(fUI.getParameterOffset());
const uint32_t parameterOffset = fUI.getParameterOffset();


if (rindex < parameterOffset) if (rindex < parameterOffset)
return; return;


DISTRHO_SAFE_ASSERT_RETURN(bufferSize == sizeof(float),) DISTRHO_SAFE_ASSERT_RETURN(bufferSize == sizeof(float),)


const float value(*(const float*)buffer);
const float value = *(const float*)buffer;
fUI.parameterChanged(rindex-parameterOffset, value); fUI.parameterChanged(rindex-parameterOffset, value);
} }
#if DISTRHO_PLUGIN_WANT_STATE #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 #endif
} }
@@ -187,11 +218,11 @@ public:
{ {
for (int i=0; options[i].key != 0; ++i) 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); fUI.setSampleRate(sampleRate);
continue; continue;
} }
@@ -211,7 +242,7 @@ public:
#if DISTRHO_PLUGIN_WANT_PROGRAMS #if DISTRHO_PLUGIN_WANT_PROGRAMS
void lv2ui_select_program(const uint32_t bank, const uint32_t program) 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); fUI.programLoaded(realProgram);
} }
@@ -237,7 +268,7 @@ protected:
{ {
DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,); 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 // join key and value
String tmpStr; String tmpStr;
@@ -248,23 +279,23 @@ protected:
tmpStr[std::strlen(key)] = '\0'; tmpStr[std::strlen(key)] = '\0';


// set msg size (key + separator + value + null terminator) // 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 // reserve atom space
const size_t atomSize(sizeof(LV2_Atom) + msgSize);
const size_t atomSize = sizeof(LV2_Atom) + msgSize;
char atomBuf[atomSize]; char atomBuf[atomSize];
std::memset(atomBuf, 0, atomSize); std::memset(atomBuf, 0, atomSize);


// set atom info // set atom info
LV2_Atom* const atom((LV2_Atom*)atomBuf);
LV2_Atom* const atom = (LV2_Atom*)atomBuf;
atom->size = msgSize; atom->size = msgSize;
atom->type = fKeyValueURID;
atom->type = fURIDs.dpfKeyValue;


// set atom data // set atom data
std::memcpy(atomBuf + sizeof(LV2_Atom), tmpStr.buffer(), msgSize); std::memcpy(atomBuf + sizeof(LV2_Atom), tmpStr.buffer(), msgSize);


// send to DSP side // send to DSP side
fWriteFunction(fController, eventInPortIndex, atomSize, fEventTransferURID, atom);
fWriteFunction(fController, eventInPortIndex, atomSize, fURIDs.atomEventTransfer, atom);
} }


#if DISTRHO_PLUGIN_WANT_MIDI_INPUT #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
@@ -275,7 +306,7 @@ protected:
if (channel > 0xF) if (channel > 0xF)
return; 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; LV2_Atom_MidiEvent atomMidiEvent;
atomMidiEvent.atom.size = 3; atomMidiEvent.atom.size = 3;
@@ -286,7 +317,8 @@ protected:
atomMidiEvent.data[2] = velocity; atomMidiEvent.data[2] = velocity;


// send to DSP side // 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 #endif


@@ -298,22 +330,68 @@ protected:
fUiResize->ui_resize(fUiResize->handle, width, height); 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: private:
UIExporter fUI; UIExporter fUI;


// LV2 features // 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 // LV2 UI stuff
const LV2UI_Controller fController; const LV2UI_Controller fController;
const LV2UI_Write_Function fWriteFunction; 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 // using ui:showInterface if true
bool fWinIdWasNull; bool fWinIdWasNull;
@@ -350,13 +428,23 @@ private:
uiPtr->setSize(width, height); uiPtr->setSize(width, height);
} }


static bool fileRequestCallback(void* ptr, const char* key)
{
return uiPtr->fileRequest(key);
}

#undef uiPtr #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) 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; 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 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
struct LV2_DirectAccess_Interface { 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) 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; options = (const LV2_Options_Option*)features[i]->data;
else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0) else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0)
uridMap = (const LV2_URID_Map*)features[i]->data; 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) else if (std::strcmp(features[i]->URI, LV2_UI__parent) == 0)
parentId = features[i]->data; 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 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
else if (std::strcmp(features[i]->URI, LV2_DATA_ACCESS_URI) == 0) else if (std::strcmp(features[i]->URI, LV2_DATA_ACCESS_URI) == 0)
extData = (const LV2_Extension_Data_Feature*)features[i]->data; 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) 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) 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; 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) #define uiPtr ((UiLv2*)ui)


Loading…
Cancel
Save