Browse Source

carla-single-lv2 starting to take shape

tags/1.9.8
falkTX 8 years ago
parent
commit
a46e37073e
4 changed files with 539 additions and 6 deletions
  1. +7
    -0
      source/modules/AppConfig.h
  2. +1
    -1
      source/modules/juce_audio_processors/processors/juce_AudioProcessor.h
  3. +16
    -0
      source/plugin/Makefile
  4. +515
    -5
      source/plugin/carla-lv2-single.cpp

+ 7
- 0
source/modules/AppConfig.h View File

@@ -296,6 +296,13 @@
*/
#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 0
/** Config: JUCE_ALLOW_STATIC_NULL_VARIABLES
If disabled, this will turn off dangerous static globals like String::empty, var::null, etc
which can cause nasty order-of-initialisation problems if they are referenced during static
constructor code.
*/
#define JUCE_ALLOW_STATIC_NULL_VARIABLES 0
// --------------------------------------------------------------------------------------------------------------------
// juce_data_structures


+ 1
- 1
source/modules/juce_audio_processors/processors/juce_AudioProcessor.h View File

@@ -1276,7 +1276,7 @@ public:
//==============================================================================
/** LV2 specific calls, saving/restore as string. */
virtual String getStateInformationString () { return String::empty; }
virtual String getStateInformationString () { return String(); }
virtual void setStateInformationString (const String&) {}
//==============================================================================


+ 16
- 0
source/plugin/Makefile View File

@@ -24,6 +24,22 @@ endif
BUILD_CXX_FLAGS += -I$(CWD)/backend -I$(CWD)/includes -I$(CWD)/utils -isystem $(CWD)/modules
BUILD_CXX_FLAGS += $(NATIVE_PLUGINS_FLAGS)

ifeq ($(HAVE_LIBLO),true)
BUILD_CXX_FLAGS += $(LIBLO_FLAGS)
endif

ifeq ($(HAVE_FLUIDSYNTH),true)
BUILD_CXX_FLAGS += $(FLUIDSYNTH_FLAGS)
endif

ifeq ($(HAVE_LINUXSAMPLER),true)
BUILD_CXX_FLAGS += $(LINUXSAMPLER_FLAGS)
endif

ifeq ($(HAVE_X11),true)
BUILD_CXX_FLAGS += $(X11_FLAGS)
endif

# ----------------------------------------------------------------------------------------------------------------------------
# Libs



+ 515
- 5
source/plugin/carla-lv2-single.cpp View File

@@ -15,25 +15,535 @@
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

// #define BUILD_BRIDGE

#include "engine/CarlaEngineInternal.hpp"
#include "CarlaPlugin.hpp"

#include "CarlaBackendUtils.hpp"
#include "CarlaEngineUtils.hpp"
#include "CarlaLv2Utils.hpp"
#include "CarlaUtils.h"

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

CARLA_BACKEND_START_NAMESPACE

class CarlaEngineLV2Single : public CarlaEngine
{
public:
CarlaEngineLV2Single(const uint32_t bufferSize, const double sampleRate, const char* const bundlePath, const LV2_URID_Map* uridMap)
: fPlugin(nullptr),
fIsRunning(false),
fIsOffline(false)
{
// xxxxx
CarlaString resourceDir(bundlePath);
resourceDir += CARLA_OS_SEP_STR "resources" CARLA_OS_SEP_STR;

pData->bufferSize = bufferSize;
pData->sampleRate = sampleRate;
pData->initTime(nullptr);

pData->options.processMode = ENGINE_PROCESS_MODE_BRIDGE;
pData->options.transportMode = ENGINE_TRANSPORT_MODE_PLUGIN;
pData->options.forceStereo = false;
pData->options.preferPluginBridges = false;
pData->options.preferUiBridges = false;
init("LV2-Export");

if (pData->options.resourceDir != nullptr)
delete[] pData->options.resourceDir;
if (pData->options.binaryDir != nullptr)
delete[] pData->options.binaryDir;

pData->options.resourceDir = resourceDir.dup();
pData->options.binaryDir = carla_strdup(carla_get_library_folder());

setCallback(_engine_callback, this);

if (! addPlugin(BINARY_NATIVE, PLUGIN_VST2, "/usr/lib/vst/3BandEQ-vst.so", nullptr, nullptr, 0, nullptr, 0x0))
{
carla_stderr2("Failed to init plugin, possible reasons: %s", getLastError());
return;
}

CARLA_SAFE_ASSERT_RETURN(pData->curPluginCount == 1,)

fPlugin = pData->plugins[0].plugin;
CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,);
CARLA_SAFE_ASSERT_RETURN(fPlugin->isEnabled(),);

fPorts.init(fPlugin);
}

