From cde56e0de493a7162af7be915462f5df191d9489 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 30 Dec 2017 14:52:47 +0100 Subject: [PATCH] Finish splitting lv2 code; Add midi and time ports to export-lv2 --- source/backend/plugin/CarlaPlugin.cpp | 79 ++- source/bridges-plugin/CarlaBridgeLV2.cpp | 290 +++++------ source/plugin/carla-lv2.cpp | 620 +++-------------------- source/plugin/carla-vst.cpp | 3 +- source/utils/CarlaLv2Utils.hpp | 532 ++++++++++++++++++- 5 files changed, 806 insertions(+), 718 deletions(-) diff --git a/source/backend/plugin/CarlaPlugin.cpp b/source/backend/plugin/CarlaPlugin.cpp index 7e5f3fa09..f5c330634 100644 --- a/source/backend/plugin/CarlaPlugin.cpp +++ b/source/backend/plugin/CarlaPlugin.cpp @@ -1019,31 +1019,62 @@ bool CarlaPlugin::exportAsLV2(const char* const lv2path) mainStream << "\n"; } + const uint32_t midiIns = getMidiInCount(); + const uint32_t midiOuts = getMidiOutCount(); + int portIndex = 0; - for (uint32_t i=0; iaudioIn.count; ++i) + if (midiIns > 0) { - const String portIndexNum(portIndex++); - const String portIndexLabel(portIndex); + mainStream << " lv2:port [\n"; + mainStream << " a lv2:InputPort, lv2:AtomPort ;\n"; + mainStream << " lv2:index 0 ;\n"; + mainStream << " lv2:symbol \"lv2_events_in\" ;\n"; + mainStream << " lv2:name \"Events Input\" ;\n"; + mainStream << " atom:bufferType atom:Sequence ;\n"; + mainStream << " atom:supports ,\n"; + mainStream << " ;\n"; + mainStream << " ] ;\n"; + ++portIndex; + for (uint32_t i=1; i ;\n"; mainStream << " ] ;\n"; + ++portIndex; } - for (uint32_t i=0; iaudioOut.count; ++i) + for (uint32_t i=0; i ;\n"; mainStream << " ] ;\n"; } @@ -1060,6 +1091,32 @@ bool CarlaPlugin::exportAsLV2(const char* const lv2path) mainStream << " lv2:portProperty ;\n"; mainStream << " ] ;\n"; + for (uint32_t i=0; iaudioIn.count; ++i) + { + const String portIndexNum(portIndex++); + const String portIndexLabel(portIndex); + + mainStream << " lv2:port [\n"; + mainStream << " a lv2:InputPort, lv2:AudioPort ;\n"; + mainStream << " lv2:index " << portIndexNum << " ;\n"; + mainStream << " lv2:symbol \"lv2_audio_in_" << portIndexLabel << "\" ;\n"; + mainStream << " lv2:name \"Audio Input " << portIndexLabel << "\" ;\n"; + mainStream << " ] ;\n"; + } + + for (uint32_t i=0; iaudioOut.count; ++i) + { + const String portIndexNum(portIndex++); + const String portIndexLabel(portIndex); + + mainStream << " lv2:port [\n"; + mainStream << " a lv2:OutputPort, lv2:AudioPort ;\n"; + mainStream << " lv2:index " << portIndexNum << " ;\n"; + mainStream << " lv2:symbol \"lv2_audio_out_" << portIndexLabel << "\" ;\n"; + mainStream << " lv2:name \"Audio Output " << portIndexLabel << "\" ;\n"; + mainStream << " ] ;\n"; + } + for (uint32_t i=0; iparam.count; ++i) { const ParameterData& paramData(pData->param.data[i]); diff --git a/source/bridges-plugin/CarlaBridgeLV2.cpp b/source/bridges-plugin/CarlaBridgeLV2.cpp index dddec4b5f..25cf69a69 100644 --- a/source/bridges-plugin/CarlaBridgeLV2.cpp +++ b/source/bridges-plugin/CarlaBridgeLV2.cpp @@ -42,8 +42,7 @@ public: const LV2_Feature* const* const features) : Lv2PluginBaseClass(sampleRate, features), fPlugin(nullptr), - fUiName(), - fPorts() + fUiName() { CARLA_SAFE_ASSERT_RETURN(pData->curPluginCount == 0,) CARLA_SAFE_ASSERT_RETURN(pData->plugins[0].plugin == nullptr,); @@ -94,7 +93,20 @@ public: CARLA_SAFE_ASSERT_RETURN(fPlugin != nullptr,); CARLA_SAFE_ASSERT_RETURN(fPlugin->isEnabled(),); - fPorts.init(fPlugin); + fPorts.usesTime = true; + fPorts.numAudioIns = fPlugin->getAudioInCount(); + fPorts.numAudioOuts = fPlugin->getAudioOutCount(); + fPorts.numMidiIns = fPlugin->getMidiInCount(); + fPorts.numMidiOuts = fPlugin->getMidiOutCount(); + fPorts.numParams = fPlugin->getParameterCount(); + + fPorts.init(); + + for (uint32_t i=0; i < fPorts.numParams; ++i) + { + fPorts.paramsLast[i] = fPlugin->getParameterValue(i); + fPorts.paramsOut [i] = fPlugin->isParameterOutput(i); + } } ~CarlaEngineLV2Single() @@ -113,11 +125,6 @@ public: // ---------------------------------------------------------------------------------------------------------------- // LV2 functions - void lv2_connect_port(const uint32_t port, void* const dataLocation) noexcept - { - fPorts.connectPort(port, dataLocation); - } - void lv2_activate() noexcept { CARLA_SAFE_ASSERT_RETURN(! fIsActive,); @@ -138,38 +145,98 @@ public: void lv2_run(const uint32_t frames) { + //const PendingRtEventsRunner prt(this, frames); + if (! lv2_pre_run(frames)) + { + updateParameterOutputs(); return; + } - fIsOffline = (fPorts.freewheel != nullptr && *fPorts.freewheel >= 0.5f); - - // Check for updated parameters - float curValue; - - for (uint32_t i=0; i < fPorts.numParams; ++i) + if (fPorts.numMidiIns > 0) { - if (fPorts.paramsOut[i]) - continue; + uint32_t engineEventIndex = 0; + carla_zeroStructs(pData->events.in, kMaxEngineEventInternalCount); - CARLA_SAFE_ASSERT_CONTINUE(fPorts.paramsPtr[i] != nullptr) + for (uint32_t i=0; i < fPorts.numMidiIns; ++i) + { + LV2_ATOM_SEQUENCE_FOREACH(fPorts.eventsIn[i], event) + { + if (event == nullptr) + continue; + if (event->body.type != fURIs.midiEvent) + continue; + if (event->body.size > 4) + continue; + if (event->time.frames >= frames) + break; - curValue = *fPorts.paramsPtr[i]; + const uint8_t* const data((const uint8_t*)(event + 1)); - if (carla_isEqual(fPorts.paramsLast[i], curValue)) - continue; + EngineEvent& engineEvent(pData->events.in[engineEventIndex++]); + + engineEvent.time = (uint32_t)event->time.frames; + engineEvent.fillFromMidiData((uint8_t)event->body.size, data, (uint8_t)i); - fPorts.paramsLast[i] = curValue; - fPlugin->setParameterValue(i, curValue, false, false, false); + if (engineEventIndex >= kMaxEngineEventInternalCount) + break; + } + } } - if (frames == 0) - return fPorts.updateOutputs(); + if (fPorts.numMidiOuts > 0) + { + carla_zeroStructs(pData->events.out, kMaxEngineEventInternalCount); + } if (fPlugin->tryLock(fIsOffline)) { fPlugin->initBuffers(); fPlugin->process(fPorts.audioIns, fPorts.audioOuts, nullptr, nullptr, frames); fPlugin->unlock(); + + if (fPorts.numMidiOuts > 0) + { + uint8_t port = 0; + uint8_t size = 0; + uint8_t data[3] = { 0, 0, 0 }; + const uint8_t* dataPtr = data; + + for (ushort i=0; i < kMaxEngineEventInternalCount; ++i) + { + const EngineEvent& engineEvent(pData->events.out[i]); + + switch (engineEvent.type) + { + case kEngineEventTypeNull: + break; + + case kEngineEventTypeControl: { + const EngineControlEvent& ctrlEvent(engineEvent.ctrl); + ctrlEvent.convertToMidiData(engineEvent.channel, size, data); + dataPtr = data; + break; + } + + case kEngineEventTypeMidi: { + const EngineMidiEvent& midiEvent(engineEvent.midi); + + port = midiEvent.port; + size = midiEvent.size; + + if (size > EngineMidiEvent::kDataSize && midiEvent.dataExt != nullptr) + dataPtr = midiEvent.dataExt; + else + dataPtr = midiEvent.data; + + break; + } + } + + if (size > 0 && ! writeMidiEvent(port, engineEvent.time, size, dataPtr)) + break; + } + } } else { @@ -178,7 +245,7 @@ public: } lv2_post_run(frames); - fPorts.updateOutputs(); + updateParameterOutputs(); } // ---------------------------------------------------------------------------------------------------------------- @@ -350,6 +417,11 @@ protected: // ---------------------------------------------------------------------------------------------------------------- + void handleParameterValueChanged(const uint32_t index, const float value) override + { + fPlugin->setParameterValue(index, value, false, false, false); + } + void handleBufferSizeChanged(const uint32_t bufferSize) override { CarlaEngine::bufferSizeChanged(bufferSize); @@ -366,156 +438,50 @@ private: CarlaPlugin* fPlugin; CarlaString fUiName; - struct Ports { - uint32_t numAudioIns; - uint32_t numAudioOuts; - uint32_t numParams; - const float** audioIns; - /* */ float** audioOuts; - float* freewheel; - float* paramsLast; - float** paramsPtr; - bool* paramsOut; - uint32_t indexOffset; - const CarlaPlugin* plugin; - - Ports() - : numAudioIns(0), - numAudioOuts(0), - numParams(0), - audioIns(nullptr), - audioOuts(nullptr), - freewheel(nullptr), - paramsLast(nullptr), - paramsPtr(nullptr), - paramsOut(nullptr), - indexOffset(1), - plugin(nullptr) {} - - ~Ports() - { - if (audioIns != nullptr) - { - delete[] audioIns; - audioIns = nullptr; - } - - if (audioOuts != nullptr) - { - delete[] audioOuts; - audioOuts = nullptr; - } - - if (paramsLast != nullptr) - { - delete[] paramsLast; - paramsLast = nullptr; - } - - if (paramsPtr != nullptr) - { - delete[] paramsPtr; - paramsPtr = nullptr; - } - - if (paramsOut != nullptr) - { - delete[] paramsOut; - paramsOut = nullptr; - } - } + void updateParameterOutputs() noexcept + { + float value; - void init(const CarlaPlugin* const pl) + for (uint32_t i=0; i < fPorts.numParams; ++i) { - plugin = pl; - - if ((numAudioIns = plugin->getAudioInCount()) > 0) - { - audioIns = new const float*[numAudioIns]; - - for (uint32_t i=0; i < numAudioIns; ++i) - audioIns[i] = nullptr; - } - - if ((numAudioOuts = plugin->getAudioOutCount()) > 0) - { - audioOuts = new float*[numAudioOuts]; - - for (uint32_t i=0; i < numAudioOuts; ++i) - audioOuts[i] = nullptr; - } - - if ((numParams = plugin->getParameterCount()) > 0) - { - paramsLast = new float[numParams]; - paramsPtr = new float*[numParams]; - paramsOut = new bool[numParams]; + if (! fPorts.paramsOut[i]) + continue; - for (uint32_t i=0; i < numParams; ++i) - { - paramsLast[i] = plugin->getParameterValue(i); - paramsPtr [i] = nullptr; - paramsOut [i] = plugin->isParameterOutput(i); - } - } + fPorts.paramsLast[i] = value = fPlugin->getParameterValue(i); - indexOffset = numAudioIns + numAudioOuts + 1; + if (fPorts.paramsPtr[i] != nullptr) + *fPorts.paramsPtr[i] = value; } + } - void connectPort(const uint32_t port, void* const dataLocation) noexcept - { - uint32_t index = 0; - - for (uint32_t i=0; i < numAudioIns; ++i) - { - if (port == index++) - { - audioIns[i] = (float*)dataLocation; - return; - } - } + bool writeMidiEvent(const uint8_t port, const uint32_t time, const uint8_t midiSize, const uint8_t* midiData) + { + CARLA_SAFE_ASSERT_RETURN(fPorts.numMidiOuts > 0, false); + CARLA_SAFE_ASSERT_RETURN(port < fPorts.numMidiOuts, false); + CARLA_SAFE_ASSERT_RETURN(midiData != nullptr, false); + CARLA_SAFE_ASSERT_RETURN(midiSize > 0, false); - for (uint32_t i=0; i < numAudioOuts; ++i) - { - if (port == index++) - { - audioOuts[i] = (float*)dataLocation; - return; - } - } + LV2_Atom_Sequence* const seq(fPorts.midiOuts[port]); + CARLA_SAFE_ASSERT_RETURN(seq != nullptr, false); - if (port == index++) - { - freewheel = (float*)dataLocation; - return; - } + Ports::MidiOutData& mData(fPorts.midiOutData[port]); - for (uint32_t i=0; i < numParams; ++i) - { - if (port == index++) - { - paramsPtr[i] = (float*)dataLocation; - return; - } - } - } + if (sizeof(LV2_Atom_Event) + midiSize > mData.capacity - mData.offset) + return false; - void updateOutputs() noexcept - { - for (uint32_t i=0; i < numParams; ++i) - { - if (! paramsOut[i]) - continue; + LV2_Atom_Event* const aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, seq) + mData.offset); - paramsLast[i] = plugin->getParameterValue(i); + aev->time.frames = time; + aev->body.size = midiSize; + aev->body.type = fURIs.midiEvent; + std::memcpy(LV2_ATOM_BODY(&aev->body), midiData, midiSize); - if (paramsPtr[i] != nullptr) - *paramsPtr[i] = paramsLast[i]; - } - } + const uint32_t size = lv2_atom_pad_size(static_cast(sizeof(LV2_Atom_Event) + midiSize)); + mData.offset += size; + seq->atom.size += size; - CARLA_DECLARE_NON_COPY_STRUCT(Ports); - } fPorts; + return true; + } // ------------------------------------------------------------------- diff --git a/source/plugin/carla-lv2.cpp b/source/plugin/carla-lv2.cpp index 9a5141e74..6bbe17a05 100644 --- a/source/plugin/carla-lv2.cpp +++ b/source/plugin/carla-lv2.cpp @@ -22,8 +22,8 @@ #include "CarlaMathUtils.hpp" #include "CarlaString.hpp" -// ----------------------------------------------------------------------- -// LV2 descriptor functions +// -------------------------------------------------------------------------------------------------------------------- +// Carla Internal Plugin API exposed as LV2 plugin class NativePlugin : public Lv2PluginBaseClass { @@ -41,8 +41,7 @@ public: #ifdef CARLA_PROPER_CPP11_SUPPORT fProgramDesc({0, 0, nullptr}), #endif - fMidiEventCount(0), - fPorts() + fMidiEventCount(0) { carla_zeroStruct(fHost); @@ -87,6 +86,8 @@ public: } } + // ---------------------------------------------------------------------------------------------------------------- + bool init() { if (fHost.resourceDir == nullptr) @@ -103,305 +104,92 @@ public: fHandle = fDescriptor->instantiate(&fHost); CARLA_SAFE_ASSERT_RETURN(fHandle != nullptr, false); - if (fDescriptor->midiIns > 0) - fUI.portOffset += fDescriptor->midiIns; - else if (fDescriptor->hints & NATIVE_PLUGIN_USES_TIME) - fUI.portOffset += 1; + fPorts.usesTime = fDescriptor->hints & NATIVE_PLUGIN_USES_TIME; + fPorts.numAudioIns = fDescriptor->audioIns; + fPorts.numAudioOuts = fDescriptor->audioOuts; + fPorts.numMidiIns = fDescriptor->midiIns; + fPorts.numMidiOuts = fDescriptor->midiOuts; - fUI.portOffset += fDescriptor->midiOuts; - fUI.portOffset += 1; // freewheel - fUI.portOffset += fDescriptor->audioIns; - fUI.portOffset += fDescriptor->audioOuts; + if (fDescriptor->get_parameter_count != nullptr && + fDescriptor->get_parameter_info != nullptr && + fDescriptor->get_parameter_value != nullptr && + fDescriptor->set_parameter_value != nullptr) + { + fPorts.numParams = fDescriptor->get_parameter_count(fHandle); + } + + fPorts.init(); + + if (fPorts.numParams > 0) + { + for (uint32_t i=0; i < fPorts.numParams; ++i) + { + fPorts.paramsLast[i] = fDescriptor->get_parameter_value(fHandle, i); + fPorts.paramsOut [i] = fDescriptor->get_parameter_info(fHandle, i)->hints & NATIVE_PARAMETER_IS_OUTPUT; + } + } - fPorts.init(fDescriptor, fHandle); return true; } - // ------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------------------------------- // LV2 functions - void lv2_connect_port(const uint32_t port, void* const dataLocation) - { - fPorts.connectPort(fDescriptor, port, dataLocation); - } - void lv2_activate() { + CARLA_SAFE_ASSERT_RETURN(! fIsActive,); + + resetTimeInfo(); + if (fDescriptor->activate != nullptr) fDescriptor->activate(fHandle); - resetTimeInfo(); + fIsActive = true; } void lv2_deactivate() { + CARLA_SAFE_ASSERT_RETURN(fIsActive,); + + fIsActive = false; + if (fDescriptor->deactivate != nullptr) fDescriptor->deactivate(fHandle); } void lv2_cleanup() { + if (fIsActive) + { + carla_stderr("Warning: Host forgot to call deactivate!"); + fIsActive = false; + + if (fDescriptor->deactivate != nullptr) + fDescriptor->deactivate(fHandle); + } + if (fDescriptor->cleanup != nullptr) fDescriptor->cleanup(fHandle); fHandle = nullptr; } + // ---------------------------------------------------------------------------------------------------------------- + void lv2_run(const uint32_t frames) { if (! lv2_pre_run(frames)) + { + updateParameterOutputs(); return; + } - fIsOffline = (fPorts.freewheel != nullptr && *fPorts.freewheel >= 0.5f); - - // cache midi events and time information first - if (fDescriptor->midiIns > 0 || (fDescriptor->hints & NATIVE_PLUGIN_USES_TIME) != 0) + if (fPorts.numMidiIns > 0) { fMidiEventCount = 0; carla_zeroStructs(fMidiEvents, kMaxMidiEvents); - if (fDescriptor->hints & NATIVE_PLUGIN_USES_TIME) - { - LV2_ATOM_SEQUENCE_FOREACH(fPorts.eventsIn[0], event) - { - if (event == nullptr) - continue; - if (event->body.type != fURIs.atomBlank && event->body.type != fURIs.atomObject) - continue; - - const LV2_Atom_Object* const obj((const LV2_Atom_Object*)&event->body); - - if (obj->body.otype != fURIs.timePos) - continue; - - LV2_Atom* bar = nullptr; - LV2_Atom* barBeat = nullptr; - LV2_Atom* beatUnit = nullptr; - LV2_Atom* beatsPerBar = nullptr; - LV2_Atom* beatsPerMinute = nullptr; - LV2_Atom* frame = nullptr; - LV2_Atom* speed = nullptr; - LV2_Atom* ticksPerBeat = nullptr; - - lv2_atom_object_get(obj, - fURIs.timeBar, &bar, - fURIs.timeBarBeat, &barBeat, - fURIs.timeBeatUnit, &beatUnit, - fURIs.timeBeatsPerBar, &beatsPerBar, - fURIs.timeBeatsPerMinute, &beatsPerMinute, - fURIs.timeFrame, &frame, - fURIs.timeSpeed, &speed, - fURIs.timeTicksPerBeat, &ticksPerBeat, - 0); - - // need to handle this first as other values depend on it - if (ticksPerBeat != nullptr) - { - double ticksPerBeatValue = -1.0; - - /**/ if (ticksPerBeat->type == fURIs.atomDouble) - ticksPerBeatValue = ((LV2_Atom_Double*)ticksPerBeat)->body; - else if (ticksPerBeat->type == fURIs.atomFloat) - ticksPerBeatValue = ((LV2_Atom_Float*)ticksPerBeat)->body; - else if (ticksPerBeat->type == fURIs.atomInt) - ticksPerBeatValue = static_cast(((LV2_Atom_Int*)ticksPerBeat)->body); - else if (ticksPerBeat->type == fURIs.atomLong) - ticksPerBeatValue = static_cast(((LV2_Atom_Long*)ticksPerBeat)->body); - else - carla_stderr("Unknown lv2 ticksPerBeat value type"); - - if (ticksPerBeatValue > 0.0) - fTimeInfo.bbt.ticksPerBeat = fLastPositionData.ticksPerBeat = ticksPerBeatValue; - else - carla_stderr("Invalid lv2 ticksPerBeat value"); - } - - // same - if (speed != nullptr) - { - /**/ if (speed->type == fURIs.atomDouble) - fLastPositionData.speed = ((LV2_Atom_Double*)speed)->body; - else if (speed->type == fURIs.atomFloat) - fLastPositionData.speed = ((LV2_Atom_Float*)speed)->body; - else if (speed->type == fURIs.atomInt) - fLastPositionData.speed = static_cast(((LV2_Atom_Int*)speed)->body); - else if (speed->type == fURIs.atomLong) - fLastPositionData.speed = static_cast(((LV2_Atom_Long*)speed)->body); - else - carla_stderr("Unknown lv2 speed value type"); - - fTimeInfo.playing = carla_isNotZero(fLastPositionData.speed); - - if (fTimeInfo.playing && fLastPositionData.beatsPerMinute > 0.0f) - { - fTimeInfo.bbt.beatsPerMinute = fLastPositionData.beatsPerMinute* - std::abs(fLastPositionData.speed); - } - } - - if (bar != nullptr) - { - int64_t barValue = -1; - - /**/ if (bar->type == fURIs.atomDouble) - barValue = static_cast(((LV2_Atom_Double*)bar)->body); - else if (bar->type == fURIs.atomFloat) - barValue = static_cast(((LV2_Atom_Float*)bar)->body); - else if (bar->type == fURIs.atomInt) - barValue = ((LV2_Atom_Int*)bar)->body; - else if (bar->type == fURIs.atomLong) - barValue = ((LV2_Atom_Long*)bar)->body; - else - carla_stderr("Unknown lv2 bar value type"); - - if (barValue >= 0 && barValue < INT32_MAX) - { - fLastPositionData.bar = static_cast(barValue); - fLastPositionData.bar_f = static_cast(barValue); - fTimeInfo.bbt.bar = fLastPositionData.bar + 1; - } - else - { - carla_stderr("Invalid lv2 bar value"); - } - } - - if (barBeat != nullptr) - { - double barBeatValue = -1.0; - - /**/ if (barBeat->type == fURIs.atomDouble) - barBeatValue = ((LV2_Atom_Double*)barBeat)->body; - else if (barBeat->type == fURIs.atomFloat) - barBeatValue = ((LV2_Atom_Float*)barBeat)->body; - else if (barBeat->type == fURIs.atomInt) - barBeatValue = static_cast(((LV2_Atom_Int*)barBeat)->body); - else if (barBeat->type == fURIs.atomLong) - barBeatValue = static_cast(((LV2_Atom_Long*)barBeat)->body); - else - carla_stderr("Unknown lv2 barBeat value type"); - - if (barBeatValue >= 0.0) - { - fLastPositionData.barBeat = static_cast(barBeatValue); - - const double rest = std::fmod(barBeatValue, 1.0); - fTimeInfo.bbt.beat = static_cast(barBeatValue-rest+1.0); - fTimeInfo.bbt.tick = static_cast(rest*fTimeInfo.bbt.ticksPerBeat+0.5); - } - else - { - carla_stderr("Invalid lv2 barBeat value"); - } - } - - if (beatUnit != nullptr) - { - int64_t beatUnitValue = -1; - - /**/ if (beatUnit->type == fURIs.atomDouble) - beatUnitValue = static_cast(((LV2_Atom_Double*)beatUnit)->body); - else if (beatUnit->type == fURIs.atomFloat) - beatUnitValue = static_cast(((LV2_Atom_Float*)beatUnit)->body); - else if (beatUnit->type == fURIs.atomInt) - beatUnitValue = ((LV2_Atom_Int*)beatUnit)->body; - else if (beatUnit->type == fURIs.atomLong) - beatUnitValue = ((LV2_Atom_Long*)beatUnit)->body; - else - carla_stderr("Unknown lv2 beatUnit value type"); - - if (beatUnitValue > 0 && beatUnitValue < UINT32_MAX) - { - fLastPositionData.beatUnit = static_cast(beatUnitValue); - fTimeInfo.bbt.beatType = static_cast(beatUnitValue); - } - else - { - carla_stderr("Invalid lv2 beatUnit value"); - } - } - - if (beatsPerBar != nullptr) - { - float beatsPerBarValue = -1.0f; - - /**/ if (beatsPerBar->type == fURIs.atomDouble) - beatsPerBarValue = static_cast(((LV2_Atom_Double*)beatsPerBar)->body); - else if (beatsPerBar->type == fURIs.atomFloat) - beatsPerBarValue = ((LV2_Atom_Float*)beatsPerBar)->body; - else if (beatsPerBar->type == fURIs.atomInt) - beatsPerBarValue = static_cast(((LV2_Atom_Int*)beatsPerBar)->body); - else if (beatsPerBar->type == fURIs.atomLong) - beatsPerBarValue = static_cast(((LV2_Atom_Long*)beatsPerBar)->body); - else - carla_stderr("Unknown lv2 beatsPerBar value type"); - - if (beatsPerBarValue > 0.0f) - fTimeInfo.bbt.beatsPerBar = fLastPositionData.beatsPerBar = beatsPerBarValue; - else - carla_stderr("Invalid lv2 beatsPerBar value"); - } - - if (beatsPerMinute != nullptr) - { - double beatsPerMinuteValue = -1.0; - - /**/ if (beatsPerMinute->type == fURIs.atomDouble) - beatsPerMinuteValue = ((LV2_Atom_Double*)beatsPerMinute)->body; - else if (beatsPerMinute->type == fURIs.atomFloat) - beatsPerMinuteValue = ((LV2_Atom_Float*)beatsPerMinute)->body; - else if (beatsPerMinute->type == fURIs.atomInt) - beatsPerMinuteValue = static_cast(((LV2_Atom_Int*)beatsPerMinute)->body); - else if (beatsPerMinute->type == fURIs.atomLong) - beatsPerMinuteValue = static_cast(((LV2_Atom_Long*)beatsPerMinute)->body); - else - carla_stderr("Unknown lv2 beatsPerMinute value type"); - - if (beatsPerMinuteValue >= 12.0 && beatsPerMinuteValue <= 999.0) - { - fTimeInfo.bbt.beatsPerMinute = fLastPositionData.beatsPerMinute = beatsPerMinuteValue; - - if (carla_isNotZero(fLastPositionData.speed)) - fTimeInfo.bbt.beatsPerMinute *= std::abs(fLastPositionData.speed); - } - else - { - carla_stderr("Invalid lv2 beatsPerMinute value"); - } - } - - if (frame != nullptr) - { - int64_t frameValue = -1; - - /**/ if (frame->type == fURIs.atomDouble) - frameValue = static_cast(((LV2_Atom_Double*)frame)->body); - else if (frame->type == fURIs.atomFloat) - frameValue = static_cast(((LV2_Atom_Float*)frame)->body); - else if (frame->type == fURIs.atomInt) - frameValue = ((LV2_Atom_Int*)frame)->body; - else if (frame->type == fURIs.atomLong) - frameValue = ((LV2_Atom_Long*)frame)->body; - else - carla_stderr("Unknown lv2 frame value type"); - - if (frameValue >= 0) - fTimeInfo.frame = fLastPositionData.frame = static_cast(frameValue); - else - carla_stderr("Invalid lv2 frame value"); - } - - fTimeInfo.bbt.barStartTick = fTimeInfo.bbt.ticksPerBeat* - fTimeInfo.bbt.beatsPerBar* - (fTimeInfo.bbt.bar-1); - - fTimeInfo.bbt.valid = (fLastPositionData.beatsPerMinute > 0.0 && - fLastPositionData.beatUnit > 0 && - fLastPositionData.beatsPerBar > 0.0f); - } - } - - for (uint32_t i=0; i < fDescriptor->midiIns; ++i) + for (uint32_t i=0; i < fPorts.numMidiIns; ++i) { LV2_ATOM_SEQUENCE_FOREACH(fPorts.eventsIn[i], event) { @@ -413,8 +201,6 @@ public: continue; if (event->time.frames >= frames) break; - if (fMidiEventCount >= kMaxMidiEvents) - break; const uint8_t* const data((const uint8_t*)(event + 1)); @@ -429,51 +215,13 @@ public: nativeEvent.data[j] = data[j]; for (; j<4; ++j) nativeEvent.data[j] = 0; - } - } - } - - // init midi out data - if (fDescriptor->midiOuts > 0) - { - for (uint32_t i=0, size=fDescriptor->midiOuts; iatom.size; - mData.offset = 0; - seq->atom.size = sizeof(LV2_Atom_Sequence_Body); - seq->atom.type = fURIs.atomSequence; - seq->body.unit = 0; - seq->body.pad = 0; + if (fMidiEventCount >= kMaxMidiEvents) + break; + } } } - // Check for updated parameters - float curValue; - - for (uint32_t i=0; i < fPorts.paramCount; ++i) - { - if (fPorts.paramsOut[i]) - continue; - - CARLA_SAFE_ASSERT_CONTINUE(fPorts.paramsPtr[i] != nullptr) - - curValue = *fPorts.paramsPtr[i]; - - if (carla_isEqual(fPorts.paramsLast[i], curValue)) - continue; - - fPorts.paramsLast[i] = curValue; - fDescriptor->set_parameter_value(fHandle, i, curValue); - } - - if (frames == 0) - return updateParameterOutputs(); - // FIXME fDescriptor->process(fHandle, const_cast(fPorts.audioIns), fPorts.audioOuts, frames, @@ -483,7 +231,7 @@ public: updateParameterOutputs(); } - // ------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------------------------------- const LV2_Program_Descriptor* lv2_get_program(const uint32_t index) { @@ -517,7 +265,7 @@ public: fDescriptor->set_midi_program(fHandle, 0, bank, program); - for (uint32_t i=0; i < fPorts.paramCount; ++i) + for (uint32_t i=0; i < fPorts.numParams; ++i) { fPorts.paramsLast[i] = fDescriptor->get_parameter_value(fHandle, i); @@ -526,7 +274,10 @@ public: } } - LV2_State_Status lv2_save(const LV2_State_Store_Function store, const LV2_State_Handle handle, const uint32_t /*flags*/, const LV2_Feature* const* const /*features*/) const + // ---------------------------------------------------------------------------------------------------------------- + + LV2_State_Status lv2_save(const LV2_State_Store_Function store, const LV2_State_Handle handle, + const uint32_t /*flags*/, const LV2_Feature* const* const /*features*/) const { if ((fDescriptor->hints & NATIVE_PLUGIN_USES_STATE) == 0 || fDescriptor->get_state == nullptr) return LV2_STATE_ERR_NO_FEATURE; @@ -541,7 +292,8 @@ public: 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*/) const + 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*/) const { if ((fDescriptor->hints & NATIVE_PLUGIN_USES_STATE) == 0 || fDescriptor->set_state == nullptr) return LV2_STATE_ERR_NO_FEATURE; @@ -564,7 +316,7 @@ public: return LV2_STATE_SUCCESS; } - // ------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------------------------------- void lv2ui_instantiate(LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features, const bool isEmbed) @@ -684,16 +436,16 @@ public: { if (format != 0 || bufferSize != sizeof(float) || buffer == nullptr) return; - if (portIndex >= fUI.portOffset || ! fUI.isVisible) + if (portIndex >= fPorts.indexOffset || ! fUI.isVisible) return; if (fDescriptor->ui_set_parameter_value == nullptr) return; const float value(*(const float*)buffer); - fDescriptor->ui_set_parameter_value(fHandle, portIndex-fUI.portOffset, value); + fDescriptor->ui_set_parameter_value(fHandle, portIndex-fPorts.indexOffset, value); } - // ------------------------------------------------------------------- + // ---------------------------------------------------------------------------------------------------------------- void lv2ui_select_program(uint32_t bank, uint32_t program) const { @@ -732,6 +484,11 @@ protected: // ---------------------------------------------------------------------------------------------------------------- + void handleParameterValueChanged(const uint32_t index, const float value) override + { + fDescriptor->set_parameter_value(fHandle, index, value); + } + void handleBufferSizeChanged(const uint32_t bufferSize) override { if (fDescriptor->dispatcher == nullptr) @@ -752,12 +509,12 @@ protected: bool handleWriteMidiEvent(const NativeMidiEvent* const event) { - CARLA_SAFE_ASSERT_RETURN(fDescriptor->midiOuts > 0, false); + CARLA_SAFE_ASSERT_RETURN(fPorts.numMidiOuts > 0, false); CARLA_SAFE_ASSERT_RETURN(event != nullptr, false); CARLA_SAFE_ASSERT_RETURN(event->size > 0, false); const uint8_t port(event->port); - CARLA_SAFE_ASSERT_RETURN(port < fDescriptor->midiOuts, false); + CARLA_SAFE_ASSERT_RETURN(port < fPorts.numMidiOuts, false); LV2_Atom_Sequence* const seq(fPorts.midiOuts[port]); CARLA_SAFE_ASSERT_RETURN(seq != nullptr, false); @@ -784,7 +541,7 @@ protected: void handleUiParameterChanged(const uint32_t index, const float value) const { if (fUI.writeFunction != nullptr && fUI.controller != nullptr) - fUI.writeFunction(fUI.controller, index+fUI.portOffset, sizeof(float), 0, &value); + fUI.writeFunction(fUI.controller, index+fPorts.indexOffset, sizeof(float), 0, &value); } void handleUiCustomDataChanged(const char* const /*key*/, const char* const /*value*/) const @@ -849,15 +606,17 @@ protected: void updateParameterOutputs() { - for (uint32_t i=0; i < fPorts.paramCount; ++i) + float value; + + for (uint32_t i=0; i < fPorts.numParams; ++i) { if (! fPorts.paramsOut[i]) continue; - fPorts.paramsLast[i] = fDescriptor->get_parameter_value(fHandle, i); + fPorts.paramsLast[i] = value = fDescriptor->get_parameter_value(fHandle, i); if (fPorts.paramsPtr[i] != nullptr) - *fPorts.paramsPtr[i] = fPorts.paramsLast[i]; + *fPorts.paramsPtr[i] = value; } } @@ -873,221 +632,6 @@ private: uint32_t fMidiEventCount; NativeMidiEvent fMidiEvents[kMaxMidiEvents]; - struct Ports { - // need to save current state - struct MidiOutData { - uint32_t capacity; - uint32_t offset; - - MidiOutData() - : capacity(0), - offset(0) {} - }; - - const LV2_Atom_Sequence** eventsIn; - /* */ LV2_Atom_Sequence** midiOuts; - /* */ MidiOutData* midiOutData; - const float** audioIns; - /* */ float** audioOuts; - float* freewheel; - uint32_t paramCount; - float* paramsLast; - float** paramsPtr; - bool* paramsOut; - - Ports() - : eventsIn(nullptr), - midiOuts(nullptr), - midiOutData(nullptr), - audioIns(nullptr), - audioOuts(nullptr), - freewheel(nullptr), - paramCount(0), - paramsLast(nullptr), - paramsPtr(nullptr), - paramsOut(nullptr) {} - - ~Ports() - { - if (eventsIn != nullptr) - { - delete[] eventsIn; - eventsIn = nullptr; - } - - if (midiOuts != nullptr) - { - delete[] midiOuts; - midiOuts = nullptr; - } - - if (midiOutData != nullptr) - { - delete[] midiOutData; - midiOutData = nullptr; - } - - if (audioIns != nullptr) - { - delete[] audioIns; - audioIns = nullptr; - } - - if (audioOuts != nullptr) - { - delete[] audioOuts; - audioOuts = nullptr; - } - - if (paramsLast != nullptr) - { - delete[] paramsLast; - paramsLast = nullptr; - } - - if (paramsPtr != nullptr) - { - delete[] paramsPtr; - paramsPtr = nullptr; - } - - if (paramsOut != nullptr) - { - delete[] paramsOut; - paramsOut = nullptr; - } - } - - void init(const NativePluginDescriptor* const desc, NativePluginHandle handle) - { - CARLA_SAFE_ASSERT_RETURN(desc != nullptr && handle != nullptr,) - - if (desc->midiIns > 0) - { - eventsIn = new const LV2_Atom_Sequence*[desc->midiIns]; - - for (uint32_t i=0; i < desc->midiIns; ++i) - eventsIn[i] = nullptr; - } - else if (desc->hints & NATIVE_PLUGIN_USES_TIME) - { - eventsIn = new const LV2_Atom_Sequence*[1]; - eventsIn[0] = nullptr; - } - - if (desc->midiOuts > 0) - { - midiOuts = new LV2_Atom_Sequence*[desc->midiOuts]; - midiOutData = new MidiOutData[desc->midiOuts]; - - for (uint32_t i=0; i < desc->midiOuts; ++i) - midiOuts[i] = nullptr; - } - - if (desc->audioIns > 0) - { - audioIns = new const float*[desc->audioIns]; - - for (uint32_t i=0; i < desc->audioIns; ++i) - audioIns[i] = nullptr; - } - - if (desc->audioOuts > 0) - { - audioOuts = new float*[desc->audioOuts]; - - for (uint32_t i=0; i < desc->audioOuts; ++i) - audioOuts[i] = nullptr; - } - - if (desc->get_parameter_count != nullptr && desc->get_parameter_info != nullptr && desc->get_parameter_value != nullptr && desc->set_parameter_value != nullptr) - { - paramCount = desc->get_parameter_count(handle); - - if (paramCount > 0) - { - paramsLast = new float[paramCount]; - paramsPtr = new float*[paramCount]; - paramsOut = new bool[paramCount]; - - for (uint32_t i=0; i < paramCount; ++i) - { - paramsLast[i] = desc->get_parameter_value(handle, i); - paramsPtr [i] = nullptr; - paramsOut [i] = (desc->get_parameter_info(handle, i)->hints & NATIVE_PARAMETER_IS_OUTPUT); - } - } - } - } - - void connectPort(const NativePluginDescriptor* const desc, const uint32_t port, void* const dataLocation) - { - uint32_t index = 0; - - if (desc->midiIns > 0 || (desc->hints & NATIVE_PLUGIN_USES_TIME) != 0) - { - if (port == index++) - { - eventsIn[0] = (LV2_Atom_Sequence*)dataLocation; - return; - } - } - - for (uint32_t i=1; i < desc->midiIns; ++i) - { - if (port == index++) - { - eventsIn[i] = (LV2_Atom_Sequence*)dataLocation; - return; - } - } - - for (uint32_t i=0; i < desc->midiOuts; ++i) - { - if (port == index++) - { - midiOuts[i] = (LV2_Atom_Sequence*)dataLocation; - return; - } - } - - if (port == index++) - { - freewheel = (float*)dataLocation; - return; - } - - for (uint32_t i=0; i < desc->audioIns; ++i) - { - if (port == index++) - { - audioIns[i] = (float*)dataLocation; - return; - } - } - - for (uint32_t i=0; i < desc->audioOuts; ++i) - { - if (port == index++) - { - audioOuts[i] = (float*)dataLocation; - return; - } - } - - for (uint32_t i=0; i < paramCount; ++i) - { - if (port == index++) - { - paramsPtr[i] = (float*)dataLocation; - return; - } - } - } - - CARLA_DECLARE_NON_COPY_STRUCT(Ports); - } fPorts; - // ------------------------------------------------------------------- #define handlePtr ((NativePlugin*)handle) diff --git a/source/plugin/carla-vst.cpp b/source/plugin/carla-vst.cpp index be5d03e53..ab45544b3 100644 --- a/source/plugin/carla-vst.cpp +++ b/source/plugin/carla-vst.cpp @@ -48,7 +48,8 @@ static double d_lastSampleRate = 0.0; static const int32_t kVstMidiEventSize = static_cast(sizeof(VstMidiEvent)); -// ----------------------------------------------------------------------- +// -------------------------------------------------------------------------------------------------------------------- +// Carla Internal Plugin API exposed as VST plugin class NativePlugin { diff --git a/source/utils/CarlaLv2Utils.hpp b/source/utils/CarlaLv2Utils.hpp index 913ada1b5..ff9d2ddea 100644 --- a/source/utils/CarlaLv2Utils.hpp +++ b/source/utils/CarlaLv2Utils.hpp @@ -631,9 +631,7 @@ public: carla_zeroStruct(fLastPositionData); } - virtual ~Lv2PluginBaseClass() - { - } + virtual ~Lv2PluginBaseClass() {} bool loadedInProperHost() const noexcept { @@ -642,8 +640,299 @@ public: // ---------------------------------------------------------------------------------------------------------------- - bool lv2_pre_run(const uint32_t /*frames*/) + void lv2_connect_port(const uint32_t port, void* const dataLocation) noexcept + { + fPorts.connectPort(port, dataLocation); + } + + bool lv2_pre_run(const uint32_t frames) { + CARLA_SAFE_ASSERT_RETURN(fIsActive, false); + + fIsOffline = (fPorts.freewheel != nullptr && *fPorts.freewheel >= 0.5f); + + // cache midi events and time information first + if (fPorts.usesTime) + { + LV2_ATOM_SEQUENCE_FOREACH(fPorts.eventsIn[0], event) + { + if (event == nullptr) + continue; + if (event->body.type != fURIs.atomBlank && event->body.type != fURIs.atomObject) + continue; + + const LV2_Atom_Object* const obj((const LV2_Atom_Object*)&event->body); + + if (obj->body.otype != fURIs.timePos) + continue; + + LV2_Atom* bar = nullptr; + LV2_Atom* barBeat = nullptr; + LV2_Atom* beatUnit = nullptr; + LV2_Atom* beatsPerBar = nullptr; + LV2_Atom* beatsPerMinute = nullptr; + LV2_Atom* frame = nullptr; + LV2_Atom* speed = nullptr; + LV2_Atom* ticksPerBeat = nullptr; + + lv2_atom_object_get(obj, + fURIs.timeBar, &bar, + fURIs.timeBarBeat, &barBeat, + fURIs.timeBeatUnit, &beatUnit, + fURIs.timeBeatsPerBar, &beatsPerBar, + fURIs.timeBeatsPerMinute, &beatsPerMinute, + fURIs.timeFrame, &frame, + fURIs.timeSpeed, &speed, + fURIs.timeTicksPerBeat, &ticksPerBeat, + 0); + + // need to handle this first as other values depend on it + if (ticksPerBeat != nullptr) + { + double ticksPerBeatValue = -1.0; + + /**/ if (ticksPerBeat->type == fURIs.atomDouble) + ticksPerBeatValue = ((LV2_Atom_Double*)ticksPerBeat)->body; + else if (ticksPerBeat->type == fURIs.atomFloat) + ticksPerBeatValue = ((LV2_Atom_Float*)ticksPerBeat)->body; + else if (ticksPerBeat->type == fURIs.atomInt) + ticksPerBeatValue = static_cast(((LV2_Atom_Int*)ticksPerBeat)->body); + else if (ticksPerBeat->type == fURIs.atomLong) + ticksPerBeatValue = static_cast(((LV2_Atom_Long*)ticksPerBeat)->body); + else + carla_stderr("Unknown lv2 ticksPerBeat value type"); + + if (ticksPerBeatValue > 0.0) + fTimeInfo.bbt.ticksPerBeat = fLastPositionData.ticksPerBeat = ticksPerBeatValue; + else + carla_stderr("Invalid lv2 ticksPerBeat value"); + } + + // same + if (speed != nullptr) + { + /**/ if (speed->type == fURIs.atomDouble) + fLastPositionData.speed = ((LV2_Atom_Double*)speed)->body; + else if (speed->type == fURIs.atomFloat) + fLastPositionData.speed = ((LV2_Atom_Float*)speed)->body; + else if (speed->type == fURIs.atomInt) + fLastPositionData.speed = static_cast(((LV2_Atom_Int*)speed)->body); + else if (speed->type == fURIs.atomLong) + fLastPositionData.speed = static_cast(((LV2_Atom_Long*)speed)->body); + else + carla_stderr("Unknown lv2 speed value type"); + + fTimeInfo.playing = carla_isNotZero(fLastPositionData.speed); + + if (fTimeInfo.playing && fLastPositionData.beatsPerMinute > 0.0f) + { + fTimeInfo.bbt.beatsPerMinute = fLastPositionData.beatsPerMinute* + std::abs(fLastPositionData.speed); + } + } + + if (bar != nullptr) + { + int64_t barValue = -1; + + /**/ if (bar->type == fURIs.atomDouble) + barValue = static_cast(((LV2_Atom_Double*)bar)->body); + else if (bar->type == fURIs.atomFloat) + barValue = static_cast(((LV2_Atom_Float*)bar)->body); + else if (bar->type == fURIs.atomInt) + barValue = ((LV2_Atom_Int*)bar)->body; + else if (bar->type == fURIs.atomLong) + barValue = ((LV2_Atom_Long*)bar)->body; + else + carla_stderr("Unknown lv2 bar value type"); + + if (barValue >= 0 && barValue < INT32_MAX) + { + fLastPositionData.bar = static_cast(barValue); + fLastPositionData.bar_f = static_cast(barValue); + fTimeInfo.bbt.bar = fLastPositionData.bar + 1; + } + else + { + carla_stderr("Invalid lv2 bar value"); + } + } + + if (barBeat != nullptr) + { + double barBeatValue = -1.0; + + /**/ if (barBeat->type == fURIs.atomDouble) + barBeatValue = ((LV2_Atom_Double*)barBeat)->body; + else if (barBeat->type == fURIs.atomFloat) + barBeatValue = ((LV2_Atom_Float*)barBeat)->body; + else if (barBeat->type == fURIs.atomInt) + barBeatValue = static_cast(((LV2_Atom_Int*)barBeat)->body); + else if (barBeat->type == fURIs.atomLong) + barBeatValue = static_cast(((LV2_Atom_Long*)barBeat)->body); + else + carla_stderr("Unknown lv2 barBeat value type"); + + if (barBeatValue >= 0.0) + { + fLastPositionData.barBeat = static_cast(barBeatValue); + + const double rest = std::fmod(barBeatValue, 1.0); + fTimeInfo.bbt.beat = static_cast(barBeatValue-rest+1.0); + fTimeInfo.bbt.tick = static_cast(rest*fTimeInfo.bbt.ticksPerBeat+0.5); + } + else + { + carla_stderr("Invalid lv2 barBeat value"); + } + } + + if (beatUnit != nullptr) + { + int64_t beatUnitValue = -1; + + /**/ if (beatUnit->type == fURIs.atomDouble) + beatUnitValue = static_cast(((LV2_Atom_Double*)beatUnit)->body); + else if (beatUnit->type == fURIs.atomFloat) + beatUnitValue = static_cast(((LV2_Atom_Float*)beatUnit)->body); + else if (beatUnit->type == fURIs.atomInt) + beatUnitValue = ((LV2_Atom_Int*)beatUnit)->body; + else if (beatUnit->type == fURIs.atomLong) + beatUnitValue = ((LV2_Atom_Long*)beatUnit)->body; + else + carla_stderr("Unknown lv2 beatUnit value type"); + + if (beatUnitValue > 0 && beatUnitValue < UINT32_MAX) + { + fLastPositionData.beatUnit = static_cast(beatUnitValue); + fTimeInfo.bbt.beatType = static_cast(beatUnitValue); + } + else + { + carla_stderr("Invalid lv2 beatUnit value"); + } + } + + if (beatsPerBar != nullptr) + { + float beatsPerBarValue = -1.0f; + + /**/ if (beatsPerBar->type == fURIs.atomDouble) + beatsPerBarValue = static_cast(((LV2_Atom_Double*)beatsPerBar)->body); + else if (beatsPerBar->type == fURIs.atomFloat) + beatsPerBarValue = ((LV2_Atom_Float*)beatsPerBar)->body; + else if (beatsPerBar->type == fURIs.atomInt) + beatsPerBarValue = static_cast(((LV2_Atom_Int*)beatsPerBar)->body); + else if (beatsPerBar->type == fURIs.atomLong) + beatsPerBarValue = static_cast(((LV2_Atom_Long*)beatsPerBar)->body); + else + carla_stderr("Unknown lv2 beatsPerBar value type"); + + if (beatsPerBarValue > 0.0f) + fTimeInfo.bbt.beatsPerBar = fLastPositionData.beatsPerBar = beatsPerBarValue; + else + carla_stderr("Invalid lv2 beatsPerBar value"); + } + + if (beatsPerMinute != nullptr) + { + double beatsPerMinuteValue = -1.0; + + /**/ if (beatsPerMinute->type == fURIs.atomDouble) + beatsPerMinuteValue = ((LV2_Atom_Double*)beatsPerMinute)->body; + else if (beatsPerMinute->type == fURIs.atomFloat) + beatsPerMinuteValue = ((LV2_Atom_Float*)beatsPerMinute)->body; + else if (beatsPerMinute->type == fURIs.atomInt) + beatsPerMinuteValue = static_cast(((LV2_Atom_Int*)beatsPerMinute)->body); + else if (beatsPerMinute->type == fURIs.atomLong) + beatsPerMinuteValue = static_cast(((LV2_Atom_Long*)beatsPerMinute)->body); + else + carla_stderr("Unknown lv2 beatsPerMinute value type"); + + if (beatsPerMinuteValue >= 12.0 && beatsPerMinuteValue <= 999.0) + { + fTimeInfo.bbt.beatsPerMinute = fLastPositionData.beatsPerMinute = beatsPerMinuteValue; + + if (carla_isNotZero(fLastPositionData.speed)) + fTimeInfo.bbt.beatsPerMinute *= std::abs(fLastPositionData.speed); + } + else + { + carla_stderr("Invalid lv2 beatsPerMinute value"); + } + } + + if (frame != nullptr) + { + int64_t frameValue = -1; + + /**/ if (frame->type == fURIs.atomDouble) + frameValue = static_cast(((LV2_Atom_Double*)frame)->body); + else if (frame->type == fURIs.atomFloat) + frameValue = static_cast(((LV2_Atom_Float*)frame)->body); + else if (frame->type == fURIs.atomInt) + frameValue = ((LV2_Atom_Int*)frame)->body; + else if (frame->type == fURIs.atomLong) + frameValue = ((LV2_Atom_Long*)frame)->body; + else + carla_stderr("Unknown lv2 frame value type"); + + if (frameValue >= 0) + fTimeInfo.frame = fLastPositionData.frame = static_cast(frameValue); + else + carla_stderr("Invalid lv2 frame value"); + } + + fTimeInfo.bbt.barStartTick = fTimeInfo.bbt.ticksPerBeat* + fTimeInfo.bbt.beatsPerBar* + (fTimeInfo.bbt.bar-1); + + fTimeInfo.bbt.valid = (fLastPositionData.beatsPerMinute > 0.0 && + fLastPositionData.beatUnit > 0 && + fLastPositionData.beatsPerBar > 0.0f); + } + } + + // Check for updated parameters + float curValue; + + for (uint32_t i=0; i < fPorts.numParams; ++i) + { + if (fPorts.paramsOut[i]) + continue; + + CARLA_SAFE_ASSERT_CONTINUE(fPorts.paramsPtr[i] != nullptr) + + curValue = *fPorts.paramsPtr[i]; + + if (carla_isEqual(fPorts.paramsLast[i], curValue)) + continue; + + fPorts.paramsLast[i] = curValue; + handleParameterValueChanged(i, curValue); + } + + if (frames == 0) + return false; + + // init midi out data + if (fPorts.numMidiOuts > 0) + { + for (uint32_t i=0; iatom.size; + fPorts.midiOutData[i].offset = 0; + + seq->atom.size = sizeof(LV2_Atom_Sequence_Body); + seq->atom.type = fURIs.atomSequence; + seq->body.unit = 0; + seq->body.pad = 0; + } + } + return true; } @@ -825,6 +1114,7 @@ protected: virtual void handleUiShow() = 0; virtual void handleUiHide() = 0; + virtual void handleParameterValueChanged(const uint32_t index, const float value) = 0; virtual void handleBufferSizeChanged(const uint32_t bufferSize) = 0; virtual void handleSampleRateChanged(const double sampleRate) = 0; @@ -878,6 +1168,238 @@ protected: fTimeInfo.bbt.beatsPerMinute = fLastPositionData.beatsPerMinute = 120.0; } + // Port stuff + struct Ports { + // need to save current state + struct MidiOutData { + uint32_t capacity; + uint32_t offset; + + MidiOutData() + : capacity(0), + offset(0) {} + }; + + // store port info + uint32_t indexOffset; + uint32_t numAudioIns; + uint32_t numAudioOuts; + uint32_t numMidiIns; + uint32_t numMidiOuts; + uint32_t numParams; + bool usesTime; + + // port buffers + const LV2_Atom_Sequence** eventsIn; + /* */ LV2_Atom_Sequence** midiOuts; + /* */ MidiOutData* midiOutData; + const float** audioIns; + /* */ float** audioOuts; + /* */ float* freewheel; + + // cached parameter values + float* paramsLast; + float** paramsPtr; + bool* paramsOut; + + Ports() + : indexOffset(0), + numAudioIns(0), + numAudioOuts(0), + numMidiIns(0), + numMidiOuts(0), + numParams(0), + usesTime(false), + eventsIn(nullptr), + midiOuts(nullptr), + midiOutData(nullptr), + audioIns(nullptr), + audioOuts(nullptr), + freewheel(nullptr), + paramsLast(nullptr), + paramsPtr(nullptr), + paramsOut(nullptr) {} + + ~Ports() + { + if (eventsIn != nullptr) + { + delete[] eventsIn; + eventsIn = nullptr; + } + + if (midiOuts != nullptr) + { + delete[] midiOuts; + midiOuts = nullptr; + } + + if (midiOutData != nullptr) + { + delete[] midiOutData; + midiOutData = nullptr; + } + + if (audioIns != nullptr) + { + delete[] audioIns; + audioIns = nullptr; + } + + if (audioOuts != nullptr) + { + delete[] audioOuts; + audioOuts = nullptr; + } + + if (paramsLast != nullptr) + { + delete[] paramsLast; + paramsLast = nullptr; + } + + if (paramsPtr != nullptr) + { + delete[] paramsPtr; + paramsPtr = nullptr; + } + + if (paramsOut != nullptr) + { + delete[] paramsOut; + paramsOut = nullptr; + } + } + + // NOTE: assumes num* has been filled by parent class + void init() + { + if (numMidiIns > 0) + { + eventsIn = new const LV2_Atom_Sequence*[numMidiIns]; + + for (uint32_t i=0; i < numMidiIns; ++i) + eventsIn[i] = nullptr; + } + else if (usesTime) + { + eventsIn = new const LV2_Atom_Sequence*[1]; + eventsIn[0] = nullptr; + } + + if (numMidiOuts > 0) + { + midiOuts = new LV2_Atom_Sequence*[numMidiOuts]; + midiOutData = new MidiOutData[numMidiOuts]; + + for (uint32_t i=0; i < numMidiOuts; ++i) + midiOuts[i] = nullptr; + } + + if (numAudioIns > 0) + { + audioIns = new const float*[numAudioIns]; + + for (uint32_t i=0; i < numAudioIns; ++i) + audioIns[i] = nullptr; + } + + if (numAudioOuts > 0) + { + audioOuts = new float*[numAudioOuts]; + + for (uint32_t i=0; i < numAudioOuts; ++i) + audioOuts[i] = nullptr; + } + + if (numParams > 0) + { + paramsLast = new float[numParams]; + paramsPtr = new float*[numParams]; + paramsOut = new bool[numParams]; + + carla_zeroFloats(paramsLast, numParams); + carla_zeroPointers(paramsPtr, numParams); + carla_zeroStructs(paramsOut, numParams); + + // NOTE: need to be filled in by the parent class + } + + indexOffset = numAudioIns + numAudioOuts + numMidiOuts; + // 1 event port for time if no midi input is used + indexOffset += numMidiIns > 0 ? numMidiIns : (usesTime ? 1 : 0); + // 1 extra for freewheel port + indexOffset += 1; + } + + void connectPort(const uint32_t port, void* const dataLocation) + { + uint32_t index = 0; + + if (numMidiIns > 0 || usesTime) + { + if (port == index++) + { + eventsIn[0] = (LV2_Atom_Sequence*)dataLocation; + return; + } + } + + for (uint32_t i=1; i < numMidiIns; ++i) + { + if (port == index++) + { + eventsIn[i] = (LV2_Atom_Sequence*)dataLocation; + return; + } + } + + for (uint32_t i=0; i < numMidiOuts; ++i) + { + if (port == index++) + { + midiOuts[i] = (LV2_Atom_Sequence*)dataLocation; + return; + } + } + + if (port == index++) + { + freewheel = (float*)dataLocation; + return; + } + + for (uint32_t i=0; i < numAudioIns; ++i) + { + if (port == index++) + { + audioIns[i] = (float*)dataLocation; + return; + } + } + + for (uint32_t i=0; i < numAudioOuts; ++i) + { + if (port == index++) + { + audioOuts[i] = (float*)dataLocation; + return; + } + } + + for (uint32_t i=0; i < numParams; ++i) + { + if (port == index++) + { + paramsPtr[i] = (float*)dataLocation; + return; + } + } + } + + CARLA_DECLARE_NON_COPY_STRUCT(Ports); + } fPorts; + // Rest of host<->plugin support struct URIDs { LV2_URID atomBlank; @@ -946,7 +1468,6 @@ protected: const LV2_External_UI_Host* host; LV2UI_Write_Function writeFunction; LV2UI_Controller controller; - uint32_t portOffset; bool isEmbed; bool isVisible; @@ -954,7 +1475,6 @@ protected: : host(nullptr), writeFunction(nullptr), controller(nullptr), - portOffset(0), isEmbed(false), isVisible(false) {} } fUI;