Browse Source

Initial (incomplete) version of Carla LV2 plugin

tags/1.9.4
falkTX 11 years ago
parent
commit
aee9efac64
8 changed files with 509 additions and 118 deletions
  1. +5
    -0
      Makefile
  2. +53
    -0
      data/lv2/carla.ttl
  3. +12
    -0
      data/lv2/manifest.ttl
  4. +6
    -0
      resources/ui/carla.ui
  5. +251
    -21
      source/backend/engine/CarlaEngineNative.cpp
  6. +121
    -91
      source/backend/engine/CarlaEnginePlugin.cpp
  7. +1
    -1
      source/backend/engine/CarlaEngineRtAudio.cpp
  8. +60
    -5
      source/carla.py

+ 5
- 0
Makefile View File

@@ -134,6 +134,7 @@ install:
install -d $(DESTDIR)$(PREFIX)/lib/carla/resources/
install -d $(DESTDIR)$(PREFIX)/lib/carla/resources/nekofilter/
install -d $(DESTDIR)$(PREFIX)/lib/carla/resources/zynaddsubfx/
install -d $(DESTDIR)$(PREFIX)/lib/lv2/carla.lv2/
install -d $(DESTDIR)$(PREFIX)/share/applications/
install -d $(DESTDIR)$(PREFIX)/share/icons/hicolor/16x16/apps/
install -d $(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps/
@@ -182,6 +183,10 @@ install:
# Install python code
install -m 755 source/*.py $(DESTDIR)$(PREFIX)/share/carla/

# Install LV2 plugin
install -m 644 data/lv2/*.ttl $(DESTDIR)$(PREFIX)/lib/lv2/carla.lv2/
$(LINK) $(PREFIX)/lib/carla/libcarla_standalone.so $(DESTDIR)$(PREFIX)/lib/lv2/carla.lv2/carla.so

# Install resources
install -m 644 source/backend/resources/nekofilter-ui $(DESTDIR)$(PREFIX)/lib/carla/resources/
install -m 644 source/backend/resources/nekofilter/*.png $(DESTDIR)$(PREFIX)/lib/carla/resources/nekofilter/


+ 53
- 0
data/lv2/carla.ttl View File

@@ -0,0 +1,53 @@
@prefix atom: <http://lv2plug.in/ns/ext/atom#> .
@prefix doap: <http://usefulinc.com/ns/doap#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .

<http://kxstudio.sf.net/carla>
a lv2:Plugin ;
lv2:requiredFeature <http://lv2plug.in/ns/ext/buf-size#boundedBlockLength> ,
<http://lv2plug.in/ns/ext/urid#map> ;
lv2:extensionData <http://lv2plug.in/ns/ext/options#interface> ,
<http://lv2plug.in/ns/ext/state#interface> ;

# ui:ui <http://kxstudio.sf.net/carla#ui> ;

lv2:port [
a lv2:InputPort, lv2:AudioPort ;
lv2:index 0 ;
lv2:symbol "audio_in_1" ;
lv2:name "Audio Input 1" ;
] ,
[
a lv2:InputPort, lv2:AudioPort ;
lv2:index 1 ;
lv2:symbol "audio_in_2" ;
lv2:name "Audio Input 2" ;
] ,
[
a lv2:OutputPort, lv2:AudioPort ;
lv2:index 2 ;
lv2:symbol "audio_out_1" ;
lv2:name "Audio Output 1" ;
] ,
[
a lv2:OutputPort, lv2:AudioPort ;
lv2:index 3 ;
lv2:symbol "audio_out_2" ;
lv2:name "Audio Output 2" ;
] ,
[
a lv2:InputPort, atom:AtomPort ;
atom:bufferType atom:Sequence ;
atom:supports <http://lv2plug.in/ns/ext/midi#MidiEvent> ,
<http://lv2plug.in/ns/ext/time#Position> ;
lv2:index 4 ;
lv2:symbol "events_in" ;
lv2:name "Events Input" ;
lv2:designation lv2:control ;
] ;

doap:name "Carla Plugin" ;
doap:maintainer [ foaf:name "falkTX" ] .

+ 12
- 0
data/lv2/manifest.ttl View File

@@ -0,0 +1,12 @@
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix ui: <http://lv2plug.in/ns/extensions/ui#> .

<http://kxstudio.sf.net/carla>
a lv2:Plugin ;
lv2:binary <carla.so> ;
rdfs:seeAlso <carla.ttl> .

<http://kxstudio.sf.net/carla#ui>
a <http://kxstudio.sf.net/ns/lv2ext/external-ui#Widget> ;
ui:binary <carla.so> .

+ 6
- 0
resources/ui/carla.ui View File

@@ -293,6 +293,7 @@
<addaction name="act_file_open"/>
<addaction name="act_file_save"/>
<addaction name="act_file_save_as"/>
<addaction name="act_file_export_lv2"/>
<addaction name="separator"/>
<addaction name="act_file_quit"/>
</widget>
@@ -738,6 +739,11 @@
<string>Add New Plugin</string>
</property>
</action>
<action name="act_file_export_lv2">
<property name="text">
<string>Export LV2 Plugin State...</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>


+ 251
- 21
source/backend/engine/CarlaEngineNative.cpp View File

@@ -17,11 +17,33 @@

#ifndef BUILD_BRIDGE

#define WANT_LV2

#include "CarlaEngineInternal.hpp"
#include "CarlaStateUtils.hpp"

#include "CarlaNative.hpp"

#ifdef WANT_LV2
# include "lv2/lv2.h"
# include "lv2/atom.h"
struct Lv2HostDescriptor {
uint32_t bufferSize;
double sampleRate;
TimeInfo timeInfo;
float* audioPorts[4];
LV2_Atom_Sequence* atomPort;

Lv2HostDescriptor()
: bufferSize(0),
sampleRate(0.0),
audioPorts{nullptr},
atomPort(nullptr) {}
};
#else
struct Lv2HostDescriptor;
#endif

#include <QtCore/QTextStream>

CARLA_BACKEND_START_NAMESPACE
@@ -32,9 +54,10 @@ class CarlaEngineNative : public PluginDescriptorClass,
public CarlaEngine
{
public:
CarlaEngineNative(const HostDescriptor* const host)
CarlaEngineNative(const HostDescriptor* const host, Lv2HostDescriptor* const lv2Host = nullptr)
: PluginDescriptorClass(host),
CarlaEngine()
CarlaEngine(),
fLv2Host(lv2Host)
{
carla_debug("CarlaEngineNative::CarlaEngineNative()");

@@ -54,6 +77,14 @@ public:
setAboutToClose();
removeAllPlugins();
close();

#ifdef WANT_LV2
if (fLv2Host != nullptr)
{
delete fLv2Host;
delete hostHandle();
}
#endif
}

protected:
@@ -74,9 +105,9 @@ protected:
bool close() override
{
carla_debug("CarlaEngineNative::close()");
CarlaEngine::close();

return true;
proccessPendingEvents();
return CarlaEngine::close();
}

bool isRunning() const override
@@ -308,10 +339,20 @@ protected:

plugin->setActive(false, true, false);
}

// just in case
proccessPendingEvents();
}

void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const uint32_t midiEventCount, const MidiEvent* const midiEvents) override
void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const uint32_t midiEventCount, const ::MidiEvent* const midiEvents) override
{
if (kData->curPluginCount == 0)
{
carla_zeroFloat(outBuffer[0], frames);
carla_zeroFloat(outBuffer[1], frames);
return CarlaEngine::proccessPendingEvents();
}

// ---------------------------------------------------------------
// Time Info

@@ -339,28 +380,77 @@ protected:
}

// ---------------------------------------------------------------
// Initial checks
// initialize input events

if (kData->curPluginCount == 0)
carla_zeroStruct<EngineEvent>(kData->rack.in, RACK_EVENT_COUNT);
{
carla_zeroFloat(outBuffer[0], frames);
carla_zeroFloat(outBuffer[1], frames);
return CarlaEngine::proccessPendingEvents();
uint32_t engineEventIndex = 0;

for (uint32_t i=0; i < midiEventCount && engineEventIndex < RACK_EVENT_COUNT; ++i)
{
const ::MidiEvent& midiEvent(midiEvents[i]);

if (midiEvent.size > 4)
continue;

const uint8_t status = MIDI_GET_STATUS_FROM_DATA(midiEvent.data);
const uint8_t channel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent.data);

// we don't want some events
if (status == MIDI_STATUS_PROGRAM_CHANGE)
continue;

// handle note/sound off properly
if (status == MIDI_STATUS_CONTROL_CHANGE)
{
const uint8_t control = midiEvent.data[1];

if (MIDI_IS_CONTROL_BANK_SELECT(control))
continue;

if (control == MIDI_CONTROL_ALL_SOUND_OFF || control == MIDI_CONTROL_ALL_NOTES_OFF)
{
EngineEvent& engineEvent(kData->rack.in[engineEventIndex++]);
engineEvent.clear();

engineEvent.type = kEngineEventTypeControl;
engineEvent.time = midiEvent.time;
engineEvent.channel = channel;

engineEvent.ctrl.type = (control == MIDI_CONTROL_ALL_SOUND_OFF) ? kEngineControlEventTypeAllSoundOff : kEngineControlEventTypeAllNotesOff;
engineEvent.ctrl.param = 0;
engineEvent.ctrl.value = 0.0f;

continue;
}
}

EngineEvent& engineEvent(kData->rack.in[engineEventIndex++]);
engineEvent.clear();

engineEvent.type = kEngineEventTypeMidi;
engineEvent.time = midiEvent.time;
engineEvent.channel = channel;

engineEvent.midi.data[0] = MIDI_GET_STATUS_FROM_DATA(midiEvent.data);
engineEvent.midi.data[1] = midiEvent.data[1];
engineEvent.midi.data[2] = midiEvent.data[2];
engineEvent.midi.data[3] = midiEvent.data[3];
engineEvent.midi.size = midiEvent.size;
}
}

// ---------------------------------------------------------------
// create audio buffers

float* inBuf[2] = { inBuffer[0], inBuffer[1] };
float* outBuf[2] = { outBuffer[0], outBuffer[1] };

// ---------------------------------------------------------------
// process
CarlaEngine::processRack(inBuf, outBuf, frames);

CarlaEngine::processRack(inBuf, outBuf, frames);
CarlaEngine::proccessPendingEvents();
return;

// unused
(void)midiEventCount;
(void)midiEvents;
}

// -------------------------------------------------------------------
@@ -383,13 +473,15 @@ protected:

void uiSetParameterValue(const uint32_t index, const float value) override
{
CARLA_ASSERT(index < getParameterCount());
return;
if (index >= getParameterCount())
return;

// TODO
CarlaPlugin* const plugin(kData->plugins[0].plugin);

// unused
(void)value;
if (plugin == nullptr || ! plugin->enabled())
return;

plugin->uiParameterChange(index, value);
}

void uiSetMidiProgram(const uint8_t channel, const uint32_t bank, const uint32_t program) override
@@ -501,6 +593,120 @@ protected:
}
}

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

#ifdef WANT_LV2
Lv2HostDescriptor* const fLv2Host;

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

public:
#define handlePtr ((Lv2HostDescriptor*)handle)

static uint32_t lv2host_get_buffer_size(HostHandle handle)
{
return handlePtr->bufferSize;
}

static double lv2host_get_sample_rate(HostHandle handle)
{
return handlePtr->sampleRate;
}

static const TimeInfo* lv2host_get_time_info(HostHandle handle)
{
return &handlePtr->timeInfo;
}

static bool lv2host_write_midi_event(HostHandle, const MidiEvent*)
{
// MIDI Out not supported yet
return false;
}

static void lv2host_ui_parameter_changed(HostHandle, uint32_t, float) {}
static void lv2host_ui_midi_program_changed(HostHandle, uint8_t, uint32_t, uint32_t) {}
static void lv2host_ui_custom_data_changed(HostHandle, const char*, const char*) {}
static void lv2host_ui_closed(HostHandle) {}
static const char* lv2host_ui_open_file(HostHandle, bool, const char*, const char*) { return nullptr; }
static const char* lv2host_ui_save_file(HostHandle, bool, const char*, const char*) { return nullptr; }
static intptr_t lv2host_dispatcher(HostHandle, HostDispatcherOpcode, int32_t, intptr_t, void*) { return 0; }

#undef handlePtr

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

#define handlePtr ((CarlaEngineNative*)handle)

static LV2_Handle lv2_instantiate(const LV2_Descriptor*, double sampleRate, const char*, const LV2_Feature* const* /*features*/)
{
Lv2HostDescriptor* const lv2Host(new Lv2HostDescriptor());
lv2Host->bufferSize = 1024; // TODO
lv2Host->sampleRate = sampleRate;
lv2Host->timeInfo.frame = 0;
lv2Host->timeInfo.usecs = 0;
lv2Host->timeInfo.playing = false;
lv2Host->timeInfo.bbt.valid = false;

HostDescriptor* const host(new HostDescriptor);
host->handle = lv2Host;
host->ui_name = nullptr;

host->get_buffer_size = lv2host_get_buffer_size;
host->get_sample_rate = lv2host_get_sample_rate;
host->get_time_info = lv2host_get_time_info;
host->write_midi_event = lv2host_write_midi_event;
host->ui_parameter_changed = lv2host_ui_parameter_changed;
host->ui_custom_data_changed = lv2host_ui_custom_data_changed;
host->ui_closed = lv2host_ui_closed;
host->ui_open_file = lv2host_ui_open_file;
host->ui_save_file = lv2host_ui_save_file;
host->dispatcher = lv2host_dispatcher;

return new CarlaEngineNative(host, lv2Host);
}