~CarlaEngineLV2Single()
{
close();
}

bool hasPlugin()
{
return fPlugin != nullptr;
}

// -----------------------------------------------------------------------------------------------------------------
// LV2 functions

void lv2_connect_port(const uint32_t port, void* const dataLocation)
{
fPorts.connectPort(port, dataLocation);
}

void lv2_activate()
{
CARLA_SAFE_ASSERT_RETURN(! fIsRunning,);

fPlugin->activate();
fIsRunning = true;
}

void lv2_deactivate()
{
CARLA_SAFE_ASSERT_RETURN(fIsRunning,);

fIsRunning = false;
fPlugin->deactivate();
}

void lv2_run(const uint32_t frames)
{
fIsOffline = (fPorts.freewheel != nullptr && *fPorts.freewheel >= 0.5f);

// Check for updated parameters
float curValue;

for (uint32_t i=0; i < fPorts.numParams; ++i)
{
if (fPorts.paramsOut[i])
continue;

CARLA_SAFE_ASSERT_CONTINUE(fPorts.paramsPtr[i] != nullptr)

curValue = *fPorts.paramsPtr[i];

if (carla_isEqual(fPorts.paramsLast[i], curValue))
continue;

fPorts.paramsLast[i] = curValue;
fPlugin->setParameterValue(i, curValue, false, false, false);
}

if (frames == 0)
return fPorts.updateOutputs();

if (fPlugin->tryLock(fIsOffline))
{
fPlugin->initBuffers();
fPlugin->process(fPorts.audioIns, fPorts.audioOuts, nullptr, nullptr, frames);
fPlugin->unlock();
}
else
{
for (uint32_t i=0; i<fPorts.numAudioOuts; ++i)
carla_zeroFloats(fPorts.audioOuts[i], frames);
}

fPorts.updateOutputs();
}

protected:
// -----------------------------------------------------------------------------------------------------------------
// CarlaEngine virtual calls

bool init(const char* const clientName) override
{
carla_stdout("CarlaEngineNative::init(\"%s\")", clientName);

if (! pData->init(clientName))
{
close();
setLastError("Failed to init internal data");
return false;
}

return true;
}

bool close() override
{
fIsRunning = false;
CarlaEngine::close();
return true;
}

bool isRunning() const noexcept override
{
return fIsRunning;
}

bool isOffline() const noexcept override
{
return fIsOffline;
}

bool usesConstantBufferSize() const noexcept override
{
return true;
}

EngineType getType() const noexcept override
{
return kEngineTypePlugin;
}

const char* getCurrentDriverName() const noexcept override
{
return "LV2 Plugin";
}

void callback(const EngineCallbackOpcode action, const uint pluginId, const int value1, const int value2, const float value3, const char* const valueStr) noexcept override
{
CarlaEngine::callback(action, pluginId, value1, value2, value3, valueStr);

//if (action == ENGINE_CALLBACK_IDLE && ! pData->aboutToClose)
// pHost->dispatcher(pHost->handle, NATIVE_HOST_OPCODE_HOST_IDLE, 0, 0, nullptr, 0.0f);
}

void engineCallback(const EngineCallbackOpcode action, const uint pluginId, const int value1, const int value2, const float value3, const char* const valueStr)
{
carla_stdout("engineCallback(%i:%s, %u, %i, %i, %f, %s)", action, EngineCallbackOpcode2Str(action), pluginId, value1, value2, value3, valueStr);
}

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

