Browse Source

Auto-scan binary plugins with cache

Signed-off-by: falkTX <falktx@falktx.com>
tags/v1.3
falkTX 2 years ago
parent
commit
e358d9ee88
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
6 changed files with 178 additions and 43 deletions
  1. +6
    -7
      README.md
  2. +1
    -1
      carla
  3. +1
    -1
      dpf
  4. +1
    -0
      plugins/Common/IldaeilBasePlugin.hpp
  5. +27
    -3
      plugins/Common/IldaeilPlugin.cpp
  6. +142
    -31
      plugins/Common/IldaeilUI.cpp

+ 6
- 7
README.md View File

@@ -2,7 +2,7 @@


DISTRHO Ildaeil is mini-plugin host working as a plugin, allowing one-to-one plugin format reusage. DISTRHO Ildaeil is mini-plugin host working as a plugin, allowing one-to-one plugin format reusage.
The idea is to load it as a plugin inside your DAW and then the other "real" plugin inside Ildaeil. The idea is to load it as a plugin inside your DAW and then the other "real" plugin inside Ildaeil.
This allows, for example, a VST3 host to load LV2 plugins.
This allows, for example, a VST3 host to load LV2 plugins and vice-versa.


The Ildaeil name comes from the korean 일대일, which means "one to one". The Ildaeil name comes from the korean 일대일, which means "one to one".


@@ -31,24 +31,23 @@ The current formats Ildaeil can work as are:
And it can load the following plugin formats: And it can load the following plugin formats:


- Internal (from Carla) - Internal (from Carla)
- LADSPA
- DSSI
- LV2 - LV2
- VST2
- VST3
- CLAP
- JSFX - JSFX


With a few extra formats through the "Load from file..." action: With a few extra formats through the "Load from file..." action:


- CLAP plugins
- VST2 plugins
- Audio files (through internal audio file player, synced to transport) - Audio files (through internal audio file player, synced to transport)
- MIDI files (through internal MIDI file player, synced to transport) - MIDI files (through internal MIDI file player, synced to transport)
- SFZ files (through internal SFZero)


## Goals ## Goals


Later on, in theory, it should be able to load the following plugin formats: Later on, in theory, it should be able to load the following plugin formats:


- LADSPA
- DSSI
- VST3
- AU (macOS only) - AU (macOS only)


Eventually the following files could be loaded too: Eventually the following files could be loaded too:


+ 1
- 1
carla

@@ -1 +1 @@
Subproject commit ad7def4bd0dbd9c2470a72cae79089a0a17dad7f
Subproject commit e4e1f7d158f7b41b7dd55d86a32449932623abc0

+ 1
- 1
dpf

@@ -1 +1 @@
Subproject commit 1736c842c5c6d609fabce8f46de3e732d17e9c90
Subproject commit b4460beb094502c868dda5991e356623b13ce658

+ 1
- 0
plugins/Common/IldaeilBasePlugin.hpp View File

@@ -58,6 +58,7 @@ public:


// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------


const char* ildaeilConfigDir();
void ildaeilProjectLoadedFromDSP(void* ui); void ildaeilProjectLoadedFromDSP(void* ui);
void ildaeilParameterChangeForUI(void* ui, uint32_t index, float value); void ildaeilParameterChangeForUI(void* ui, uint32_t index, float value);
void ildaeilCloseUI(void* ui); void ildaeilCloseUI(void* ui);


+ 27
- 3
plugins/Common/IldaeilPlugin.cpp View File