static void lv2_connect_port(LV2_Handle handle, uint32_t port, void* dataLocation)
{
if (port < 4)
handlePtr->fLv2Host->audioPorts[port] = (float*)dataLocation;
else if (port == 4)
handlePtr->fLv2Host->atomPort = (LV2_Atom_Sequence*)dataLocation;
}

static void lv2_activate(LV2_Handle handle)
{
handlePtr->activate();
}

static void lv2_run(LV2_Handle handle, uint32_t sampleCount)
{
float* inBuffer[2] = { handlePtr->fLv2Host->audioPorts[0], handlePtr->fLv2Host->audioPorts[1] };
float* outBuffer[2] = { handlePtr->fLv2Host->audioPorts[2], handlePtr->fLv2Host->audioPorts[3] };

// TODO - get midiEvents and timePos from atomPort

handlePtr->process(inBuffer, outBuffer, sampleCount, 0, nullptr);
}

static void lv2_deactivate(LV2_Handle handle)
{
handlePtr->deactivate();
}

static void lv2_cleanup(LV2_Handle handle)
{
delete handlePtr;
}

static const void* lv2_extension_data(const char* /*uri*/)
{
return nullptr;
}

#undef handlePtr
#endif

private:
PluginDescriptorClassEND(CarlaEngineNative)
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineNative)
@@ -524,6 +730,19 @@ static const PluginDescriptor carlaDesc = {
PluginDescriptorFILL(CarlaEngineNative)
};