private:
CarlaPlugin* fPlugin;
bool fIsRunning;

// Lv2 host data
bool fIsOffline;

struct Ports {
uint32_t numAudioIns;
uint32_t numAudioOuts;
uint32_t numParams;
const float** audioIns;
/* */ float** audioOuts;
float* freewheel;
float* paramsLast;
float** paramsPtr;
bool* paramsOut;
const CarlaPlugin* plugin;

Ports()
: numAudioIns(0),
numAudioOuts(0),
numParams(0),
audioIns(nullptr),
audioOuts(nullptr),
freewheel(nullptr),
paramsLast(nullptr),
paramsPtr(nullptr),
paramsOut(nullptr),
plugin(nullptr) {}

~Ports()
{
if (audioIns != nullptr)
{
delete[] audioIns;
audioIns = nullptr;
}

if (audioOuts != nullptr)
{
delete[] audioOuts;
audioOuts = nullptr;
}

if (paramsLast != nullptr)
{
delete[] paramsLast;
paramsLast = nullptr;
}

if (paramsPtr != nullptr)
{
delete[] paramsPtr;
paramsPtr = nullptr;
}

if (paramsOut != nullptr)
{
delete[] paramsOut;
paramsOut = nullptr;
}
}

void init(const CarlaPlugin* const pl)
{
plugin = pl;

if ((numAudioIns = plugin->getAudioInCount()) > 0)
{
audioIns = new const float*[numAudioIns];

for (uint32_t i=0; i < numAudioIns; ++i)
audioIns[i] = nullptr;
}

if ((numAudioOuts = plugin->getAudioOutCount()) > 0)
{
audioOuts = new float*[numAudioOuts];

for (uint32_t i=0; i < numAudioOuts; ++i)
audioOuts[i] = nullptr;
}

// -----------------------------------------------------------------------
if ((numParams = plugin->getParameterCount()) > 0)
{
paramsLast = new float[numParams];
paramsPtr = new float*[numParams];
paramsOut = new bool[numParams];

for (uint32_t i=0; i < numParams; ++i)
{
paramsLast[i] = plugin->getParameterValue(i);
paramsPtr [i] = nullptr;
paramsOut [i] = plugin->isParameterOutput(i);
}
}
}

void connectPort(const uint32_t port, void* const dataLocation) noexcept
{
uint32_t index = 0;

for (uint32_t i=0; i < numAudioIns; ++i)
{
if (port == index++)
{
audioIns[i] = (float*)dataLocation;
return;
}
}

for (uint32_t i=0; i < numAudioOuts; ++i)
{
if (port == index++)
{
audioOuts[i] = (float*)dataLocation;
return;
}
}

if (port == index++)
{
freewheel = (float*)dataLocation;
return;
}

for (uint32_t i=0; i < numParams; ++i)
{
if (port == index++)
{
paramsPtr[i] = (float*)dataLocation;
return;
}
}
}

void updateOutputs() noexcept
{
for (uint32_t i=0; i < numParams; ++i)
{
if (! paramsOut[i])
continue;

paramsLast[i] = plugin->getParameterValue(i);

if (paramsPtr[i] != nullptr)
*paramsPtr[i] = paramsLast[i];
}
}

CARLA_DECLARE_NON_COPY_STRUCT(Ports);

} fPorts;

#define handlePtr ((CarlaEngineLV2Single*)handle)

static void _engine_callback(void* handle, EngineCallbackOpcode action, uint pluginId, int value1, int value2, float value3, const char* valueStr)
{
handlePtr->engineCallback(action, pluginId, value1, value2, value3, valueStr);
}

#undef handlePtr

CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineLV2Single)
};

CARLA_BACKEND_END_NAMESPACE

// ---------------------------------------------------------------------------------------------------------------------
// LV2 plugin descriptor functions

CARLA_BACKEND_USE_NAMESPACE

