| @@ -11,6 +11,7 @@ | |||||
| a lv2:UtilityPlugin, lv2:Plugin ; | a lv2:UtilityPlugin, lv2:Plugin ; | ||||
| lv2:optionalFeature <http://lv2plug.in/ns/lv2core#hardRTCapable> ; | lv2:optionalFeature <http://lv2plug.in/ns/lv2core#hardRTCapable> ; | ||||
| lv2:optionalFeature <http://lv2plug.in/ns/ext/state#threadSafeRestore> ; | |||||
| lv2:requiredFeature <http://lv2plug.in/ns/ext/buf-size#boundedBlockLength> , | lv2:requiredFeature <http://lv2plug.in/ns/ext/buf-size#boundedBlockLength> , | ||||
| <http://lv2plug.in/ns/ext/worker#schedule> , | <http://lv2plug.in/ns/ext/worker#schedule> , | ||||
| @@ -30,6 +31,7 @@ | |||||
| a lv2:InputPort, atom:AtomPort ; | a lv2:InputPort, atom:AtomPort ; | ||||
| atom:bufferType atom:Sequence ; | atom:bufferType atom:Sequence ; | ||||
| atom:supports <http://lv2plug.in/ns/ext/time#Position> ; | atom:supports <http://lv2plug.in/ns/ext/time#Position> ; | ||||
| atom:supports <http://lv2plug.in/ns/ext/patch#Message> ; | |||||
| lv2:designation lv2:control ; | lv2:designation lv2:control ; | ||||
| lv2:index 0 ; | lv2:index 0 ; | ||||
| lv2:symbol "lv2_events_in" ; | lv2:symbol "lv2_events_in" ; | ||||
| @@ -41,6 +43,7 @@ | |||||
| lv2:port [ | lv2:port [ | ||||
| a lv2:OutputPort, atom:AtomPort ; | a lv2:OutputPort, atom:AtomPort ; | ||||
| atom:bufferType atom:Sequence ; | atom:bufferType atom:Sequence ; | ||||
| atom:supports <http://lv2plug.in/ns/ext/patch#Message> ; | |||||
| lv2:index 1 ; | lv2:index 1 ; | ||||
| lv2:symbol "lv2_events_out" ; | lv2:symbol "lv2_events_out" ; | ||||
| lv2:name "Events Output" ; | lv2:name "Events Output" ; | ||||
| @@ -9,6 +9,7 @@ | |||||
| a lv2:UtilityPlugin, lv2:Plugin ; | a lv2:UtilityPlugin, lv2:Plugin ; | ||||
| lv2:optionalFeature <http://lv2plug.in/ns/lv2core#hardRTCapable> ; | lv2:optionalFeature <http://lv2plug.in/ns/lv2core#hardRTCapable> ; | ||||
| lv2:optionalFeature <http://lv2plug.in/ns/ext/state#threadSafeRestore> ; | |||||
| lv2:requiredFeature <http://lv2plug.in/ns/ext/buf-size#boundedBlockLength> , | lv2:requiredFeature <http://lv2plug.in/ns/ext/buf-size#boundedBlockLength> , | ||||
| <http://lv2plug.in/ns/ext/options#options> , | <http://lv2plug.in/ns/ext/options#options> , | ||||
| @@ -27,6 +28,7 @@ | |||||
| a lv2:InputPort, atom:AtomPort ; | a lv2:InputPort, atom:AtomPort ; | ||||
| atom:bufferType atom:Sequence ; | atom:bufferType atom:Sequence ; | ||||
| atom:supports <http://lv2plug.in/ns/ext/time#Position> ; | atom:supports <http://lv2plug.in/ns/ext/time#Position> ; | ||||
| atom:supports <http://lv2plug.in/ns/ext/patch#Message> ; | |||||
| lv2:designation lv2:control ; | lv2:designation lv2:control ; | ||||
| lv2:index 0 ; | lv2:index 0 ; | ||||
| lv2:symbol "lv2_events_in" ; | lv2:symbol "lv2_events_in" ; | ||||
| @@ -39,6 +41,7 @@ | |||||
| a lv2:OutputPort, atom:AtomPort ; | a lv2:OutputPort, atom:AtomPort ; | ||||
| atom:bufferType atom:Sequence ; | atom:bufferType atom:Sequence ; | ||||
| atom:supports <http://lv2plug.in/ns/ext/midi#MidiEvent> ; | atom:supports <http://lv2plug.in/ns/ext/midi#MidiEvent> ; | ||||
| atom:supports <http://lv2plug.in/ns/ext/patch#Message> ; | |||||
| lv2:index 1 ; | lv2:index 1 ; | ||||
| lv2:symbol "lv2_events_out" ; | lv2:symbol "lv2_events_out" ; | ||||
| lv2:name "Events Output" ; | lv2:name "Events Output" ; | ||||
| @@ -7303,7 +7303,7 @@ public: | |||||
| lv2_atom_forge_urid(&atomForge, urid); | lv2_atom_forge_urid(&atomForge, urid); | ||||
| lv2_atom_forge_key(&atomForge, kUridPatchValue); | lv2_atom_forge_key(&atomForge, kUridPatchValue); | ||||
| lv2_atom_forge_path(&atomForge, path, static_cast<uint32_t>(std::strlen(path))); | |||||
| lv2_atom_forge_path(&atomForge, path, static_cast<uint32_t>(std::strlen(path))+1); | |||||
| lv2_atom_forge_pop(&atomForge, &forgeFrame); | lv2_atom_forge_pop(&atomForge, &forgeFrame); | ||||
| @@ -140,7 +140,7 @@ lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size); | |||||
| not held. | not held. | ||||
| */ | */ | ||||
| static inline void | static inline void | ||||
| lv2_atom_forge_init(LV2_Atom_Forge* forge, LV2_URID_Map* map) | |||||
| lv2_atom_forge_init(LV2_Atom_Forge* forge, const LV2_URID_Map* map) | |||||
| { | { | ||||
| lv2_atom_forge_set_buffer(forge, NULL, 0); | lv2_atom_forge_set_buffer(forge, NULL, 0); | ||||
| forge->Blank = map->map(map->handle, LV2_ATOM__Blank); | forge->Blank = map->map(map->handle, LV2_ATOM__Blank); | ||||
| @@ -323,10 +323,12 @@ static void writePluginFile(const NativePluginDescriptor* const pluginDesc, | |||||
| // optional | // optional | ||||
| if (pluginDesc->hints & NATIVE_PLUGIN_IS_RTSAFE) | if (pluginDesc->hints & NATIVE_PLUGIN_IS_RTSAFE) | ||||
| text += " lv2:optionalFeature <" LV2_CORE__hardRTCapable "> ;\n\n"; | |||||
| text += " lv2:optionalFeature <" LV2_CORE__hardRTCapable "> ;\n"; | |||||
| if (pluginDesc->hints & NATIVE_PLUGIN_HAS_INLINE_DISPLAY) | if (pluginDesc->hints & NATIVE_PLUGIN_HAS_INLINE_DISPLAY) | ||||
| text += " lv2:optionalFeature <" LV2_INLINEDISPLAY__queue_draw "> ;\n"; | text += " lv2:optionalFeature <" LV2_INLINEDISPLAY__queue_draw "> ;\n"; | ||||
| if ((pluginDesc->hints & NATIVE_PLUGIN_USES_STATE) || (pluginDesc->hints & NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE)) | |||||
| text += " lv2:optionalFeature <" LV2_STATE__threadSafeRestore "> ;\n"; | |||||
| text += "\n"; | |||||
| // required | // required | ||||
| text += " lv2:requiredFeature <" LV2_BUF_SIZE__boundedBlockLength "> ,\n"; | text += " lv2:requiredFeature <" LV2_BUF_SIZE__boundedBlockLength "> ,\n"; | ||||
| @@ -404,6 +406,8 @@ static void writePluginFile(const NativePluginDescriptor* const pluginDesc, | |||||
| { | { | ||||
| text += " atom:supports <" LV2_TIME__Position "> ;\n"; | text += " atom:supports <" LV2_TIME__Position "> ;\n"; | ||||
| } | } | ||||
| if (pluginDesc->hints & NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE) | |||||
| text += " atom:supports <" LV2_PATCH__Message "> ;\n"; | |||||
| text += " lv2:designation lv2:control ;\n"; | text += " lv2:designation lv2:control ;\n"; | ||||
| text += " lv2:index " + String(portIndex++) + " ;\n"; | text += " lv2:index " + String(portIndex++) + " ;\n"; | ||||
| @@ -482,6 +486,8 @@ static void writePluginFile(const NativePluginDescriptor* const pluginDesc, | |||||
| if (pluginDesc->midiOuts > 0) | if (pluginDesc->midiOuts > 0) | ||||
| text += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n"; | text += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n"; | ||||
| if (pluginDesc->hints & NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE) | |||||
| text += " atom:supports <" LV2_PATCH__Message "> ;\n"; | |||||
| text += " lv2:index " + String(portIndex++) + " ;\n"; | text += " lv2:index " + String(portIndex++) + " ;\n"; | ||||
| @@ -54,9 +54,11 @@ public: | |||||
| fProgramDesc({0, 0, nullptr}), | fProgramDesc({0, 0, nullptr}), | ||||
| #endif | #endif | ||||
| kIgnoreParameters(std::strncmp(desc->label, "carla", 5) == 0), | kIgnoreParameters(std::strncmp(desc->label, "carla", 5) == 0), | ||||
| fAtomForge(), | |||||
| fMidiEventCount(0), | fMidiEventCount(0), | ||||
| fLastProjectPath(), | fLastProjectPath(), | ||||
| fLoadedFile(), | fLoadedFile(), | ||||
| fNeedsNotifyFileChanged(false), | |||||
| fPluginNeedsIdle(false), | fPluginNeedsIdle(false), | ||||
| fWorkerUISignal(0) | fWorkerUISignal(0) | ||||
| { | { | ||||
| @@ -89,6 +91,9 @@ public: | |||||
| fHost.ui_open_file = host_ui_open_file; | fHost.ui_open_file = host_ui_open_file; | ||||
| fHost.ui_save_file = host_ui_save_file; | fHost.ui_save_file = host_ui_save_file; | ||||
| fHost.dispatcher = host_dispatcher; | fHost.dispatcher = host_dispatcher; | ||||
| carla_zeroStruct(fAtomForge); | |||||
| lv2_atom_forge_init(&fAtomForge, fUridMap); | |||||
| } | } | ||||
| ~NativePlugin() | ~NativePlugin() | ||||
| @@ -259,7 +264,8 @@ public: | |||||
| { | { | ||||
| const LV2_Atom_Object* const obj = (const LV2_Atom_Object*)(&event->body); | const LV2_Atom_Object* const obj = (const LV2_Atom_Object*)(&event->body); | ||||
| if (obj->body.otype == fURIs.patchSet) { | |||||
| if (obj->body.otype == fURIs.patchSet) | |||||
| { | |||||
| // Get property URI. | // Get property URI. | ||||
| const LV2_Atom* property = nullptr; | const LV2_Atom* property = nullptr; | ||||
| lv2_atom_object_get(obj, fURIs.patchProperty, &property, 0); | lv2_atom_object_get(obj, fURIs.patchProperty, &property, 0); | ||||
| @@ -288,6 +294,11 @@ public: | |||||
| static_cast<uint32_t>(std::strlen(filepath) + 1U), | static_cast<uint32_t>(std::strlen(filepath) + 1U), | ||||
| filepath); | filepath); | ||||
| } | } | ||||
| else if (obj->body.otype == fURIs.patchGet) | |||||
| { | |||||
| if (fDescriptor->hints & NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE) | |||||
| fNeedsNotifyFileChanged = true; | |||||
| } | |||||
| continue; | continue; | ||||
| } | } | ||||
| @@ -317,6 +328,54 @@ public: | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| if (fNeedsNotifyFileChanged) | |||||
| { | |||||
| fNeedsNotifyFileChanged = false; | |||||
| uint8_t atomBuf[4096]; | |||||
| LV2_Atom_Forge atomForge = fAtomForge; | |||||
| lv2_atom_forge_set_buffer(&atomForge, atomBuf, sizeof(atomBuf)); | |||||
| LV2_Atom_Forge_Frame forgeFrame; | |||||
| lv2_atom_forge_object(&atomForge, &forgeFrame, 0, fURIs.patchSet); | |||||
| lv2_atom_forge_key(&atomForge, fURIs.patchProperty); | |||||
| /* */ if (std::strcmp(fDescriptor->label, "audiofile") == 0) { | |||||
| lv2_atom_forge_urid(&atomForge, fURIs.carlaFileAudio); | |||||
| } else if (std::strcmp(fDescriptor->label, "midifile") == 0) { | |||||
| lv2_atom_forge_urid(&atomForge, fURIs.carlaFileMIDI); | |||||
| } else { | |||||
| lv2_atom_forge_urid(&atomForge, fURIs.carlaFile); | |||||
| } | |||||
| lv2_atom_forge_key(&atomForge, fURIs.patchValue); | |||||
| lv2_atom_forge_path(&atomForge, | |||||
| fLoadedFile.buffer(), | |||||
| static_cast<uint32_t>(fLoadedFile.length()+1)); | |||||
| lv2_atom_forge_pop(&atomForge, &forgeFrame); | |||||
| LV2_Atom* const atom = (LV2_Atom*)atomBuf; | |||||
| LV2_Atom_Sequence* const seq = fPorts.eventsOut[0]; | |||||
| Ports::EventsOutData& mData(fPorts.eventsOutData[0]); | |||||
| if (sizeof(LV2_Atom_Event) + atom->size <= mData.capacity - mData.offset) | |||||
| { | |||||
| LV2_Atom_Event* const aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, seq) + mData.offset); | |||||
| aev->time.frames = 0; | |||||
| aev->body.size = atom->size; | |||||
| aev->body.type = atom->type; | |||||
| std::memcpy(LV2_ATOM_BODY(&aev->body), atom + 1, atom->size); | |||||
| const uint32_t size = lv2_atom_pad_size(static_cast<uint32_t>(sizeof(LV2_Atom_Event) + atom->size)); | |||||
| mData.offset += size; | |||||
| seq->atom.size += size; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| fDescriptor->process(fHandle, fPorts.audioCVIns, fPorts.audioCVOuts, frames, fMidiEvents, fMidiEventCount); | fDescriptor->process(fHandle, fPorts.audioCVIns, fPorts.audioCVOuts, frames, fMidiEvents, fMidiEventCount); | ||||
| @@ -324,7 +383,7 @@ public: | |||||
| if (fPluginNeedsIdle) | if (fPluginNeedsIdle) | ||||
| { | { | ||||
| fPluginNeedsIdle = false; | fPluginNeedsIdle = false; | ||||
| const char* const msg = "idle"; | |||||
| const char* const msg = "_idle_"; | |||||
| const size_t msgSize = std::strlen(msg); | const size_t msgSize = std::strlen(msg); | ||||
| fWorker->schedule_work(fWorker->handle, static_cast<uint32_t>(msgSize + 1U), msg); | fWorker->schedule_work(fWorker->handle, static_cast<uint32_t>(msgSize + 1U), msg); | ||||
| } | } | ||||
| @@ -453,22 +512,57 @@ public: | |||||
| if (fDescriptor->hints & NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE) | if (fDescriptor->hints & NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE) | ||||
| { | { | ||||
| if (fLoadedFile.isEmpty()) | |||||
| return LV2_STATE_SUCCESS; | |||||
| const LV2_State_Free_Path* freePath = nullptr; | |||||
| const LV2_State_Map_Path* mapPath = nullptr; | |||||
| if (features != nullptr) | |||||
| { | |||||
| for (int i=0; features[i] != nullptr; ++i) | |||||
| { | |||||
| /**/ if (freePath == nullptr && std::strcmp(features[i]->URI, LV2_STATE__freePath) == 0) | |||||
| freePath = (const LV2_State_Free_Path*)features[i]->data; | |||||
| else if (mapPath == nullptr && std::strcmp(features[i]->URI, LV2_STATE__mapPath) == 0) | |||||
| mapPath = (const LV2_State_Map_Path*)features[i]->data; | |||||
| } | |||||
| } | |||||
| if (mapPath == nullptr || mapPath->abstract_path == nullptr) | |||||
| return LV2_STATE_ERR_NO_FEATURE; | |||||
| char* path = mapPath->abstract_path(mapPath->handle, fLoadedFile.buffer()); | |||||
| store(handle, | store(handle, | ||||
| fUridMap->map(fUridMap->handle, "http://kxstudio.sf.net/ns/carla/file"), | fUridMap->map(fUridMap->handle, "http://kxstudio.sf.net/ns/carla/file"), | ||||
| fLoadedFile.buffer(), | |||||
| fLoadedFile.length()+1, | |||||
| path, | |||||
| std::strlen(path)+1, | |||||
| fURIs.atomPath, | fURIs.atomPath, | ||||
| LV2_STATE_IS_POD); | |||||
| LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE); | |||||
| if (freePath != nullptr && freePath->free_path != nullptr) | |||||
| freePath->free_path(freePath->handle, path); | |||||
| #ifndef CARLA_OS_WIN | |||||
| // this is not safe to call under windows | |||||
| else | |||||
| std::free(path); | |||||
| #endif | |||||
| return LV2_STATE_SUCCESS; | return LV2_STATE_SUCCESS; | ||||
| } | } | ||||
| if ((fDescriptor->hints & NATIVE_PLUGIN_USES_STATE) == 0 || fDescriptor->get_state == nullptr) | if ((fDescriptor->hints & NATIVE_PLUGIN_USES_STATE) == 0 || fDescriptor->get_state == nullptr) | ||||
| return LV2_STATE_ERR_NO_FEATURE; | |||||
| return LV2_STATE_ERR_UNKNOWN; | |||||
| if (char* const state = fDescriptor->get_state(fHandle)) | if (char* const state = fDescriptor->get_state(fHandle)) | ||||
| { | { | ||||
| store(handle, fUridMap->map(fUridMap->handle, "http://kxstudio.sf.net/ns/carla/chunk"), | |||||
| state, std::strlen(state)+1, fURIs.atomString, LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE); | |||||
| store(handle, | |||||
| fUridMap->map(fUridMap->handle, "http://kxstudio.sf.net/ns/carla/chunk"), | |||||
| state, | |||||
| std::strlen(state)+1, | |||||
| fURIs.atomString, | |||||
| LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE); | |||||
| std::free(state); | std::free(state); | ||||
| return LV2_STATE_SUCCESS; | return LV2_STATE_SUCCESS; | ||||
| } | } | ||||
| @@ -476,8 +570,10 @@ public: | |||||
| return LV2_STATE_ERR_UNKNOWN; | return LV2_STATE_ERR_UNKNOWN; | ||||
| } | } | ||||
| LV2_State_Status lv2_restore(const LV2_State_Retrieve_Function retrieve, const LV2_State_Handle handle, | |||||
| uint32_t flags, const LV2_Feature* const* const features) | |||||
| LV2_State_Status lv2_restore(const LV2_State_Retrieve_Function retrieve, | |||||
| const LV2_State_Handle handle, | |||||
| uint32_t flags, | |||||
| const LV2_Feature* const* const features) | |||||
| { | { | ||||
| saveLastProjectPathIfPossible(features); | saveLastProjectPathIfPossible(features); | ||||
| @@ -490,21 +586,53 @@ public: | |||||
| const void* const data = retrieve(handle, | const void* const data = retrieve(handle, | ||||
| fUridMap->map(fUridMap->handle, "http://kxstudio.sf.net/ns/carla/file"), | fUridMap->map(fUridMap->handle, "http://kxstudio.sf.net/ns/carla/file"), | ||||
| &size, &type, &flags); | &size, &type, &flags); | ||||
| if (size <= 1 || type == 0) | |||||
| return LV2_STATE_ERR_NO_PROPERTY; | |||||
| CARLA_SAFE_ASSERT_RETURN(type == fURIs.atomPath, LV2_STATE_ERR_UNKNOWN); | CARLA_SAFE_ASSERT_RETURN(type == fURIs.atomPath, LV2_STATE_ERR_UNKNOWN); | ||||
| const LV2_State_Free_Path* freePath = nullptr; | |||||
| const LV2_State_Map_Path* mapPath = nullptr; | |||||
| if (features != nullptr) | |||||
| { | |||||
| for (int i=0; features[i] != nullptr; ++i) | |||||
| { | |||||
| /**/ if (freePath == nullptr && std::strcmp(features[i]->URI, LV2_STATE__freePath) == 0) | |||||
| freePath = (const LV2_State_Free_Path*)features[i]->data; | |||||
| else if (mapPath == nullptr && std::strcmp(features[i]->URI, LV2_STATE__mapPath) == 0) | |||||
| mapPath = (const LV2_State_Map_Path*)features[i]->data; | |||||
| } | |||||
| } | |||||
| if (mapPath == nullptr || mapPath->absolute_path == nullptr) | |||||
| return LV2_STATE_ERR_NO_FEATURE; | |||||
| const char* const filename = (const char*)data; | const char* const filename = (const char*)data; | ||||
| fLoadedFile = filename; | |||||
| fDescriptor->set_custom_data(fHandle, "file", filename); | |||||
| char* const absolute_filename = mapPath->absolute_path(mapPath->handle, filename); | |||||
| fLoadedFile = absolute_filename; | |||||
| if (freePath != nullptr && freePath->free_path != nullptr) | |||||
| freePath->free_path(freePath->handle, absolute_filename); | |||||
| #ifndef CARLA_OS_WIN | |||||
| // this is not safe to call under windows | |||||
| else | |||||
| std::free(absolute_filename); | |||||
| #endif | |||||
| fNeedsNotifyFileChanged = true; | |||||
| fDescriptor->set_custom_data(fHandle, "file", fLoadedFile); | |||||
| return LV2_STATE_SUCCESS; | return LV2_STATE_SUCCESS; | ||||
| } | } | ||||
| if ((fDescriptor->hints & NATIVE_PLUGIN_USES_STATE) == 0 || fDescriptor->set_state == nullptr) | if ((fDescriptor->hints & NATIVE_PLUGIN_USES_STATE) == 0 || fDescriptor->set_state == nullptr) | ||||
| return LV2_STATE_ERR_NO_FEATURE; | |||||
| return LV2_STATE_ERR_UNKNOWN; | |||||
| size = type = 0; | size = type = 0; | ||||
| const void* const data = retrieve(handle, fUridMap->map(fUridMap->handle, "http://kxstudio.sf.net/ns/carla/chunk"), &size, &type, &flags); | |||||
| const void* const data = retrieve(handle, | |||||
| fUridMap->map(fUridMap->handle, "http://kxstudio.sf.net/ns/carla/chunk"), | |||||
| &size, &type, &flags); | |||||
| if (size == 0) | if (size == 0) | ||||
| return LV2_STATE_ERR_UNKNOWN; | return LV2_STATE_ERR_UNKNOWN; | ||||
| @@ -526,13 +654,14 @@ public: | |||||
| { | { | ||||
| const char* const msg = (const char*)data; | const char* const msg = (const char*)data; | ||||
| if (fDescriptor->hints & NATIVE_PLUGIN_REQUESTS_IDLE) | |||||
| if (std::strcmp(msg, "_idle_") == 0) | |||||
| { | { | ||||
| if (std::strcmp(msg, "idle") == 0) | |||||
| if (fDescriptor->hints & NATIVE_PLUGIN_REQUESTS_IDLE) | |||||
| { | { | ||||
| fDescriptor->dispatcher(fHandle, NATIVE_PLUGIN_OPCODE_IDLE, 0, 0, nullptr, 0.0f); | fDescriptor->dispatcher(fHandle, NATIVE_PLUGIN_OPCODE_IDLE, 0, 0, nullptr, 0.0f); | ||||
| return LV2_WORKER_SUCCESS; | return LV2_WORKER_SUCCESS; | ||||
| } | } | ||||
| return LV2_WORKER_ERR_UNKNOWN; | |||||
| } | } | ||||
| if (fDescriptor->hints & NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE) | if (fDescriptor->hints & NATIVE_PLUGIN_NEEDS_UI_OPEN_SAVE) | ||||
| @@ -949,11 +1078,13 @@ private: | |||||
| // carla as plugin does not implement lv2 parameter API yet, needed for feedback | // carla as plugin does not implement lv2 parameter API yet, needed for feedback | ||||
| const bool kIgnoreParameters; | const bool kIgnoreParameters; | ||||
| LV2_Atom_Forge fAtomForge; | |||||
| uint32_t fMidiEventCount; | uint32_t fMidiEventCount; | ||||
| NativeMidiEvent fMidiEvents[kMaxMidiEvents]; | NativeMidiEvent fMidiEvents[kMaxMidiEvents]; | ||||
| CarlaString fLastProjectPath; | CarlaString fLastProjectPath; | ||||
| CarlaString fLoadedFile; | CarlaString fLoadedFile; | ||||
| volatile bool fNeedsNotifyFileChanged; | |||||
| volatile bool fPluginNeedsIdle; | volatile bool fPluginNeedsIdle; | ||||
| int fWorkerUISignal; | int fWorkerUISignal; | ||||
| @@ -963,10 +963,12 @@ public: | |||||
| if (frames == 0) | if (frames == 0) | ||||
| return false; | return false; | ||||
| // init midi out data | |||||
| // init event out data | |||||
| if (fPorts.numMidiOuts > 0 || fPorts.hasUI) | if (fPorts.numMidiOuts > 0 || fPorts.hasUI) | ||||
| { | { | ||||
| for (uint32_t i=0; i<fPorts.numMidiOuts; ++i) | |||||
| const uint32_t count = fPorts.numMidiOuts > 0 ? fPorts.numMidiOuts : 1; | |||||
| for (uint32_t i=0; i < count; ++i) | |||||
| { | { | ||||
| LV2_Atom_Sequence* const seq(fPorts.eventsOut[i]); | LV2_Atom_Sequence* const seq(fPorts.eventsOut[i]); | ||||
| CARLA_SAFE_ASSERT_CONTINUE(seq != nullptr); | CARLA_SAFE_ASSERT_CONTINUE(seq != nullptr); | ||||
| @@ -1371,6 +1373,7 @@ protected: | |||||
| { | { | ||||
| eventsOut = new LV2_Atom_Sequence*[1]; | eventsOut = new LV2_Atom_Sequence*[1]; | ||||
| eventsOut[0] = nullptr; | eventsOut[0] = nullptr; | ||||
| eventsOutData = new EventsOutData[1]; | |||||
| } | } | ||||
| if (const uint32_t numAudioCVIns = numAudioIns+numCVIns) | if (const uint32_t numAudioCVIns = numAudioIns+numCVIns) | ||||
| @@ -1519,6 +1522,7 @@ protected: | |||||
| LV2_URID carlaFileMIDI; | LV2_URID carlaFileMIDI; | ||||
| LV2_URID midiEvent; | LV2_URID midiEvent; | ||||
| LV2_URID patchProperty; | LV2_URID patchProperty; | ||||
| LV2_URID patchGet; | |||||
| LV2_URID patchSet; | LV2_URID patchSet; | ||||
| LV2_URID patchValue; | LV2_URID patchValue; | ||||
| LV2_URID timePos; | LV2_URID timePos; | ||||
| @@ -1549,6 +1553,7 @@ protected: | |||||
| carlaFileMIDI(0), | carlaFileMIDI(0), | ||||
| midiEvent(0), | midiEvent(0), | ||||
| patchProperty(0), | patchProperty(0), | ||||
| patchGet(0), | |||||
| patchSet(0), | patchSet(0), | ||||
| patchValue(0), | patchValue(0), | ||||
| timePos(0), | timePos(0), | ||||
| @@ -1580,6 +1585,7 @@ protected: | |||||
| carlaFileMIDI = uridMap->map(uridMap->handle, "http://kxstudio.sf.net/carla/file/midi"); | carlaFileMIDI = uridMap->map(uridMap->handle, "http://kxstudio.sf.net/carla/file/midi"); | ||||
| midiEvent = uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent); | midiEvent = uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent); | ||||
| patchProperty = uridMap->map(uridMap->handle, LV2_PATCH__property); | patchProperty = uridMap->map(uridMap->handle, LV2_PATCH__property); | ||||
| patchGet = uridMap->map(uridMap->handle, LV2_PATCH__Get); | |||||
| patchSet = uridMap->map(uridMap->handle, LV2_PATCH__Set); | patchSet = uridMap->map(uridMap->handle, LV2_PATCH__Set); | ||||
| patchValue = uridMap->map(uridMap->handle, LV2_PATCH__value); | patchValue = uridMap->map(uridMap->handle, LV2_PATCH__value); | ||||
| timePos = uridMap->map(uridMap->handle, LV2_TIME__Position); | timePos = uridMap->map(uridMap->handle, LV2_TIME__Position); | ||||
| @@ -2344,12 +2350,11 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) | |||||
| // ---------------------------------------------------------------------------------------------------------------- | // ---------------------------------------------------------------------------------------------------------------- | ||||
| // Set Plugin Parameters | // Set Plugin Parameters | ||||
| { | { | ||||
| std::map<std::string, LV2_RDF_Parameter> parameters; | |||||
| Lilv::Nodes patchWritableNodes(lilvPlugin.get_value(lv2World.patch_writable)); | Lilv::Nodes patchWritableNodes(lilvPlugin.get_value(lv2World.patch_writable)); | ||||
| if (const uint numParameters = patchWritableNodes.size()) | if (const uint numParameters = patchWritableNodes.size()) | ||||
| { | { | ||||
| rdfDescriptor->Parameters = new LV2_RDF_Parameter[numParameters]; | |||||
| uint numUsed = 0; | uint numUsed = 0; | ||||
| LILV_FOREACH(nodes, it, patchWritableNodes) | LILV_FOREACH(nodes, it, patchWritableNodes) | ||||
| { | { | ||||
| @@ -2371,10 +2376,10 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) | |||||
| lilv_node_free(typeNode); | lilv_node_free(typeNode); | ||||
| } | } | ||||
| LV2_RDF_Parameter& rdfParam(rdfDescriptor->Parameters[numUsed++]); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(patchWritableNode.is_uri()); | CARLA_SAFE_ASSERT_CONTINUE(patchWritableNode.is_uri()); | ||||
| ++numUsed; | |||||
| LV2_RDF_Parameter rdfParam; | |||||
| rdfParam.URI = carla_strdup(patchWritableNode.as_uri()); | rdfParam.URI = carla_strdup(patchWritableNode.as_uri()); | ||||
| // ---------------------------------------------------------------------------------------------------- | // ---------------------------------------------------------------------------------------------------- | ||||
| @@ -2557,9 +2562,20 @@ const LV2_RDF_Descriptor* lv2_rdf_new(const LV2_URI uri, const bool loadPresets) | |||||
| lilv_node_free(unitUnitNode); | lilv_node_free(unitUnitNode); | ||||
| } | } | ||||
| parameters[rdfParam.URI] = rdfParam; | |||||
| } | } | ||||
| CARLA_SAFE_ASSERT_UINT2(parameters.size() == numUsed, parameters.size(), numUsed); | |||||
| rdfDescriptor->Parameters = new LV2_RDF_Parameter[numUsed]; | |||||
| rdfDescriptor->ParameterCount = numUsed; | rdfDescriptor->ParameterCount = numUsed; | ||||
| numUsed = 0; | |||||
| for (std::map<std::string, LV2_RDF_Parameter>::iterator it = parameters.begin(), end = parameters.end(); | |||||
| it != end; ++it) | |||||
| { | |||||
| rdfDescriptor->Parameters[numUsed++] = it->second; | |||||
| } | |||||
| } | } | ||||
| lilv_nodes_free(const_cast<LilvNodes*>(patchWritableNodes.me)); | lilv_nodes_free(const_cast<LilvNodes*>(patchWritableNodes.me)); | ||||