#ifdef WANT_LV2
static const LV2_Descriptor carlaLv2Desc = {
/* URI */ "http://kxstudio.sf.net/carla",
/* instantiate */ CarlaEngineNative::lv2_instantiate,
/* connect_port */ CarlaEngineNative::lv2_connect_port,
/* activate */ CarlaEngineNative::lv2_activate,
/* run */ CarlaEngineNative::lv2_run,
/* deactivate */ CarlaEngineNative::lv2_deactivate,
/* cleanup */ CarlaEngineNative::lv2_cleanup,
/* extension_data */ CarlaEngineNative::lv2_extension_data
};
#endif

void CarlaEngine::registerNativePlugin()
{
carla_register_native_plugin(&carlaDesc);
@@ -533,4 +752,15 @@ CARLA_BACKEND_END_NAMESPACE

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

#ifdef WANT_LV2
CARLA_EXPORT
const LV2_Descriptor* lv2_descriptor(uint32_t index)
{
CARLA_BACKEND_USE_NAMESPACE;
return (index == 0) ? &carlaLv2Desc : nullptr;
}
#endif

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

#endif // ! BUILD_BRIDGE

+ 121
- 91
source/backend/engine/CarlaEnginePlugin.cpp View File

@@ -18,8 +18,7 @@
#ifdef WANT_PLUGIN

#include "CarlaEngineInternal.hpp"
#include "CarlaBackendUtils.hpp"
#include "CarlaMIDI.h"
#include "CarlaStateUtils.hpp"

#include "DistrhoPlugin.hpp"

@@ -31,47 +30,47 @@ CARLA_BACKEND_START_NAMESPACE
// -----------------------------------------
// Parameters

static const unsigned char paramMap[] = {
static const unsigned char kParamMap[] = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F
};

static const unsigned int paramVolume = 5;
static const unsigned int paramBalance = 6;
static const unsigned int paramPan = 8;
static const unsigned int kParamVolume = 5;
static const unsigned int kParamBalance = 6;
static const unsigned int kParamPan = 8;

static const unsigned int paramCount = sizeof(paramMap);
static const unsigned int programCount = 128;
static const unsigned int stateCount = MAX_RACK_PLUGINS;
static const unsigned int kParamCount = sizeof(paramMap);
static const unsigned int kProgramCount = 128;
static const unsigned int kStateCount = MAX_RACK_PLUGINS;

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

class CarlaEnginePlugin : public CarlaEngine,
public DISTRHO::Plugin
class CarlaEnginePlugin : public DISTRHO::Plugin,
public CarlaEngine
{
public:
CarlaEnginePlugin()
: CarlaEngine(),
DISTRHO::Plugin(paramCount, programCount, stateCount),
paramBuffers{0.0f},
prevParamBuffers{0.0f}
: DISTRHO::Plugin(kParamCount, kProgramCount, kStateCount),
CarlaEngine(),
fParamBuffers{0.0f},
fPrevParamBuffers{0.0f}
{
carla_debug("CarlaEnginePlugin::CarlaEnginePlugin()");

// init parameters
paramBuffers[paramVolume] = 100.0f;
paramBuffers[paramBalance] = 63.5f;
paramBuffers[paramPan] = 63.5f;
fParamBuffers[kParamVolume] = 100.0f;
fParamBuffers[kParamBalance] = 63.5f;
fParamBuffers[kParamPan] = 63.5f;

prevParamBuffers[paramVolume] = 100.0f;
prevParamBuffers[paramBalance] = 63.5f;
prevParamBuffers[paramPan] = 63.5f;
fPrevParamBuffers[kParamVolume] = 100.0f;
fPrevParamBuffers[kParamBalance] = 63.5f;
fPrevParamBuffers[kParamPan] = 63.5f;

// set-up engine
fOptions.processMode = PROCESS_MODE_CONTINUOUS_RACK;
fOptions.transportMode = TRANSPORT_MODE_INTERNAL;
fOptions.transportMode = TRANSPORT_MODE_PLUGIN;
fOptions.forceStereo = true;
fOptions.preferPluginBridges = false;
fOptions.preferUiBridges = false;
@@ -89,7 +88,7 @@ public:

protected:
// -------------------------------------
// CarlaEngine virtual calls
// Carla Engine virtual calls

bool init(const char* const clientName) override
{
@@ -105,9 +104,9 @@ protected:
bool close() override
{
carla_debug("CarlaEnginePlugin::close()");
CarlaEngine::close();

return true;
proccessPendingEvents();
return CarlaEngine::close();
}

bool isRunning() const override
@@ -158,7 +157,7 @@ protected:

void d_initParameter(uint32_t index, DISTRHO::Parameter& parameter) override
{
if (index >= paramCount)
if (index >= kParamCount)
return;

parameter.hints = DISTRHO::PARAMETER_IS_AUTOMABLE;
@@ -166,14 +165,7 @@ protected:
parameter.ranges.min = 0.0f;
parameter.ranges.max = 127.0f;

if (index == paramVolume)
parameter.ranges.def = 100.0f;
else if (index == paramBalance)
parameter.ranges.def = 63.5f;
else if (index == paramPan)
parameter.ranges.def = 63.5f;

switch (paramMap[index])
switch (kParamMap[index])
{
case 0x01:
parameter.name = "0x01 Modulation";
@@ -192,15 +184,18 @@ protected:
break;
case 0x07:
parameter.name = "0x07 Volume";
parameter.ranges.def = 100.0f;
break;
case 0x08:
parameter.name = "0x08 Balance";
parameter.ranges.def = 63.5f;
break;
case 0x09:
parameter.name = "0x09 (Undefined)";
break;
case 0x0A:
parameter.name = "0x0A Pan";
parameter.ranges.def = 63.5f;
break;
case 0x0B:
parameter.name = "0x0B Expression";
@@ -325,6 +320,9 @@ protected:
case 0x5F:
parameter.name = "0x5F FX 5 Depth [Phaser]";
break;
default:
parameter.name = "";
break;
}
}

@@ -343,39 +341,41 @@ protected:

float d_parameterValue(uint32_t index) override
{
if (index >= paramCount)
if (index >= kParamCount)
return 0.0f;

return paramBuffers[index];
return fParamBuffers[index];
}

void d_setParameterValue(uint32_t index, float value) override
{
if (index >= paramCount)
if (index >= kParamCount)
return;

paramBuffers[index] = value;
fParamBuffers[index] = value;
}

void d_setProgram(uint32_t index) override
{
if (index >= programCount)
if (index >= kProgramCount)
return;
if (kData->curPluginCount == 0)
if (kData->curPluginCount == 0 || kData->plugins == nullptr)
return;

if (CarlaPlugin* const plugin = getPlugin(0))
CarlaPlugin* const plugin(kData->plugins[0].plugin);

if (plugin == nullptr || ! plugin->enabled())
return;

if (plugin->midiProgramCount() > 0)
{
if (plugin->programCount() > 0)
{
if (index <= plugin->programCount())
plugin->setProgram(index, true, true, false);
}
else if (plugin->midiProgramCount())
{
if (index <= plugin->midiProgramCount())
plugin->setMidiProgram(index, true, true, false);
}
if (index <= plugin->midiProgramCount())
plugin->setMidiProgram(index, true, true, false);
}
else if (plugin->programCount() > 0 && plugin->type() != PLUGIN_LV2)
{
if (index <= plugin->programCount())
plugin->setProgram(index, true, true, false);
}
}

@@ -391,17 +391,6 @@ protected:

void d_activate() override
{
static bool firstTestInit = true;

if (firstTestInit)
{
firstTestInit = false;
if (! addPlugin(PLUGIN_INTERNAL, nullptr, nullptr, "zynaddsubfx"))
carla_stderr2("Plugin add zynaddsubfx failed");
if (! addPlugin(PLUGIN_INTERNAL, nullptr, nullptr, "PingPongPan"))
carla_stderr2("Plugin add Pan failed");
}

for (unsigned int i=0; i < kData->curPluginCount; ++i)
{
CarlaPlugin* const plugin(getPluginUnchecked(i));
@@ -410,7 +399,7 @@ protected:
plugin->setActive(true, true, false);
}

carla_copyFloat(prevParamBuffers, paramBuffers, paramCount);
carla_copyFloat(fPrevParamBuffers, fParamBuffers, kParamCount);
}

void d_deactivate() override
@@ -422,6 +411,9 @@ protected:
if (plugin != nullptr && plugin->enabled())
plugin->setActive(false, true, false);
}

// just in case
proccessPendingEvents();
}

void d_run(float** inputs, float** outputs, uint32_t frames, uint32_t midiEventCount, const DISTRHO::MidiEvent* midiEvents) override
@@ -433,59 +425,97 @@ protected:
return proccessPendingEvents();
}

// ---------------------------------------------------------------
// create audio buffers

float* inBuf[2] = { inputs[0], inputs[1] };
float* outBuf[2] = { outputs[0], outputs[1] };

// ---------------------------------------------------------------
// initialize input events

carla_zeroStruct<EngineEvent>(kData->rack.in, RACK_EVENT_COUNT);
{
uint32_t engineEventIndex = 0;

for (unsigned int i=0; i < paramCount && engineEventIndex+midiEventCount < RACK_EVENT_COUNT; ++i)
for (unsigned int i=0; i < kParamCount && engineEventIndex+midiEventCount < RACK_EVENT_COUNT; ++i)
{
if (paramBuffers[i] == prevParamBuffers[i])
if (fParamBuffers[i] == fPrevParamBuffers[i])
continue;

EngineEvent* const engineEvent = &kData->rack.in[engineEventIndex++];
engineEvent->clear();
EngineEvent& engineEvent(kData->rack.in[engineEventIndex++]);
engineEvent.clear();

engineEvent->type = kEngineEventTypeControl;
engineEvent->time = 0;
engineEvent->channel = 0;
engineEvent.type = kEngineEventTypeControl;
engineEvent.time = 0;
engineEvent.channel = 0;

engineEvent->ctrl.type = kEngineControlEventTypeParameter;
engineEvent->ctrl.param = paramMap[i];
engineEvent->ctrl.value = paramBuffers[i]/127.0f;
engineEvent.ctrl.type = kEngineControlEventTypeParameter;
engineEvent.ctrl.param = kParamMap[i];
engineEvent.ctrl.value = fParamBuffers[i]/127.0f;

prevParamBuffers[i] = paramBuffers[i];
fPrevParamBuffers[i] = fParamBuffers[i];
}

const DISTRHO::MidiEvent* midiEvent;

for (uint32_t i=0; i < midiEventCount && engineEventIndex < RACK_EVENT_COUNT; ++i)
{
midiEvent = &midiEvents[i];
const DISTRHO::MidiEvent& midiEvent(midiEvents[i]);

if (midiEvent.size > 4)
continue;

const uint8_t status = MIDI_GET_STATUS_FROM_DATA(midiEvent.buf);
const uint8_t channel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent.buf);

if (midiEvent->size > 4)
// we don't want some events
if (status == MIDI_STATUS_PROGRAM_CHANGE)
continue;

EngineEvent* const engineEvent = &kData->rack.in[engineEventIndex++];
engineEvent->clear();
// handle note/sound off properly
if (status == MIDI_STATUS_CONTROL_CHANGE)
{
const uint8_t control = midiEvent.buf[1];

if (MIDI_IS_CONTROL_BANK_SELECT(control))
continue;

if (control == MIDI_CONTROL_ALL_SOUND_OFF || control == MIDI_CONTROL_ALL_NOTES_OFF)
{
EngineEvent& engineEvent(kData->rack.in[engineEventIndex++]);
engineEvent.clear();

engineEvent.type = kEngineEventTypeControl;
engineEvent.time = midiEvent.frame;
engineEvent.channel = channel;

engineEvent->type = kEngineEventTypeMidi;
engineEvent->time = midiEvent->frame;
engineEvent->channel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent->buf);
engineEvent.ctrl.type = (control == MIDI_CONTROL_ALL_SOUND_OFF) ? kEngineControlEventTypeAllSoundOff : kEngineControlEventTypeAllNotesOff;
engineEvent.ctrl.param = 0;
engineEvent.ctrl.value = 0.0f;

engineEvent->midi.data[0] = MIDI_GET_STATUS_FROM_DATA(midiEvent->buf);
engineEvent->midi.data[1] = midiEvent->buf[1];
engineEvent->midi.data[2] = midiEvent->buf[2];
engineEvent->midi.data[3] = midiEvent->buf[3];
engineEvent->midi.size = midiEvent->size;
continue;
}
}

EngineEvent& engineEvent(kData->rack.in[engineEventIndex++]);
engineEvent.clear();

engineEvent.type = kEngineEventTypeMidi;
engineEvent.time = midiEvent.frame;
engineEvent.channel = channel;

engineEvent.midi.data[0] = MIDI_GET_STATUS_FROM_DATA(midiEvent.buf);
engineEvent.midi.data[1] = midiEvent.buf[1];
engineEvent.midi.data[2] = midiEvent.buf[2];
engineEvent.midi.data[3] = midiEvent.buf[3];
engineEvent.midi.size = midiEvent.size;
}
}