static LV2_Handle lv2_instantiate(const LV2_Descriptor* lv2Descriptor, double sampleRate, const char* bundlePath, const LV2_Feature* const* features)
{
carla_stdout("lv2_instantiate(%p, %g, %s, %p)", lv2Descriptor, sampleRate, bundlePath, features);

const LV2_Options_Option* options = nullptr;
const LV2_URID_Map* uridMap = nullptr;
const LV2_URID_Unmap* uridUnmap = nullptr;

for (int i=0; features[i] != nullptr; ++i)
{
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_URID__unmap) == 0)
uridUnmap = (const LV2_URID_Unmap*)features[i]->data;
}

if (options == nullptr || uridMap == nullptr)
{
carla_stderr("Host doesn't provide option or urid-map features");
return nullptr;
}

uint32_t bufferSize = 0;

for (int i=0; options[i].key != 0; ++i)
{
if (uridUnmap != nullptr) {
carla_stdout("Host option %i:\"%s\"", i, uridUnmap->unmap(uridUnmap->handle, options[i].key));
}

if (options[i].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__nominalBlockLength))
{
if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Int))
{
const int value(*(const int*)options[i].value);
CARLA_SAFE_ASSERT_CONTINUE(value > 0);

bufferSize = static_cast<uint32_t>(value);
}
else
{
carla_stderr("Host provides nominalBlockLength but has wrong value type");
}
break;
}

if (options[i].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength))
{
if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Int))
{
const int value(*(const int*)options[i].value);
CARLA_SAFE_ASSERT_CONTINUE(value > 0);

bufferSize = static_cast<uint32_t>(value);
}
else
{
carla_stderr("Host provides maxBlockLength but has wrong value type");
}
// no break, continue in case host supports nominalBlockLength
}
}

if (bufferSize == 0)
{
carla_stderr("Host doesn't provide bufferSize feature");
return nullptr;
}

return new CarlaEngineLV2Single(bufferSize, sampleRate, bundlePath, uridMap);
}

#define instancePtr ((CarlaEngineLV2Single*)instance)

static void lv2_connect_port(LV2_Handle instance, uint32_t port, void* dataLocation)
{
instancePtr->lv2_connect_port(port, dataLocation);
}

static void lv2_activate(LV2_Handle instance)
{
carla_stdout("lv2_activate(%p)", instance);
instancePtr->lv2_activate();
}

static void lv2_run(LV2_Handle instance, uint32_t sampleCount)
{
instancePtr->lv2_run(sampleCount);
}

static void lv2_deactivate(LV2_Handle instance)
{
carla_stdout("lv2_deactivate(%p)", instance);
instancePtr->lv2_deactivate();
}

static void lv2_cleanup(LV2_Handle instance)
{
carla_stdout("lv2_cleanup(%p)", instance);
delete instancePtr;
}

static const void* lv2_extension_data(const char* uri)
{
carla_stdout("lv2_extension_data(\"%s\")", uri);

return nullptr;
}

#undef instancePtr

// ---------------------------------------------------------------------------------------------------------------------
// Startup code

CARLA_EXPORT
const LV2_Descriptor* lv2_descriptor(uint32_t index)
{
carla_debug("lv2_descriptor(%i)", index);
carla_stdout("lv2_descriptor(%i)", index);

return nullptr;
if (index != 0)
return nullptr;

static CarlaString ret;

if (ret.isEmpty())
{
using namespace juce;
const File file(File::getSpecialLocation(File::currentExecutableFile).withFileExtension("ttl"));
ret = String("file://" + file.getFullPathName()).toRawUTF8();
}

static const LV2_Descriptor desc = {
/* URI */ ret.buffer(),
/* instantiate */ lv2_instantiate,
/* connect_port */ lv2_connect_port,
/* activate */ lv2_activate,
/* run */ lv2_run,
/* deactivate */ lv2_deactivate,
/* cleanup */ lv2_cleanup,
/* extension_data */ lv2_extension_data
};

return &desc;
}

CARLA_EXPORT
const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
{
carla_debug("lv2ui_descriptor(%i)", index);
carla_stdout("lv2ui_descriptor(%i)", index);

return nullptr;
}

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

Loading…
Cancel
Save