@@ -40,7 +40,7 @@ static void host_ui_custom_data_changed(NativeHostHandle handle, const char* key
static void host_ui_closed(NativeHostHandle handle); static void host_ui_closed(NativeHostHandle handle);
static const char* host_ui_open_file(NativeHostHandle handle, bool isDir, const char* title, const char* filter); static const char* host_ui_open_file(NativeHostHandle handle, bool isDir, const char* title, const char* filter);
static const char* host_ui_save_file(NativeHostHandle handle, bool isDir, const char* title, const char* filter); static const char* host_ui_save_file(NativeHostHandle handle, bool isDir, const char* title, const char* filter);
static intptr_t host_dispatcher(NativeHostHandle handle, NativeHostDispatcherOpcode opcode, int32_t index, intptr_t value, void* ptr, float opt);
static intptr_t host_dispatcher(NativeHostHandle h, NativeHostDispatcherOpcode op, int32_t, intptr_t, void*, float);
// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
@@ -48,13 +48,11 @@ Mutex IldaeilBasePlugin::sPluginInfoLoadMutex;
// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
#ifndef CARLA_OS_WIN
static water::String getHomePath() static water::String getHomePath()
{ {
static water::String path(water::File::getSpecialLocation(water::File::userHomeDirectory).getFullPathName()); static water::String path(water::File::getSpecialLocation(water::File::userHomeDirectory).getFullPathName());
return path; return path;
} }
#endif
static const char* getPathForLADSPA() static const char* getPathForLADSPA()
{ {
@@ -262,6 +260,32 @@ const char* IldaeilBasePlugin::getPluginPath(const PluginType ptype)
// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------
const char* ildaeilConfigDir()
{
static water::String configDir;
if (configDir.isEmpty())
{
#if defined(CARLA_OS_WASM)
configDir = "/userfiles";
#elif defined(CARLA_OS_MAC)
configDir = getHomePath() + "/Documents/Ildaeil";
#elif defined(CARLA_OS_WIN)
configDir = water::File::getSpecialLocation(water::File::winMyDocuments).getFullPathName() + "\\Ildaeil";
#else
if (const char* const xdgEnv = getenv("XDG_CONFIG_HOME"))
configDir = xdgEnv;
else
configDir = getHomePath() + "/.config";
configDir += "/Ildaeil";
#endif
}
return configDir.toRawUTF8();
}
// --------------------------------------------------------------------------------------------------------------------
class IldaeilPlugin : public IldaeilBasePlugin class IldaeilPlugin : public IldaeilBasePlugin
{ {
#if DISTRHO_PLUGIN_NUM_INPUTS == 0 || DISTRHO_PLUGIN_NUM_OUTPUTS == 0 #if DISTRHO_PLUGIN_NUM_INPUTS == 0 || DISTRHO_PLUGIN_NUM_OUTPUTS == 0


+ 142
- 31
plugins/Common/IldaeilUI.cpp View File

@@ -29,6 +29,11 @@
// IDE helper // IDE helper
#include "DearImGui.hpp" #include "DearImGui.hpp"


#include "water/files/File.h"
#include "water/files/FileInputStream.h"
#include "water/files/FileOutputStream.h"
#include "water/memory/MemoryBlock.h"

#include <string> #include <string>
#include <vector> #include <vector>


@@ -160,7 +165,6 @@ class IldaeilUI : public UI,
PluginType fNextPluginType; PluginType fNextPluginType;
uint fPluginId; uint fPluginId;
int fPluginSelected; int fPluginSelected;
bool fPluginScanningFinished;
bool fPluginHasCustomUI; bool fPluginHasCustomUI;
bool fPluginHasEmbedUI; bool fPluginHasEmbedUI;
bool fPluginHasFileOpen; bool fPluginHasFileOpen;
@@ -211,7 +215,6 @@ public:
fNextPluginType(fPluginType), fNextPluginType(fPluginType),
fPluginId(0), fPluginId(0),
fPluginSelected(-1), fPluginSelected(-1),
fPluginScanningFinished(false),
fPluginHasCustomUI(false), fPluginHasCustomUI(false),
fPluginHasEmbedUI(false), fPluginHasEmbedUI(false),
fPluginHasFileOpen(false), fPluginHasFileOpen(false),
@@ -790,22 +793,19 @@ protected:
if (fRunnerData.needsReinit) if (fRunnerData.needsReinit)
{ {
fRunnerData.needsReinit = false; fRunnerData.needsReinit = false;
fPluginScanningFinished = false;


{ {
const MutexLocker cml(fPluginsMutex); const MutexLocker cml(fPluginsMutex);
fPlugins.clear(); fPlugins.clear();
} }


{
const MutexLocker cml(fPlugin->sPluginInfoLoadMutex);

d_stdout("Will scan plugins now...");
fRunnerData.handle = carla_plugin_discovery_start(fPlugin->fDiscoveryTool,
fPluginType,
IldaeilBasePlugin::getPluginPath(fPluginType),
_binaryPluginSearchCallback, this);
}
d_stdout("Will scan plugins now...");
fRunnerData.handle = carla_plugin_discovery_start(fPlugin->fDiscoveryTool,
fPluginType,
IldaeilBasePlugin::getPluginPath(fPluginType),
_binaryPluginSearchCallback,
_binaryPluginCheckCacheCallback,
this);


if (fDrawingState == kDrawingLoading) if (fDrawingState == kDrawingLoading)
{ {
@@ -816,37 +816,74 @@ protected:
if (fRunnerData.handle == nullptr) if (fRunnerData.handle == nullptr)
{ {
d_stdout("Nothing found!"); d_stdout("Nothing found!");
fPluginScanningFinished = true;
return false; return false;
} }
} }


DISTRHO_SAFE_ASSERT_RETURN(!fPluginScanningFinished, false);
DISTRHO_SAFE_ASSERT_RETURN(fRunnerData.handle != nullptr, false); DISTRHO_SAFE_ASSERT_RETURN(fRunnerData.handle != nullptr, false);


bool ok;
{
const MutexLocker cml(fPlugin->sPluginInfoLoadMutex);
ok = carla_plugin_discovery_idle(fRunnerData.handle);
}

if (ok)
if (carla_plugin_discovery_idle(fRunnerData.handle))
return true; return true;


// stop here // stop here
{
const MutexLocker cml(fPlugin->sPluginInfoLoadMutex);
d_stdout("Found %lu plugins!", (ulong)fPlugins.size());
carla_plugin_discovery_stop(fRunnerData.handle);
fRunnerData.handle = nullptr;
}
d_stdout("Found %lu plugins!", (ulong)fPlugins.size());
carla_plugin_discovery_stop(fRunnerData.handle);
fRunnerData.handle = nullptr;


fPluginScanningFinished = true;
return false; return false;
} }


void binaryPluginSearchCallback(const CarlaPluginDiscoveryInfo* const info)
void binaryPluginSearchCallback(const CarlaPluginDiscoveryInfo* const info, const char* const sha1sum)
{ {
// save plugin info into cache
if (sha1sum != nullptr)
{
const water::String configDir(ildaeilConfigDir());
const water::File cacheFile(configDir + CARLA_OS_SEP_STR "cache" CARLA_OS_SEP_STR + sha1sum);

if (cacheFile.create().ok())
{
water::FileOutputStream stream(cacheFile);

if (stream.openedOk())
{
if (info != nullptr)
{
stream.writeString(getBinaryTypeAsString(info->btype));
stream.writeString(getPluginTypeAsString(info->ptype));
stream.writeString(info->filename);
stream.writeString(info->label);
stream.writeInt64(info->uniqueId);
stream.writeString(info->metadata.name);
stream.writeString(info->metadata.maker);
stream.writeString(getPluginCategoryAsString(info->metadata.category));
stream.writeInt(info->metadata.hints);
stream.writeCompressedInt(info->io.audioIns);
stream.writeCompressedInt(info->io.audioOuts);
stream.writeCompressedInt(info->io.cvIns);
stream.writeCompressedInt(info->io.cvOuts);
stream.writeCompressedInt(info->io.midiIns);
stream.writeCompressedInt(info->io.midiOuts);
stream.writeCompressedInt(info->io.parameterIns);
stream.writeCompressedInt(info->io.parameterOuts);
}
}
else
{
d_stderr("Failed to write cache file for %s%s%s",
ildaeilConfigDir(), CARLA_OS_SEP_STR "cache" CARLA_OS_SEP_STR, sha1sum);
}
}
else
{
d_stderr("Failed to write cache file directories for %s%s%s",
ildaeilConfigDir(), CARLA_OS_SEP_STR "cache" CARLA_OS_SEP_STR, sha1sum);
}
}

if (info == nullptr)
return;

if (info->io.cvIns != 0 || info->io.cvOuts != 0) if (info->io.cvIns != 0 || info->io.cvOuts != 0)
return; return;
if (info->io.midiIns != 0 && info->io.midiIns != 1) if (info->io.midiIns != 0 && info->io.midiIns != 1)
@@ -911,9 +948,83 @@ protected:
fPlugins.push_back(pinfo); fPlugins.push_back(pinfo);
} }


static void _binaryPluginSearchCallback(void* ptr, const CarlaPluginDiscoveryInfo* info)
static void _binaryPluginSearchCallback(void* const ptr,
const CarlaPluginDiscoveryInfo* const info,
const char* const sha1sum)
{
static_cast<IldaeilUI*>(ptr)->binaryPluginSearchCallback(info, sha1sum);
}

bool binaryPluginCheckCacheCallback(const char* const filename, const char* const sha1sum)
{
if (sha1sum == nullptr)
return false;

const water::String configDir(ildaeilConfigDir());
const water::File cacheFile(configDir + CARLA_OS_SEP_STR "cache" CARLA_OS_SEP_STR + sha1sum);

if (cacheFile.existsAsFile())
{
water::FileInputStream stream(cacheFile);

if (stream.openedOk())
{
while (! stream.isExhausted())
{
CarlaPluginDiscoveryInfo info = {};

// read back everything the same way and order as we wrote it
info.btype = getBinaryTypeFromString(stream.readString().toRawUTF8());
info.ptype = getPluginTypeFromString(stream.readString().toRawUTF8());
const water::String pfilename(stream.readString());
const water::String label(stream.readString());
info.uniqueId = stream.readInt64();
const water::String name(stream.readString());
const water::String maker(stream.readString());
info.metadata.category = getPluginCategoryFromString(stream.readString().toRawUTF8());
info.metadata.hints = stream.readInt();
info.io.audioIns = stream.readCompressedInt();
info.io.audioOuts = stream.readCompressedInt();
info.io.cvIns = stream.readCompressedInt();
info.io.cvOuts = stream.readCompressedInt();
info.io.midiIns = stream.readCompressedInt();
info.io.midiOuts = stream.readCompressedInt();
info.io.parameterIns = stream.readCompressedInt();
info.io.parameterOuts = stream.readCompressedInt();

// string stuff
info.filename = pfilename.toRawUTF8();
info.label = label.toRawUTF8();
info.metadata.name = name.toRawUTF8();
info.metadata.maker = maker.toRawUTF8();

// check sha1 collisions
if (pfilename != filename)
{
d_stderr("Cache hash collision for %s: \"%s\" vs \"%s\"",
sha1sum, pfilename.toRawUTF8(), filename);
return false;
}

// purposefully not passing sha1sum, to not override cache file
binaryPluginSearchCallback(&info, nullptr);
}

return true;
}
else
{
d_stderr("Failed to read cache file for %s%s%s",
ildaeilConfigDir(), CARLA_OS_SEP_STR "cache" CARLA_OS_SEP_STR, sha1sum);
}
}

return false;
}

static bool _binaryPluginCheckCacheCallback(void* const ptr, const char* const filename, const char* const sha1)
{ {
static_cast<IldaeilUI*>(ptr)->binaryPluginSearchCallback(info);
return static_cast<IldaeilUI*>(ptr)->binaryPluginCheckCacheCallback(filename, sha1);
} }


void onImGuiDisplay() override void onImGuiDisplay() override


Loading…
Cancel
Save