// ---------------------------------------------------------------
// process

processRack(inBuf, outBuf, frames);
proccessPendingEvents();
}

// ---------------------------------------------
@@ -512,8 +542,8 @@ protected:
// ---------------------------------------------

private:
float paramBuffers[paramCount];
float prevParamBuffers[paramCount];
float fParamBuffers[kParamCount];
float fPrevParamBuffers[kParamCount];
};

CARLA_BACKEND_END_NAMESPACE
@@ -533,4 +563,4 @@ END_NAMESPACE_DISTRHO

#include "DistrhoPluginMain.cpp"

#endif // CARLA_ENGINE_PLUGIN
#endif // WANT_PLUGIN

+ 1
- 1
source/backend/engine/CarlaEngineRtAudio.cpp View File

@@ -350,7 +350,7 @@ public:
fMidiInEvents.clear();
//fMidiOutEvents.clear();

return true;
return (! hasError);
}

bool isRunning() const override


+ 60
- 5
source/carla.py View File

@@ -761,6 +761,7 @@ class CarlaMainW(QMainWindow):
self.connect(self.ui.act_file_open, SIGNAL("triggered()"), SLOT("slot_fileOpen()"))
self.connect(self.ui.act_file_save, SIGNAL("triggered()"), SLOT("slot_fileSave()"))
self.connect(self.ui.act_file_save_as, SIGNAL("triggered()"), SLOT("slot_fileSaveAs()"))
self.connect(self.ui.act_file_export_lv2, SIGNAL("triggered()"), SLOT("slot_fileExportLv2Preset()"))

