| @@ -134,6 +134,7 @@ install: | |||||
| install -d $(DESTDIR)$(PREFIX)/lib/carla/resources/ | install -d $(DESTDIR)$(PREFIX)/lib/carla/resources/ | ||||
| install -d $(DESTDIR)$(PREFIX)/lib/carla/resources/nekofilter/ | install -d $(DESTDIR)$(PREFIX)/lib/carla/resources/nekofilter/ | ||||
| install -d $(DESTDIR)$(PREFIX)/lib/carla/resources/zynaddsubfx/ | 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/applications/ | ||||
| install -d $(DESTDIR)$(PREFIX)/share/icons/hicolor/16x16/apps/ | install -d $(DESTDIR)$(PREFIX)/share/icons/hicolor/16x16/apps/ | ||||
| install -d $(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps/ | install -d $(DESTDIR)$(PREFIX)/share/icons/hicolor/48x48/apps/ | ||||
| @@ -182,6 +183,10 @@ install: | |||||
| # Install python code | # Install python code | ||||
| install -m 755 source/*.py $(DESTDIR)$(PREFIX)/share/carla/ | 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 resources | ||||
| install -m 644 source/backend/resources/nekofilter-ui $(DESTDIR)$(PREFIX)/lib/carla/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/ | install -m 644 source/backend/resources/nekofilter/*.png $(DESTDIR)$(PREFIX)/lib/carla/resources/nekofilter/ | ||||
| @@ -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" ] . | |||||
| @@ -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> . | |||||
| @@ -293,6 +293,7 @@ | |||||
| <addaction name="act_file_open"/> | <addaction name="act_file_open"/> | ||||
| <addaction name="act_file_save"/> | <addaction name="act_file_save"/> | ||||
| <addaction name="act_file_save_as"/> | <addaction name="act_file_save_as"/> | ||||
| <addaction name="act_file_export_lv2"/> | |||||
| <addaction name="separator"/> | <addaction name="separator"/> | ||||
| <addaction name="act_file_quit"/> | <addaction name="act_file_quit"/> | ||||
| </widget> | </widget> | ||||
| @@ -738,6 +739,11 @@ | |||||
| <string>Add New Plugin</string> | <string>Add New Plugin</string> | ||||
| </property> | </property> | ||||
| </action> | </action> | ||||
| <action name="act_file_export_lv2"> | |||||
| <property name="text"> | |||||
| <string>Export LV2 Plugin State...</string> | |||||
| </property> | |||||
| </action> | |||||
| </widget> | </widget> | ||||
| <customwidgets> | <customwidgets> | ||||
| <customwidget> | <customwidget> | ||||
| @@ -17,11 +17,33 @@ | |||||
| #ifndef BUILD_BRIDGE | #ifndef BUILD_BRIDGE | ||||
| #define WANT_LV2 | |||||
| #include "CarlaEngineInternal.hpp" | #include "CarlaEngineInternal.hpp" | ||||
| #include "CarlaStateUtils.hpp" | #include "CarlaStateUtils.hpp" | ||||
| #include "CarlaNative.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> | #include <QtCore/QTextStream> | ||||
| CARLA_BACKEND_START_NAMESPACE | CARLA_BACKEND_START_NAMESPACE | ||||
| @@ -32,9 +54,10 @@ class CarlaEngineNative : public PluginDescriptorClass, | |||||
| public CarlaEngine | public CarlaEngine | ||||
| { | { | ||||
| public: | public: | ||||
| CarlaEngineNative(const HostDescriptor* const host) | |||||
| CarlaEngineNative(const HostDescriptor* const host, Lv2HostDescriptor* const lv2Host = nullptr) | |||||
| : PluginDescriptorClass(host), | : PluginDescriptorClass(host), | ||||
| CarlaEngine() | |||||
| CarlaEngine(), | |||||
| fLv2Host(lv2Host) | |||||
| { | { | ||||
| carla_debug("CarlaEngineNative::CarlaEngineNative()"); | carla_debug("CarlaEngineNative::CarlaEngineNative()"); | ||||
| @@ -54,6 +77,14 @@ public: | |||||
| setAboutToClose(); | setAboutToClose(); | ||||
| removeAllPlugins(); | removeAllPlugins(); | ||||
| close(); | close(); | ||||
| #ifdef WANT_LV2 | |||||
| if (fLv2Host != nullptr) | |||||
| { | |||||
| delete fLv2Host; | |||||
| delete hostHandle(); | |||||
| } | |||||
| #endif | |||||
| } | } | ||||
| protected: | protected: | ||||
| @@ -74,9 +105,9 @@ protected: | |||||
| bool close() override | bool close() override | ||||
| { | { | ||||
| carla_debug("CarlaEngineNative::close()"); | carla_debug("CarlaEngineNative::close()"); | ||||
| CarlaEngine::close(); | |||||
| return true; | |||||
| proccessPendingEvents(); | |||||
| return CarlaEngine::close(); | |||||
| } | } | ||||
| bool isRunning() const override | bool isRunning() const override | ||||
| @@ -308,10 +339,20 @@ protected: | |||||
| plugin->setActive(false, true, false); | 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 | // 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 | // create audio buffers | ||||
| float* inBuf[2] = { inBuffer[0], inBuffer[1] }; | float* inBuf[2] = { inBuffer[0], inBuffer[1] }; | ||||
| float* outBuf[2] = { outBuffer[0], outBuffer[1] }; | float* outBuf[2] = { outBuffer[0], outBuffer[1] }; | ||||
| // --------------------------------------------------------------- | |||||
| // process | // process | ||||
| CarlaEngine::processRack(inBuf, outBuf, frames); | |||||
| CarlaEngine::processRack(inBuf, outBuf, frames); | |||||
| CarlaEngine::proccessPendingEvents(); | CarlaEngine::proccessPendingEvents(); | ||||
| return; | |||||
| // unused | |||||
| (void)midiEventCount; | |||||
| (void)midiEvents; | |||||
| } | } | ||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| @@ -383,13 +473,15 @@ protected: | |||||
| void uiSetParameterValue(const uint32_t index, const float value) override | 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 | 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: | private: | ||||
| PluginDescriptorClassEND(CarlaEngineNative) | PluginDescriptorClassEND(CarlaEngineNative) | ||||
| CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineNative) | CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineNative) | ||||
| @@ -524,6 +730,19 @@ static const PluginDescriptor carlaDesc = { | |||||
| PluginDescriptorFILL(CarlaEngineNative) | 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() | void CarlaEngine::registerNativePlugin() | ||||
| { | { | ||||
| carla_register_native_plugin(&carlaDesc); | 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 | #endif // ! BUILD_BRIDGE | ||||
| @@ -18,8 +18,7 @@ | |||||
| #ifdef WANT_PLUGIN | #ifdef WANT_PLUGIN | ||||
| #include "CarlaEngineInternal.hpp" | #include "CarlaEngineInternal.hpp" | ||||
| #include "CarlaBackendUtils.hpp" | |||||
| #include "CarlaMIDI.h" | |||||
| #include "CarlaStateUtils.hpp" | |||||
| #include "DistrhoPlugin.hpp" | #include "DistrhoPlugin.hpp" | ||||
| @@ -31,47 +30,47 @@ CARLA_BACKEND_START_NAMESPACE | |||||
| // ----------------------------------------- | // ----------------------------------------- | ||||
| // Parameters | // 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, | 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, | 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, | 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, | ||||
| 0x50, 0x51, 0x52, 0x53, 0x54, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F | 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: | public: | ||||
| CarlaEnginePlugin() | 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()"); | carla_debug("CarlaEnginePlugin::CarlaEnginePlugin()"); | ||||
| // init parameters | // 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 | // set-up engine | ||||
| fOptions.processMode = PROCESS_MODE_CONTINUOUS_RACK; | fOptions.processMode = PROCESS_MODE_CONTINUOUS_RACK; | ||||
| fOptions.transportMode = TRANSPORT_MODE_INTERNAL; | |||||
| fOptions.transportMode = TRANSPORT_MODE_PLUGIN; | |||||
| fOptions.forceStereo = true; | fOptions.forceStereo = true; | ||||
| fOptions.preferPluginBridges = false; | fOptions.preferPluginBridges = false; | ||||
| fOptions.preferUiBridges = false; | fOptions.preferUiBridges = false; | ||||
| @@ -89,7 +88,7 @@ public: | |||||
| protected: | protected: | ||||
| // ------------------------------------- | // ------------------------------------- | ||||
| // CarlaEngine virtual calls | |||||
| // Carla Engine virtual calls | |||||
| bool init(const char* const clientName) override | bool init(const char* const clientName) override | ||||
| { | { | ||||
| @@ -105,9 +104,9 @@ protected: | |||||
| bool close() override | bool close() override | ||||
| { | { | ||||
| carla_debug("CarlaEnginePlugin::close()"); | carla_debug("CarlaEnginePlugin::close()"); | ||||
| CarlaEngine::close(); | |||||
| return true; | |||||
| proccessPendingEvents(); | |||||
| return CarlaEngine::close(); | |||||
| } | } | ||||
| bool isRunning() const override | bool isRunning() const override | ||||
| @@ -158,7 +157,7 @@ protected: | |||||
| void d_initParameter(uint32_t index, DISTRHO::Parameter& parameter) override | void d_initParameter(uint32_t index, DISTRHO::Parameter& parameter) override | ||||
| { | { | ||||
| if (index >= paramCount) | |||||
| if (index >= kParamCount) | |||||
| return; | return; | ||||
| parameter.hints = DISTRHO::PARAMETER_IS_AUTOMABLE; | parameter.hints = DISTRHO::PARAMETER_IS_AUTOMABLE; | ||||
| @@ -166,14 +165,7 @@ protected: | |||||
| parameter.ranges.min = 0.0f; | parameter.ranges.min = 0.0f; | ||||
| parameter.ranges.max = 127.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: | case 0x01: | ||||
| parameter.name = "0x01 Modulation"; | parameter.name = "0x01 Modulation"; | ||||
| @@ -192,15 +184,18 @@ protected: | |||||
| break; | break; | ||||
| case 0x07: | case 0x07: | ||||
| parameter.name = "0x07 Volume"; | parameter.name = "0x07 Volume"; | ||||
| parameter.ranges.def = 100.0f; | |||||
| break; | break; | ||||
| case 0x08: | case 0x08: | ||||
| parameter.name = "0x08 Balance"; | parameter.name = "0x08 Balance"; | ||||
| parameter.ranges.def = 63.5f; | |||||
| break; | break; | ||||
| case 0x09: | case 0x09: | ||||
| parameter.name = "0x09 (Undefined)"; | parameter.name = "0x09 (Undefined)"; | ||||
| break; | break; | ||||
| case 0x0A: | case 0x0A: | ||||
| parameter.name = "0x0A Pan"; | parameter.name = "0x0A Pan"; | ||||
| parameter.ranges.def = 63.5f; | |||||
| break; | break; | ||||
| case 0x0B: | case 0x0B: | ||||
| parameter.name = "0x0B Expression"; | parameter.name = "0x0B Expression"; | ||||
| @@ -325,6 +320,9 @@ protected: | |||||
| case 0x5F: | case 0x5F: | ||||
| parameter.name = "0x5F FX 5 Depth [Phaser]"; | parameter.name = "0x5F FX 5 Depth [Phaser]"; | ||||
| break; | break; | ||||
| default: | |||||
| parameter.name = ""; | |||||
| break; | |||||
| } | } | ||||
| } | } | ||||
| @@ -343,39 +341,41 @@ protected: | |||||
| float d_parameterValue(uint32_t index) override | float d_parameterValue(uint32_t index) override | ||||
| { | { | ||||
| if (index >= paramCount) | |||||
| if (index >= kParamCount) | |||||
| return 0.0f; | return 0.0f; | ||||
| return paramBuffers[index]; | |||||
| return fParamBuffers[index]; | |||||
| } | } | ||||
| void d_setParameterValue(uint32_t index, float value) override | void d_setParameterValue(uint32_t index, float value) override | ||||
| { | { | ||||
| if (index >= paramCount) | |||||
| if (index >= kParamCount) | |||||
| return; | return; | ||||
| paramBuffers[index] = value; | |||||
| fParamBuffers[index] = value; | |||||
| } | } | ||||
| void d_setProgram(uint32_t index) override | void d_setProgram(uint32_t index) override | ||||
| { | { | ||||
| if (index >= programCount) | |||||
| if (index >= kProgramCount) | |||||
| return; | return; | ||||
| if (kData->curPluginCount == 0) | |||||
| if (kData->curPluginCount == 0 || kData->plugins == nullptr) | |||||
| return; | 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 | 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) | for (unsigned int i=0; i < kData->curPluginCount; ++i) | ||||
| { | { | ||||
| CarlaPlugin* const plugin(getPluginUnchecked(i)); | CarlaPlugin* const plugin(getPluginUnchecked(i)); | ||||
| @@ -410,7 +399,7 @@ protected: | |||||
| plugin->setActive(true, true, false); | plugin->setActive(true, true, false); | ||||
| } | } | ||||
| carla_copyFloat(prevParamBuffers, paramBuffers, paramCount); | |||||
| carla_copyFloat(fPrevParamBuffers, fParamBuffers, kParamCount); | |||||
| } | } | ||||
| void d_deactivate() override | void d_deactivate() override | ||||
| @@ -422,6 +411,9 @@ protected: | |||||
| if (plugin != nullptr && plugin->enabled()) | if (plugin != nullptr && plugin->enabled()) | ||||
| plugin->setActive(false, true, false); | 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 | 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(); | return proccessPendingEvents(); | ||||
| } | } | ||||
| // --------------------------------------------------------------- | |||||
| // create audio buffers | // create audio buffers | ||||
| float* inBuf[2] = { inputs[0], inputs[1] }; | float* inBuf[2] = { inputs[0], inputs[1] }; | ||||
| float* outBuf[2] = { outputs[0], outputs[1] }; | float* outBuf[2] = { outputs[0], outputs[1] }; | ||||
| // --------------------------------------------------------------- | |||||
| // initialize input events | // initialize input events | ||||
| carla_zeroStruct<EngineEvent>(kData->rack.in, RACK_EVENT_COUNT); | carla_zeroStruct<EngineEvent>(kData->rack.in, RACK_EVENT_COUNT); | ||||
| { | { | ||||
| uint32_t engineEventIndex = 0; | 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; | 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) | 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; | 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); | processRack(inBuf, outBuf, frames); | ||||
| proccessPendingEvents(); | |||||
| } | } | ||||
| // --------------------------------------------- | // --------------------------------------------- | ||||
| @@ -512,8 +542,8 @@ protected: | |||||
| // --------------------------------------------- | // --------------------------------------------- | ||||
| private: | private: | ||||
| float paramBuffers[paramCount]; | |||||
| float prevParamBuffers[paramCount]; | |||||
| float fParamBuffers[kParamCount]; | |||||
| float fPrevParamBuffers[kParamCount]; | |||||
| }; | }; | ||||
| CARLA_BACKEND_END_NAMESPACE | CARLA_BACKEND_END_NAMESPACE | ||||
| @@ -533,4 +563,4 @@ END_NAMESPACE_DISTRHO | |||||
| #include "DistrhoPluginMain.cpp" | #include "DistrhoPluginMain.cpp" | ||||
| #endif // CARLA_ENGINE_PLUGIN | |||||
| #endif // WANT_PLUGIN | |||||
| @@ -350,7 +350,7 @@ public: | |||||
| fMidiInEvents.clear(); | fMidiInEvents.clear(); | ||||
| //fMidiOutEvents.clear(); | //fMidiOutEvents.clear(); | ||||
| return true; | |||||
| return (! hasError); | |||||
| } | } | ||||
| bool isRunning() const override | bool isRunning() const override | ||||
| @@ -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_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, SIGNAL("triggered()"), SLOT("slot_fileSave()")) | ||||
| self.connect(self.ui.act_file_save_as, SIGNAL("triggered()"), SLOT("slot_fileSaveAs()")) | 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_start, SIGNAL("triggered()"), SLOT("slot_engineStart()")) | ||||
| self.connect(self.ui.act_engine_stop, SIGNAL("triggered()"), SLOT("slot_engineStop()")) | 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) | filenameTry = QFileDialog.getOpenFileName(self, self.tr("Open Carla Project File"), self.fSavedSettings["Main/DefaultProjectFolder"], filter=fileFilter) | ||||
| if filenameTry: | if filenameTry: | ||||
| # FIXME - show dialog to user | |||||
| # FIXME - show dialog to user (remove all plugins?) | |||||
| self.removeAllPlugins() | self.removeAllPlugins() | ||||
| self.loadProject(filenameTry) | self.loadProject(filenameTry) | ||||
| @@ -1373,16 +1374,70 @@ class CarlaMainW(QMainWindow): | |||||
| fileFilter = self.tr("Carla Project File (*.carxp)") | fileFilter = self.tr("Carla Project File (*.carxp)") | ||||
| filenameTry = QFileDialog.getSaveFileName(self, self.tr("Save Carla Project File"), self.fSavedSettings["Main/DefaultProjectFolder"], filter=fileFilter) | 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() | @pyqtSlot() | ||||
| def slot_fileSaveAs(self): | def slot_fileSaveAs(self): | ||||
| self.slot_fileSave(True) | 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() | @pyqtSlot() | ||||
| def slot_loadProjectLater(self): | def slot_loadProjectLater(self): | ||||
| self.fProjectLoading = True | self.fProjectLoading = True | ||||