self.connect(self.ui.act_engine_start, SIGNAL("triggered()"), SLOT("slot_engineStart()"))
self.connect(self.ui.act_engine_stop, SIGNAL("triggered()"), SLOT("slot_engineStop()"))
@@ -1361,7 +1362,7 @@ class CarlaMainW(QMainWindow):
filenameTry = QFileDialog.getOpenFileName(self, self.tr("Open Carla Project File"), self.fSavedSettings["Main/DefaultProjectFolder"], filter=fileFilter)

if filenameTry:
# FIXME - show dialog to user
# FIXME - show dialog to user (remove all plugins?)
self.removeAllPlugins()
self.loadProject(filenameTry)

@@ -1373,16 +1374,70 @@ class CarlaMainW(QMainWindow):
fileFilter = self.tr("Carla Project File (*.carxp)")
filenameTry = QFileDialog.getSaveFileName(self, self.tr("Save Carla Project File"), self.fSavedSettings["Main/DefaultProjectFolder"], filter=fileFilter)

if filenameTry:
if not filenameTry.endswith(".carxp"):
filenameTry += ".carxp"
if not filenameTry:
return

self.saveProject(filenameTry)
if not filenameTry.endswith(".carxp"):
filenameTry += ".carxp"

self.saveProject(filenameTry)

@pyqtSlot()
def slot_fileSaveAs(self):
self.slot_fileSave(True)

@pyqtSlot()
def slot_fileExportLv2Preset(self):
fileFilter = self.tr("LV2 Preset (*.lv2)")
filenameTry = QFileDialog.getSaveFileName(self, self.tr("Save Carla Project File"), self.fSavedSettings["Main/DefaultProjectFolder"], filter=fileFilter)

if not filenameTry:
return

if not filenameTry.endswith(".lv2"):
filenameTry += ".lv2"

if os.path.exists(filenameTry) and not os.path.isdir(filenameTry):
# TODO - error
return

# Save current project to a tmp file, and read it
tmpFile = os.path.join(TMP, "carla-plugin-export.carxp")

if not Carla.host.save_project(tmpFile):
# TODO - error
return

tmpFileFd = open(tmpFile, "r")
presetContents = tmpFileFd.read()
tmpFileFd.close()
os.remove(tmpFile)

# Create LV2 Preset
os.mkdir(filenameTry)

manifestPath = os.path.join(filenameTry, "manifest.ttl")

manifestFd = open(manifestPath, "w")
manifestFd.write("""# LV2 Preset for the Carla LV2 Plugin
@prefix lv2: <http://lv2plug.in/ns/lv2core#> .
@prefix pset: <http://lv2plug.in/ns/ext/presets#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix state: <http://lv2plug.in/ns/ext/state#> .

<file://%s>
a pset:Preset ;
lv2:appliesTo <http://kxstudio.sf.net/carla> ;
rdfs:label "%s" ;
state:state [
<http://kxstudio.sf.net/ns/carla/string>
\"\"\"
%s
\"\"\"
] .
""" % (manifestPath, os.path.basename(filenameTry), presetContents))
manifestFd.close()

@pyqtSlot()
def slot_loadProjectLater(self):
self.fProjectLoading = True


Loading…
Cancel
Save