| @@ -274,6 +274,8 @@ struct CARLA_API EngineOptions { | |||||
| * Engine BBT Time information. | * Engine BBT Time information. | ||||
| */ | */ | ||||
| struct CARLA_API EngineTimeInfoBBT { | struct CARLA_API EngineTimeInfoBBT { | ||||
| bool valid; | |||||
| int32_t bar; //!< current bar | int32_t bar; //!< current bar | ||||
| int32_t beat; //!< current beat-within-bar | int32_t beat; //!< current beat-within-bar | ||||
| int32_t tick; //!< current tick-within-beat | int32_t tick; //!< current tick-within-beat | ||||
| @@ -294,12 +296,9 @@ struct CARLA_API EngineTimeInfoBBT { | |||||
| * Engine Time information. | * Engine Time information. | ||||
| */ | */ | ||||
| struct CARLA_API EngineTimeInfo { | struct CARLA_API EngineTimeInfo { | ||||
| static const uint kValidBBT = 0x1; | |||||
| bool playing; | bool playing; | ||||
| uint64_t frame; | uint64_t frame; | ||||
| uint64_t usecs; | uint64_t usecs; | ||||
| uint valid; | |||||
| EngineTimeInfoBBT bbt; | EngineTimeInfoBBT bbt; | ||||
| /*! | /*! | ||||
| @@ -788,14 +788,7 @@ uint64_t carla_get_current_transport_frame() | |||||
| const CarlaTransportInfo* carla_get_transport_info() | const CarlaTransportInfo* carla_get_transport_info() | ||||
| { | { | ||||
| static CarlaTransportInfo retInfo; | static CarlaTransportInfo retInfo; | ||||
| // reset | |||||
| retInfo.playing = false; | |||||
| retInfo.frame = 0; | |||||
| retInfo.bar = 0; | |||||
| retInfo.beat = 0; | |||||
| retInfo.tick = 0; | |||||
| retInfo.bpm = 0.0; | |||||
| carla_zeroStruct(retInfo); | |||||
| CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr && gStandalone.engine->isRunning(), &retInfo); | CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr && gStandalone.engine->isRunning(), &retInfo); | ||||
| @@ -804,7 +797,7 @@ const CarlaTransportInfo* carla_get_transport_info() | |||||
| retInfo.playing = timeInfo.playing; | retInfo.playing = timeInfo.playing; | ||||
| retInfo.frame = timeInfo.frame; | retInfo.frame = timeInfo.frame; | ||||
| if (timeInfo.valid & CB::EngineTimeInfo::kValidBBT) | |||||
| if (timeInfo.bbt.valid) | |||||
| { | { | ||||
| retInfo.bar = timeInfo.bbt.bar; | retInfo.bar = timeInfo.bbt.bar; | ||||
| retInfo.beat = timeInfo.bbt.beat; | retInfo.beat = timeInfo.bbt.beat; | ||||
| @@ -1180,12 +1180,12 @@ protected: | |||||
| EngineTimeInfo& timeInfo(pData->timeInfo); | EngineTimeInfo& timeInfo(pData->timeInfo); | ||||
| timeInfo.playing = bridgeTimeInfo.playing; | |||||
| timeInfo.frame = bridgeTimeInfo.frame; | |||||
| timeInfo.usecs = bridgeTimeInfo.usecs; | |||||
| timeInfo.valid = bridgeTimeInfo.valid; | |||||
| timeInfo.playing = bridgeTimeInfo.playing; | |||||
| timeInfo.frame = bridgeTimeInfo.frame; | |||||
| timeInfo.usecs = bridgeTimeInfo.usecs; | |||||
| timeInfo.bbt.valid = (bridgeTimeInfo.validFlags & kPluginBridgeTimeInfoValidBBT) != 0; | |||||
| if (timeInfo.valid & EngineTimeInfo::kValidBBT) | |||||
| if (timeInfo.bbt.valid) | |||||
| { | { | ||||
| timeInfo.bbt.bar = bridgeTimeInfo.bar; | timeInfo.bbt.bar = bridgeTimeInfo.bar; | ||||
| timeInfo.bbt.beat = bridgeTimeInfo.beat; | timeInfo.bbt.beat = bridgeTimeInfo.beat; | ||||
| @@ -1,6 +1,6 @@ | |||||
| /* | /* | ||||
| * Carla Plugin Host | * Carla Plugin Host | ||||
| * Copyright (C) 2011-2014 Filipe Coelho <falktx@falktx.com> | |||||
| * Copyright (C) 2011-2017 Filipe Coelho <falktx@falktx.com> | |||||
| * | * | ||||
| * This program is free software; you can redistribute it and/or | * This program is free software; you can redistribute it and/or | ||||
| * modify it under the terms of the GNU General Public License as | * modify it under the terms of the GNU General Public License as | ||||
| @@ -308,7 +308,8 @@ EngineOptions::Wine::~Wine() noexcept | |||||
| // EngineTimeInfoBBT | // EngineTimeInfoBBT | ||||
| EngineTimeInfoBBT::EngineTimeInfoBBT() noexcept | EngineTimeInfoBBT::EngineTimeInfoBBT() noexcept | ||||
| : bar(0), | |||||
| : valid(false), | |||||
| bar(0), | |||||
| beat(0), | beat(0), | ||||
| tick(0), | tick(0), | ||||
| barStartTick(0.0), | barStartTick(0.0), | ||||
| @@ -324,7 +325,6 @@ EngineTimeInfo::EngineTimeInfo() noexcept | |||||
| : playing(false), | : playing(false), | ||||
| frame(0), | frame(0), | ||||
| usecs(0), | usecs(0), | ||||
| valid(0x0), | |||||
| bbt() {} | bbt() {} | ||||
| void EngineTimeInfo::clear() noexcept | void EngineTimeInfo::clear() noexcept | ||||
| @@ -332,14 +332,14 @@ void EngineTimeInfo::clear() noexcept | |||||
| playing = false; | playing = false; | ||||
| frame = 0; | frame = 0; | ||||
| usecs = 0; | usecs = 0; | ||||
| valid = 0x0; | |||||
| carla_zeroStruct(bbt); | |||||
| } | } | ||||
| bool EngineTimeInfo::operator==(const EngineTimeInfo& timeInfo) const noexcept | bool EngineTimeInfo::operator==(const EngineTimeInfo& timeInfo) const noexcept | ||||
| { | { | ||||
| if (timeInfo.playing != playing || timeInfo.frame != frame || timeInfo.valid != valid) | |||||
| if (timeInfo.playing != playing || timeInfo.frame != frame || timeInfo.bbt.valid != bbt.valid) | |||||
| return false; | return false; | ||||
| if ((valid & kValidBBT) == 0) | |||||
| if (! bbt.valid) | |||||
| return true; | return true; | ||||
| if (carla_isNotEqual(timeInfo.bbt.beatsPerMinute, bbt.beatsPerMinute)) | if (carla_isNotEqual(timeInfo.bbt.beatsPerMinute, bbt.beatsPerMinute)) | ||||
| return false; | return false; | ||||
| @@ -175,7 +175,7 @@ void EngineInternalTime::fillEngineTimeInfo(const uint32_t newFrames) noexcept | |||||
| if (needsReset) | if (needsReset) | ||||
| { | { | ||||
| timeInfo.valid = EngineTimeInfo::kValidBBT; | |||||
| timeInfo.bbt.valid = true; | |||||
| timeInfo.bbt.beatType = 4.0f; | timeInfo.bbt.beatType = 4.0f; | ||||
| timeInfo.bbt.ticksPerBeat = kTicksPerBeat; | timeInfo.bbt.ticksPerBeat = kTicksPerBeat; | ||||
| @@ -563,6 +563,11 @@ void CarlaEngine::ProtectedData::initTime(const char* const features) | |||||
| #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE) | #if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE) | ||||
| const bool linkEnabled = features != nullptr && std::strstr(features, ":link:") != nullptr; | const bool linkEnabled = features != nullptr && std::strstr(features, ":link:") != nullptr; | ||||
| time.enableLink(linkEnabled); | time.enableLink(linkEnabled); | ||||
| #else | |||||
| return; | |||||
| // unused | |||||
| (void)features; | |||||
| #endif | #endif | ||||
| } | } | ||||
| @@ -1393,7 +1393,7 @@ public: | |||||
| if (fClient != nullptr) | if (fClient != nullptr) | ||||
| { | { | ||||
| if ((pData->timeInfo.valid & EngineTimeInfo::kValidBBT) == 0x0) | |||||
| if (! pData->timeInfo.bbt.valid) | |||||
| { | { | ||||
| // old timebase master no longer active, make ourselves master again | // old timebase master no longer active, make ourselves master again | ||||
| pData->time.setNeedsReset(); | pData->time.setNeedsReset(); | ||||
| @@ -1573,7 +1573,7 @@ protected: | |||||
| if (jpos.valid & JackPositionBBT) | if (jpos.valid & JackPositionBBT) | ||||
| { | { | ||||
| pData->timeInfo.valid = EngineTimeInfo::kValidBBT; | |||||
| pData->timeInfo.bbt.valid = true; | |||||
| pData->timeInfo.bbt.bar = jpos.bar; | pData->timeInfo.bbt.bar = jpos.bar; | ||||
| pData->timeInfo.bbt.beat = jpos.beat; | pData->timeInfo.bbt.beat = jpos.beat; | ||||
| pData->timeInfo.bbt.tick = jpos.tick; | pData->timeInfo.bbt.tick = jpos.tick; | ||||
| @@ -1585,13 +1585,14 @@ protected: | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| pData->timeInfo.valid = 0x0; | |||||
| pData->timeInfo.bbt.valid = false; | |||||
| } | } | ||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| pData->timeInfo.frame = 0; | pData->timeInfo.frame = 0; | ||||
| pData->timeInfo.valid = 0x0; | |||||
| pData->timeInfo.usecs = 0; | |||||
| pData->timeInfo.bbt.valid = false; | |||||
| } | } | ||||
| } | } | ||||
| @@ -1471,15 +1471,13 @@ protected: | |||||
| const NativeTimeInfo* const timeInfo(pHost->get_time_info(pHost->handle)); | const NativeTimeInfo* const timeInfo(pHost->get_time_info(pHost->handle)); | ||||
| pData->timeInfo.playing = timeInfo->playing; | |||||
| pData->timeInfo.frame = timeInfo->frame; | |||||
| pData->timeInfo.usecs = timeInfo->usecs; | |||||
| pData->timeInfo.valid = 0x0; | |||||
| pData->timeInfo.playing = timeInfo->playing; | |||||
| pData->timeInfo.frame = timeInfo->frame; | |||||
| pData->timeInfo.usecs = timeInfo->usecs; | |||||
| pData->timeInfo.bbt.valid = timeInfo->bbt.valid; | |||||
| if (timeInfo->bbt.valid) | if (timeInfo->bbt.valid) | ||||
| { | { | ||||
| pData->timeInfo.valid |= EngineTimeInfo::kValidBBT; | |||||
| pData->timeInfo.bbt.bar = timeInfo->bbt.bar; | pData->timeInfo.bbt.bar = timeInfo->bbt.bar; | ||||
| pData->timeInfo.bbt.beat = timeInfo->bbt.beat; | pData->timeInfo.bbt.beat = timeInfo->bbt.beat; | ||||
| pData->timeInfo.bbt.tick = timeInfo->bbt.tick; | pData->timeInfo.bbt.tick = timeInfo->bbt.tick; | ||||
| @@ -1748,7 +1746,7 @@ protected: | |||||
| if (! fUiServer.writeMessage(timeInfo.playing ? "true\n" : "false\n")) | if (! fUiServer.writeMessage(timeInfo.playing ? "true\n" : "false\n")) | ||||
| return; | return; | ||||
| if (timeInfo.valid & EngineTimeInfo::kValidBBT) | |||||
| if (timeInfo.bbt.valid) | |||||
| { | { | ||||
| std::sprintf(tmpBuf, P_UINT64 ":%i:%i:%i\n", timeInfo.frame, timeInfo.bbt.bar, timeInfo.bbt.beat, timeInfo.bbt.tick); | std::sprintf(tmpBuf, P_UINT64 ":%i:%i:%i\n", timeInfo.frame, timeInfo.bbt.bar, timeInfo.bbt.beat, timeInfo.bbt.tick); | ||||
| if (! fUiServer.writeMessage(tmpBuf)) | if (! fUiServer.writeMessage(tmpBuf)) | ||||
| @@ -1388,12 +1388,12 @@ public: | |||||
| const EngineTimeInfo& timeInfo(pData->engine->getTimeInfo()); | const EngineTimeInfo& timeInfo(pData->engine->getTimeInfo()); | ||||
| BridgeTimeInfo& bridgeTimeInfo(fShmRtClientControl.data->timeInfo); | BridgeTimeInfo& bridgeTimeInfo(fShmRtClientControl.data->timeInfo); | ||||
| bridgeTimeInfo.playing = timeInfo.playing; | |||||
| bridgeTimeInfo.frame = timeInfo.frame; | |||||
| bridgeTimeInfo.usecs = timeInfo.usecs; | |||||
| bridgeTimeInfo.valid = timeInfo.valid; | |||||
| bridgeTimeInfo.playing = timeInfo.playing; | |||||
| bridgeTimeInfo.frame = timeInfo.frame; | |||||
| bridgeTimeInfo.usecs = timeInfo.usecs; | |||||
| bridgeTimeInfo.validFlags = timeInfo.bbt.valid ? kPluginBridgeTimeInfoValidBBT : 0x0; | |||||
| if (timeInfo.valid & EngineTimeInfo::kValidBBT) | |||||
| if (timeInfo.bbt.valid) | |||||
| { | { | ||||
| bridgeTimeInfo.bar = timeInfo.bbt.bar; | bridgeTimeInfo.bar = timeInfo.bbt.bar; | ||||
| bridgeTimeInfo.beat = timeInfo.bbt.beat; | bridgeTimeInfo.beat = timeInfo.bbt.beat; | ||||
| @@ -921,12 +921,12 @@ public: | |||||
| const EngineTimeInfo& timeInfo(pData->engine->getTimeInfo()); | const EngineTimeInfo& timeInfo(pData->engine->getTimeInfo()); | ||||
| BridgeTimeInfo& bridgeTimeInfo(fShmRtClientControl.data->timeInfo); | BridgeTimeInfo& bridgeTimeInfo(fShmRtClientControl.data->timeInfo); | ||||
| bridgeTimeInfo.playing = timeInfo.playing; | |||||
| bridgeTimeInfo.frame = timeInfo.frame; | |||||
| bridgeTimeInfo.usecs = timeInfo.usecs; | |||||
| bridgeTimeInfo.valid = timeInfo.valid; | |||||
| bridgeTimeInfo.playing = timeInfo.playing; | |||||
| bridgeTimeInfo.frame = timeInfo.frame; | |||||
| bridgeTimeInfo.usecs = timeInfo.usecs; | |||||
| bridgeTimeInfo.validFlags = timeInfo.bbt.valid ? kPluginBridgeTimeInfoValidBBT : 0x0; | |||||
| if (timeInfo.valid & EngineTimeInfo::kValidBBT) | |||||
| if (timeInfo.bbt.valid) | |||||
| { | { | ||||
| bridgeTimeInfo.bar = timeInfo.bbt.bar; | bridgeTimeInfo.bar = timeInfo.bbt.bar; | ||||
| bridgeTimeInfo.beat = timeInfo.bbt.beat; | bridgeTimeInfo.beat = timeInfo.bbt.beat; | ||||
| @@ -2824,6 +2824,7 @@ public: | |||||
| doPostRt = true; | doPostRt = true; | ||||
| } | } | ||||
| break; | break; | ||||
| case LV2_PORT_DESIGNATION_TIME_FRAME: | case LV2_PORT_DESIGNATION_TIME_FRAME: | ||||
| if (fLastTimeInfo.frame != timeInfo.frame) | if (fLastTimeInfo.frame != timeInfo.frame) | ||||
| { | { | ||||
| @@ -2831,55 +2832,62 @@ public: | |||||
| doPostRt = true; | doPostRt = true; | ||||
| } | } | ||||
| break; | break; | ||||
| case LV2_PORT_DESIGNATION_TIME_FRAMES_PER_SECOND: | case LV2_PORT_DESIGNATION_TIME_FRAMES_PER_SECOND: | ||||
| break; | break; | ||||
| // BBT | // BBT | ||||
| case LV2_PORT_DESIGNATION_TIME_BAR: | case LV2_PORT_DESIGNATION_TIME_BAR: | ||||
| if ((timeInfo.valid & EngineTimeInfo::kValidBBT) != 0 && fLastTimeInfo.bbt.bar != timeInfo.bbt.bar) | |||||
| if (timeInfo.bbt.valid && fLastTimeInfo.bbt.bar != timeInfo.bbt.bar) | |||||
| { | { | ||||
| fParamBuffers[k] = static_cast<float>(timeInfo.bbt.bar - 1); | fParamBuffers[k] = static_cast<float>(timeInfo.bbt.bar - 1); | ||||
| doPostRt = true; | doPostRt = true; | ||||
| } | } | ||||
| break; | break; | ||||
| case LV2_PORT_DESIGNATION_TIME_BAR_BEAT: | case LV2_PORT_DESIGNATION_TIME_BAR_BEAT: | ||||
| if ((timeInfo.valid & EngineTimeInfo::kValidBBT) != 0 && (fLastTimeInfo.bbt.tick != timeInfo.bbt.tick || | |||||
| fLastTimeInfo.bbt.beat != timeInfo.bbt.beat)) | |||||
| if (timeInfo.bbt.valid && (fLastTimeInfo.bbt.tick != timeInfo.bbt.tick || | |||||
| fLastTimeInfo.bbt.beat != timeInfo.bbt.beat)) | |||||
| { | { | ||||
| fParamBuffers[k] = static_cast<float>(static_cast<double>(timeInfo.bbt.beat) - 1.0 + (static_cast<double>(timeInfo.bbt.tick) / timeInfo.bbt.ticksPerBeat)); | |||||
| fParamBuffers[k] = static_cast<float>(static_cast<double>(timeInfo.bbt.beat) - 1.0 | |||||
| + (static_cast<double>(timeInfo.bbt.tick) / timeInfo.bbt.ticksPerBeat)); | |||||
| doPostRt = true; | doPostRt = true; | ||||
| } | } | ||||
| break; | break; | ||||
| case LV2_PORT_DESIGNATION_TIME_BEAT: | case LV2_PORT_DESIGNATION_TIME_BEAT: | ||||
| if ((timeInfo.valid & EngineTimeInfo::kValidBBT) != 0 && fLastTimeInfo.bbt.beat != timeInfo.bbt.beat) | |||||
| if (timeInfo.bbt.valid && fLastTimeInfo.bbt.beat != timeInfo.bbt.beat) | |||||
| { | { | ||||
| fParamBuffers[k] = static_cast<float>(timeInfo.bbt.beat - 1); | fParamBuffers[k] = static_cast<float>(timeInfo.bbt.beat - 1); | ||||
| doPostRt = true; | doPostRt = true; | ||||
| } | } | ||||
| break; | break; | ||||
| case LV2_PORT_DESIGNATION_TIME_BEAT_UNIT: | case LV2_PORT_DESIGNATION_TIME_BEAT_UNIT: | ||||
| if ((timeInfo.valid & EngineTimeInfo::kValidBBT) != 0 && carla_isNotEqual(fLastTimeInfo.bbt.beatType, timeInfo.bbt.beatType)) | |||||
| if (timeInfo.bbt.valid && carla_isNotEqual(fLastTimeInfo.bbt.beatType, timeInfo.bbt.beatType)) | |||||
| { | { | ||||
| fParamBuffers[k] = timeInfo.bbt.beatType; | fParamBuffers[k] = timeInfo.bbt.beatType; | ||||
| doPostRt = true; | doPostRt = true; | ||||
| } | } | ||||
| break; | break; | ||||
| case LV2_PORT_DESIGNATION_TIME_BEATS_PER_BAR: | case LV2_PORT_DESIGNATION_TIME_BEATS_PER_BAR: | ||||
| if ((timeInfo.valid & EngineTimeInfo::kValidBBT) != 0 && carla_isNotEqual(fLastTimeInfo.bbt.beatsPerBar, timeInfo.bbt.beatsPerBar)) | |||||
| if (timeInfo.bbt.valid && carla_isNotEqual(fLastTimeInfo.bbt.beatsPerBar, timeInfo.bbt.beatsPerBar)) | |||||
| { | { | ||||
| fParamBuffers[k] = timeInfo.bbt.beatsPerBar; | fParamBuffers[k] = timeInfo.bbt.beatsPerBar; | ||||
| doPostRt = true; | doPostRt = true; | ||||
| } | } | ||||
| break; | break; | ||||
| case LV2_PORT_DESIGNATION_TIME_BEATS_PER_MINUTE: | case LV2_PORT_DESIGNATION_TIME_BEATS_PER_MINUTE: | ||||
| if ((timeInfo.valid & EngineTimeInfo::kValidBBT) != 0 && carla_isNotEqual(fLastTimeInfo.bbt.beatsPerMinute, timeInfo.bbt.beatsPerMinute)) | |||||
| if (timeInfo.bbt.valid && carla_isNotEqual(fLastTimeInfo.bbt.beatsPerMinute, timeInfo.bbt.beatsPerMinute)) | |||||
| { | { | ||||
| fParamBuffers[k] = static_cast<float>(timeInfo.bbt.beatsPerMinute); | fParamBuffers[k] = static_cast<float>(timeInfo.bbt.beatsPerMinute); | ||||
| doPostRt = true; | doPostRt = true; | ||||
| } | } | ||||
| break; | break; | ||||
| case LV2_PORT_DESIGNATION_TIME_TICKS_PER_BEAT: | case LV2_PORT_DESIGNATION_TIME_TICKS_PER_BEAT: | ||||
| if ((timeInfo.valid & EngineTimeInfo::kValidBBT) != 0 && carla_isNotEqual(fLastTimeInfo.bbt.ticksPerBeat, timeInfo.bbt.ticksPerBeat)) | |||||
| if (timeInfo.bbt.valid && carla_isNotEqual(fLastTimeInfo.bbt.ticksPerBeat, timeInfo.bbt.ticksPerBeat)) | |||||
| { | { | ||||
| fParamBuffers[k] = static_cast<float>(timeInfo.bbt.ticksPerBeat); | fParamBuffers[k] = static_cast<float>(timeInfo.bbt.ticksPerBeat); | ||||
| doPostRt = true; | doPostRt = true; | ||||
| @@ -2908,7 +2916,7 @@ public: | |||||
| lv2_atom_forge_key(&fAtomForge, kUridTimeFrame); | lv2_atom_forge_key(&fAtomForge, kUridTimeFrame); | ||||
| lv2_atom_forge_long(&fAtomForge, static_cast<int64_t>(timeInfo.frame)); | lv2_atom_forge_long(&fAtomForge, static_cast<int64_t>(timeInfo.frame)); | ||||
| if (timeInfo.valid & EngineTimeInfo::kValidBBT) | |||||
| if (timeInfo.bbt.valid) | |||||
| { | { | ||||
| lv2_atom_forge_key(&fAtomForge, kUridTimeBar); | lv2_atom_forge_key(&fAtomForge, kUridTimeBar); | ||||
| lv2_atom_forge_long(&fAtomForge, timeInfo.bbt.bar - 1); | lv2_atom_forge_long(&fAtomForge, timeInfo.bbt.bar - 1); | ||||
| @@ -1383,7 +1383,7 @@ public: | |||||
| fTimeInfo.frame = timeInfo.frame; | fTimeInfo.frame = timeInfo.frame; | ||||
| fTimeInfo.usecs = timeInfo.usecs; | fTimeInfo.usecs = timeInfo.usecs; | ||||
| if (timeInfo.valid & EngineTimeInfo::kValidBBT) | |||||
| if (timeInfo.bbt.valid) | |||||
| { | { | ||||
| fTimeInfo.bbt.valid = true; | fTimeInfo.bbt.valid = true; | ||||
| @@ -1399,7 +1399,9 @@ public: | |||||
| fTimeInfo.bbt.beatsPerMinute = timeInfo.bbt.beatsPerMinute; | fTimeInfo.bbt.beatsPerMinute = timeInfo.bbt.beatsPerMinute; | ||||
| } | } | ||||
| else | else | ||||
| { | |||||
| fTimeInfo.bbt.valid = false; | fTimeInfo.bbt.valid = false; | ||||
| } | |||||
| // -------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------- | ||||
| // Event Input and Processing | // Event Input and Processing | ||||
| @@ -1092,7 +1092,7 @@ public: | |||||
| fTimeInfo.flags |= kVstNanosValid; | fTimeInfo.flags |= kVstNanosValid; | ||||
| } | } | ||||
| if (timeInfo.valid & EngineTimeInfo::kValidBBT) | |||||
| if (timeInfo.bbt.valid) | |||||
| { | { | ||||
| CARLA_SAFE_ASSERT_INT(timeInfo.bbt.bar > 0, timeInfo.bbt.bar); | CARLA_SAFE_ASSERT_INT(timeInfo.bbt.bar > 0, timeInfo.bbt.bar); | ||||
| CARLA_SAFE_ASSERT_INT(timeInfo.bbt.beat > 0, timeInfo.bbt.beat); | CARLA_SAFE_ASSERT_INT(timeInfo.bbt.beat > 0, timeInfo.bbt.beat); | ||||
| @@ -29,12 +29,12 @@ | |||||
| #include "water/files/File.h" | #include "water/files/File.h" | ||||
| // --------------------------------------------------------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| CARLA_BACKEND_START_NAMESPACE | CARLA_BACKEND_START_NAMESPACE | ||||
| class CarlaEngineLV2Single : public CarlaEngine, | class CarlaEngineLV2Single : public CarlaEngine, | ||||
| public Lv2PluginBaseClass | |||||
| public Lv2PluginBaseClass<EngineTimeInfo> | |||||
| { | { | ||||
| public: | public: | ||||
| CarlaEngineLV2Single(const double sampleRate, | CarlaEngineLV2Single(const double sampleRate, | ||||
| @@ -42,10 +42,8 @@ public: | |||||
| const LV2_Feature* const* const features) | const LV2_Feature* const* const features) | ||||
| : Lv2PluginBaseClass(sampleRate, features), | : Lv2PluginBaseClass(sampleRate, features), | ||||
| fPlugin(nullptr), | fPlugin(nullptr), | ||||
| fIsActive(false), | |||||
| fIsOffline(false), | |||||
| fPorts(), | |||||
| fUI() | |||||
| fUiName(), | |||||
| fPorts() | |||||
| { | { | ||||
| CARLA_SAFE_ASSERT_RETURN(pData->curPluginCount == 0,) | CARLA_SAFE_ASSERT_RETURN(pData->curPluginCount == 0,) | ||||
| CARLA_SAFE_ASSERT_RETURN(pData->plugins[0].plugin == nullptr,); | CARLA_SAFE_ASSERT_RETURN(pData->plugins[0].plugin == nullptr,); | ||||
| @@ -112,7 +110,7 @@ public: | |||||
| return fPlugin != nullptr; | return fPlugin != nullptr; | ||||
| } | } | ||||
| // ----------------------------------------------------------------------------------------------------------------- | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| // LV2 functions | // LV2 functions | ||||
| void lv2_connect_port(const uint32_t port, void* const dataLocation) noexcept | void lv2_connect_port(const uint32_t port, void* const dataLocation) noexcept | ||||
| @@ -124,6 +122,8 @@ public: | |||||
| { | { | ||||
| CARLA_SAFE_ASSERT_RETURN(! fIsActive,); | CARLA_SAFE_ASSERT_RETURN(! fIsActive,); | ||||
| resetTimeInfo(); | |||||
| fPlugin->setActive(true, false, false); | fPlugin->setActive(true, false, false); | ||||
| fIsActive = true; | fIsActive = true; | ||||
| } | } | ||||
| @@ -138,6 +138,9 @@ public: | |||||
| void lv2_run(const uint32_t frames) | void lv2_run(const uint32_t frames) | ||||
| { | { | ||||
| if (! lv2_pre_run(frames)) | |||||
| return; | |||||
| fIsOffline = (fPorts.freewheel != nullptr && *fPorts.freewheel >= 0.5f); | fIsOffline = (fPorts.freewheel != nullptr && *fPorts.freewheel >= 0.5f); | ||||
| // Check for updated parameters | // Check for updated parameters | ||||
| @@ -174,10 +177,11 @@ public: | |||||
| carla_zeroFloats(fPorts.audioOuts[i], frames); | carla_zeroFloats(fPorts.audioOuts[i], frames); | ||||
| } | } | ||||
| lv2_post_run(frames); | |||||
| fPorts.updateOutputs(); | fPorts.updateOutputs(); | ||||
| } | } | ||||
| // ----------------------------------------------------------------------------------------------------------------- | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| void lv2ui_instantiate(LV2UI_Write_Function writeFunction, LV2UI_Controller controller, | void lv2ui_instantiate(LV2UI_Write_Function writeFunction, LV2UI_Controller controller, | ||||
| LV2UI_Widget* widget, const LV2_Feature* const* features) | LV2UI_Widget* widget, const LV2_Feature* const* features) | ||||
| @@ -185,9 +189,11 @@ public: | |||||
| fUI.writeFunction = writeFunction; | fUI.writeFunction = writeFunction; | ||||
| fUI.controller = controller; | fUI.controller = controller; | ||||
| fUiName.clear(); | |||||
| const LV2_URID_Map* uridMap = nullptr; | const LV2_URID_Map* uridMap = nullptr; | ||||
| // ------------------------------------------------------------------------------------------------------------- | |||||
| // ------------------------------------------------------------------------------------------------------------ | |||||
| // see if the host supports external-ui, get uridMap | // see if the host supports external-ui, get uridMap | ||||
| for (int i=0; features[i] != nullptr; ++i) | for (int i=0; features[i] != nullptr; ++i) | ||||
| @@ -205,12 +211,12 @@ public: | |||||
| if (fUI.host != nullptr) | if (fUI.host != nullptr) | ||||
| { | { | ||||
| fUI.name = fUI.host->plugin_human_id; | |||||
| *widget = (LV2_External_UI_Widget*)this; | |||||
| fUiName = fUI.host->plugin_human_id; | |||||
| *widget = (LV2_External_UI_Widget*)this; | |||||
| return; | return; | ||||
| } | } | ||||
| // ------------------------------------------------------------------------------------------------------------- | |||||
| // ------------------------------------------------------------------------------------------------------------ | |||||
| // no external-ui support, use showInterface | // no external-ui support, use showInterface | ||||
| for (int i=0; features[i] != nullptr; ++i) | for (int i=0; features[i] != nullptr; ++i) | ||||
| @@ -223,7 +229,7 @@ public: | |||||
| { | { | ||||
| if (options[j].key == uridMap->map(uridMap->handle, LV2_UI__windowTitle)) | if (options[j].key == uridMap->map(uridMap->handle, LV2_UI__windowTitle)) | ||||
| { | { | ||||
| fUI.name = (const char*)options[j].value; | |||||
| fUiName = (const char*)options[j].value; | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| @@ -231,8 +237,8 @@ public: | |||||
| } | } | ||||
| } | } | ||||
| if (fUI.name.isEmpty()) | |||||
| fUI.name = fPlugin->getName(); | |||||
| if (fUiName.isEmpty()) | |||||
| fUiName = fPlugin->getName(); | |||||
| *widget = nullptr; | *widget = nullptr; | ||||
| } | } | ||||
| @@ -241,48 +247,15 @@ public: | |||||
| { | { | ||||
| if (format != 0 || bufferSize != sizeof(float) || buffer == nullptr) | if (format != 0 || bufferSize != sizeof(float) || buffer == nullptr) | ||||
| return; | return; | ||||
| if (portIndex >= fPorts.indexOffset || ! fUI.visible) | |||||
| if (portIndex >= fPorts.indexOffset || ! fUI.isVisible) | |||||
| return; | return; | ||||
| const float value(*(const float*)buffer); | const float value(*(const float*)buffer); | ||||
| fPlugin->uiParameterChange(portIndex-fPorts.indexOffset, value); | fPlugin->uiParameterChange(portIndex-fPorts.indexOffset, value); | ||||
| } | } | ||||
| void lv2ui_cleanup() | |||||
| { | |||||
| if (fUI.visible) | |||||
| handleUiHide(); | |||||
| fUI.writeFunction = nullptr; | |||||
| fUI.controller = nullptr; | |||||
| fUI.host = nullptr; | |||||
| } | |||||
| // ----------------------------------------------------------------------------------------------------------------- | |||||
| int lv2ui_idle() const | |||||
| { | |||||
| if (! fUI.visible) | |||||
| return 1; | |||||
| handleUiRun(); | |||||
| return 0; | |||||
| } | |||||
| int lv2ui_show() | |||||
| { | |||||
| handleUiShow(); | |||||
| return 0; | |||||
| } | |||||
| int lv2ui_hide() | |||||
| { | |||||
| handleUiHide(); | |||||
| return 0; | |||||
| } | |||||
| protected: | protected: | ||||
| // ----------------------------------------------------------------------------------------------------------------- | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| // CarlaEngine virtual calls | // CarlaEngine virtual calls | ||||
| bool init(const char* const clientName) override | bool init(const char* const clientName) override | ||||
| @@ -330,7 +303,7 @@ protected: | |||||
| { | { | ||||
| case ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED: | case ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED: | ||||
| CARLA_SAFE_ASSERT_RETURN(value1 >= 0,); | CARLA_SAFE_ASSERT_RETURN(value1 >= 0,); | ||||
| if (fUI.writeFunction != nullptr && fUI.controller != nullptr && fUI.visible) | |||||
| if (fUI.writeFunction != nullptr && fUI.controller != nullptr && fUI.isVisible) | |||||
| { | { | ||||
| fUI.writeFunction(fUI.controller, | fUI.writeFunction(fUI.controller, | ||||
| static_cast<uint32_t>(value1)+fPorts.indexOffset, | static_cast<uint32_t>(value1)+fPorts.indexOffset, | ||||
| @@ -339,7 +312,7 @@ protected: | |||||
| break; | break; | ||||
| case ENGINE_CALLBACK_UI_STATE_CHANGED: | case ENGINE_CALLBACK_UI_STATE_CHANGED: | ||||
| fUI.visible = value1 == 1; | |||||
| fUI.isVisible = (value1 == 1); | |||||
| if (fUI.host != nullptr) | if (fUI.host != nullptr) | ||||
| fUI.host->ui_closed(fUI.controller); | fUI.host->ui_closed(fUI.controller); | ||||
| break; | break; | ||||
| @@ -354,7 +327,7 @@ protected: | |||||
| } | } | ||||
| } | } | ||||
| // ----------------------------------------------------------------------------------------------------------------- | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| void handleUiRun() const override | void handleUiRun() const override | ||||
| { | { | ||||
| @@ -366,23 +339,32 @@ protected: | |||||
| void handleUiShow() override | void handleUiShow() override | ||||
| { | { | ||||
| fPlugin->showCustomUI(true); | fPlugin->showCustomUI(true); | ||||
| fUI.visible = true; | |||||
| fUI.isVisible = true; | |||||
| } | } | ||||
| void handleUiHide() override | void handleUiHide() override | ||||
| { | { | ||||
| fUI.visible = false; | |||||
| fUI.isVisible = false; | |||||
| fPlugin->showCustomUI(false); | fPlugin->showCustomUI(false); | ||||
| } | } | ||||
| // ----------------------------------------------------------------------------------------------------------------- | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| void handleBufferSizeChanged(const uint32_t bufferSize) override | |||||
| { | |||||
| CarlaEngine::bufferSizeChanged(bufferSize); | |||||
| } | |||||
| void handleSampleRateChanged(const double sampleRate) override | |||||
| { | |||||
| CarlaEngine::sampleRateChanged(sampleRate); | |||||
| } | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| private: | private: | ||||
| CarlaPlugin* fPlugin; | CarlaPlugin* fPlugin; | ||||
| // Lv2 host data | |||||
| bool fIsActive; | |||||
| bool fIsOffline; | |||||
| CarlaString fUiName; | |||||
| struct Ports { | struct Ports { | ||||
| uint32_t numAudioIns; | uint32_t numAudioIns; | ||||
| @@ -535,22 +517,6 @@ private: | |||||
| CARLA_DECLARE_NON_COPY_STRUCT(Ports); | CARLA_DECLARE_NON_COPY_STRUCT(Ports); | ||||
| } fPorts; | } fPorts; | ||||
| struct UI { | |||||
| LV2UI_Write_Function writeFunction; | |||||
| LV2UI_Controller controller; | |||||
| const LV2_External_UI_Host* host; | |||||
| CarlaString name; | |||||
| bool visible; | |||||
| UI() | |||||
| : writeFunction(nullptr), | |||||
| controller(nullptr), | |||||
| host(nullptr), | |||||
| name(), | |||||
| visible(false) {} | |||||
| CARLA_DECLARE_NON_COPY_STRUCT(UI) | |||||
| } fUI; | |||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| #define handlePtr ((CarlaEngineLV2Single*)handle) | #define handlePtr ((CarlaEngineLV2Single*)handle) | ||||
| @@ -569,7 +535,7 @@ CARLA_BACKEND_END_NAMESPACE | |||||
| using CarlaBackend::CarlaEngineLV2Single; | using CarlaBackend::CarlaEngineLV2Single; | ||||
| // --------------------------------------------------------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // LV2 DSP functions | // LV2 DSP functions | ||||
| static LV2_Handle lv2_instantiate(const LV2_Descriptor* lv2Descriptor, double sampleRate, const char* bundlePath, const LV2_Feature* const* features) | static LV2_Handle lv2_instantiate(const LV2_Descriptor* lv2Descriptor, double sampleRate, const char* bundlePath, const LV2_Feature* const* features) | ||||
| @@ -624,7 +590,7 @@ static const void* lv2_extension_data(const char* uri) | |||||
| #undef instancePtr | #undef instancePtr | ||||
| // --------------------------------------------------------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // LV2 UI functions | // LV2 UI functions | ||||
| static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char*, const char*, | static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char*, const char*, | ||||
| @@ -703,7 +669,7 @@ static const void* lv2ui_extension_data(const char* uri) | |||||
| #undef uiPtr | #undef uiPtr | ||||
| // --------------------------------------------------------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // Startup code | // Startup code | ||||
| CARLA_EXPORT | CARLA_EXPORT | ||||
| @@ -762,4 +728,4 @@ const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) | |||||
| return (index == 0) ? &lv2UiExtDesc : nullptr; | return (index == 0) ? &lv2UiExtDesc : nullptr; | ||||
| } | } | ||||
| // --------------------------------------------------------------------------------------------------------------------- | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| @@ -596,7 +596,7 @@ bool CarlaJackAppClient::handleRtData() | |||||
| fServer.position.frame = bridgeTimeInfo.frame; | fServer.position.frame = bridgeTimeInfo.frame; | ||||
| fServer.position.usecs = bridgeTimeInfo.usecs; | fServer.position.usecs = bridgeTimeInfo.usecs; | ||||
| if (bridgeTimeInfo.valid & 0x1 /* kValidBBT */) | |||||
| if (bridgeTimeInfo.validFlags & kPluginBridgeTimeInfoValidBBT) | |||||
| { | { | ||||
| fServer.position.valid = JackPositionBBT; | fServer.position.valid = JackPositionBBT; | ||||
| @@ -613,7 +613,7 @@ bool CarlaJackAppClient::handleRtData() | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| fServer.position.valid = static_cast<jack_position_bits_t>(0); | |||||
| fServer.position.valid = static_cast<jack_position_bits_t>(0x0); | |||||
| } | } | ||||
| int numClientOutputsProcessed = 0; | int numClientOutputsProcessed = 0; | ||||
| @@ -25,7 +25,7 @@ | |||||
| // ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
| // LV2 descriptor functions | // LV2 descriptor functions | ||||
| class NativePlugin : public Lv2PluginBaseClass | |||||
| class NativePlugin : public Lv2PluginBaseClass<NativeTimeInfo> | |||||
| { | { | ||||
| public: | public: | ||||
| static const uint32_t kMaxMidiEvents = 512; | static const uint32_t kMaxMidiEvents = 512; | ||||
| @@ -42,9 +42,6 @@ public: | |||||
| fProgramDesc({0, 0, nullptr}), | fProgramDesc({0, 0, nullptr}), | ||||
| #endif | #endif | ||||
| fMidiEventCount(0), | fMidiEventCount(0), | ||||
| fTimeInfo(), | |||||
| fLastPositionData(), | |||||
| fUI(), | |||||
| fPorts() | fPorts() | ||||
| { | { | ||||
| carla_zeroStruct(fHost); | carla_zeroStruct(fHost); | ||||
| @@ -82,6 +79,12 @@ public: | |||||
| delete[] fHost.resourceDir; | delete[] fHost.resourceDir; | ||||
| fHost.resourceDir = nullptr; | fHost.resourceDir = nullptr; | ||||
| } | } | ||||
| if (fHost.uiName != nullptr) | |||||
| { | |||||
| delete[] fHost.uiName; | |||||
| fHost.uiName = nullptr; | |||||
| } | |||||
| } | } | ||||
| bool init() | bool init() | ||||
| @@ -96,7 +99,6 @@ public: | |||||
| } | } | ||||
| carla_zeroStructs(fMidiEvents, kMaxMidiEvents); | carla_zeroStructs(fMidiEvents, kMaxMidiEvents); | ||||
| carla_zeroStruct(fTimeInfo); | |||||
| fHandle = fDescriptor->instantiate(&fHost); | fHandle = fDescriptor->instantiate(&fHost); | ||||
| CARLA_SAFE_ASSERT_RETURN(fHandle != nullptr, false); | CARLA_SAFE_ASSERT_RETURN(fHandle != nullptr, false); | ||||
| @@ -128,17 +130,7 @@ public: | |||||
| if (fDescriptor->activate != nullptr) | if (fDescriptor->activate != nullptr) | ||||
| fDescriptor->activate(fHandle); | fDescriptor->activate(fHandle); | ||||
| carla_zeroStruct(fTimeInfo); | |||||
| // hosts may not send all values, resulting on some invalid data | |||||
| fTimeInfo.bbt.bar = 1; | |||||
| fTimeInfo.bbt.beat = 1; | |||||
| fTimeInfo.bbt.tick = 0; | |||||
| fTimeInfo.bbt.barStartTick = 0; | |||||
| fTimeInfo.bbt.beatsPerBar = 4; | |||||
| fTimeInfo.bbt.beatType = 4; | |||||
| fTimeInfo.bbt.ticksPerBeat = 960.0; | |||||
| fTimeInfo.bbt.beatsPerMinute = 120.0; | |||||
| resetTimeInfo(); | |||||
| } | } | ||||
| void lv2_deactivate() | void lv2_deactivate() | ||||
| @@ -157,6 +149,9 @@ public: | |||||
| void lv2_run(const uint32_t frames) | void lv2_run(const uint32_t frames) | ||||
| { | { | ||||
| if (! lv2_pre_run(frames)) | |||||
| return; | |||||
| fIsOffline = (fPorts.freewheel != nullptr && *fPorts.freewheel >= 0.5f); | fIsOffline = (fPorts.freewheel != nullptr && *fPorts.freewheel >= 0.5f); | ||||
| // cache midi events and time information first | // cache midi events and time information first | ||||
| @@ -484,130 +479,12 @@ public: | |||||
| const_cast<float**>(fPorts.audioIns), fPorts.audioOuts, frames, | const_cast<float**>(fPorts.audioIns), fPorts.audioOuts, frames, | ||||
| fMidiEvents, fMidiEventCount); | fMidiEvents, fMidiEventCount); | ||||
| // update timePos for next callback | |||||
| if (carla_isNotZero(fLastPositionData.speed)) | |||||
| { | |||||
| if (fLastPositionData.speed > 0.0) | |||||
| { | |||||
| // playing forwards | |||||
| fLastPositionData.frame += frames; | |||||
| } | |||||
| else | |||||
| { | |||||
| // playing backwards | |||||
| if (frames >= fLastPositionData.frame) | |||||
| fLastPositionData.frame = 0; | |||||
| else | |||||
| fLastPositionData.frame -= frames; | |||||
| } | |||||
| fTimeInfo.frame = fLastPositionData.frame; | |||||
| if (fTimeInfo.bbt.valid) | |||||
| { | |||||
| const double beatsPerMinute = fLastPositionData.beatsPerMinute * fLastPositionData.speed; | |||||
| const double framesPerBeat = 60.0 * fSampleRate / beatsPerMinute; | |||||
| const double addedBarBeats = double(frames) / framesPerBeat; | |||||
| if (fLastPositionData.barBeat >= 0.0f) | |||||
| { | |||||
| fLastPositionData.barBeat = std::fmod(fLastPositionData.barBeat+static_cast<float>(addedBarBeats), | |||||
| fLastPositionData.beatsPerBar); | |||||
| const double rest = std::fmod(fLastPositionData.barBeat, 1.0f); | |||||
| fTimeInfo.bbt.beat = static_cast<int32_t>(fLastPositionData.barBeat-rest+1.0); | |||||
| fTimeInfo.bbt.tick = static_cast<int32_t>(rest*fTimeInfo.bbt.ticksPerBeat+0.5); | |||||
| if (fLastPositionData.bar_f >= 0.0f) | |||||
| { | |||||
| fLastPositionData.bar_f += std::floor((fLastPositionData.barBeat+static_cast<float>(addedBarBeats))/ | |||||
| fLastPositionData.beatsPerBar); | |||||
| if (fLastPositionData.bar_f <= 0.0f) | |||||
| { | |||||
| fLastPositionData.bar = 0; | |||||
| fLastPositionData.bar_f = 0.0f; | |||||
| } | |||||
| else | |||||
| { | |||||
| fLastPositionData.bar = static_cast<int32_t>(fLastPositionData.bar_f+0.5f); | |||||
| } | |||||
| fTimeInfo.bbt.bar = fLastPositionData.bar + 1; | |||||
| fTimeInfo.bbt.barStartTick = fTimeInfo.bbt.ticksPerBeat* | |||||
| fTimeInfo.bbt.beatsPerBar* | |||||
| (fTimeInfo.bbt.bar-1); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| lv2_post_run(frames); | |||||
| updateParameterOutputs(); | updateParameterOutputs(); | ||||
| } | } | ||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| uint32_t lv2_get_options(LV2_Options_Option* const /*options*/) const | |||||
| { | |||||
| // currently unused | |||||
| return LV2_OPTIONS_SUCCESS; | |||||
| } | |||||
| uint32_t lv2_set_options(const LV2_Options_Option* const options) | |||||
| { | |||||
| for (int i=0; options[i].key != 0; ++i) | |||||
| { | |||||
| if (options[i].key == fUridMap->map(fUridMap->handle, LV2_BUF_SIZE__nominalBlockLength)) | |||||
| { | |||||
| if (options[i].type == fURIs.atomInt) | |||||
| { | |||||
| const int value(*(const int*)options[i].value); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(value > 0); | |||||
| fBufferSize = static_cast<uint32_t>(value); | |||||
| if (fDescriptor->dispatcher != nullptr) | |||||
| fDescriptor->dispatcher(fHandle, NATIVE_PLUGIN_OPCODE_BUFFER_SIZE_CHANGED, 0, value, nullptr, 0.0f); | |||||
| } | |||||
| else | |||||
| carla_stderr("Host changed nominalBlockLength but with wrong value type"); | |||||
| } | |||||
| else if (options[i].key == fUridMap->map(fUridMap->handle, LV2_BUF_SIZE__maxBlockLength) && ! fUsingNominal) | |||||
| { | |||||
| if (options[i].type == fURIs.atomInt) | |||||
| { | |||||
| const int value(*(const int*)options[i].value); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(value > 0); | |||||
| fBufferSize = static_cast<uint32_t>(value); | |||||
| if (fDescriptor->dispatcher != nullptr) | |||||
| fDescriptor->dispatcher(fHandle, NATIVE_PLUGIN_OPCODE_BUFFER_SIZE_CHANGED, 0, value, nullptr, 0.0f); | |||||
| } | |||||
| else | |||||
| carla_stderr("Host changed maxBlockLength but with wrong value type"); | |||||
| } | |||||
| else if (options[i].key == fUridMap->map(fUridMap->handle, LV2_CORE__sampleRate)) | |||||
| { | |||||
| if (options[i].type == fURIs.atomDouble) | |||||
| { | |||||
| const double value(*(const double*)options[i].value); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(value > 0.0); | |||||
| fSampleRate = value; | |||||
| if (fDescriptor->dispatcher != nullptr) | |||||
| fDescriptor->dispatcher(fHandle, NATIVE_PLUGIN_OPCODE_SAMPLE_RATE_CHANGED, 0, 0, nullptr, (float)fSampleRate); | |||||
| } | |||||
| else | |||||
| carla_stderr("Host changed sampleRate but with wrong value type"); | |||||
| } | |||||
| } | |||||
| return LV2_OPTIONS_SUCCESS; | |||||
| } | |||||
| const LV2_Program_Descriptor* lv2_get_program(const uint32_t index) | const LV2_Program_Descriptor* lv2_get_program(const uint32_t index) | ||||
| { | { | ||||
| if (fDescriptor->category == NATIVE_PLUGIN_CATEGORY_SYNTH) | if (fDescriptor->category == NATIVE_PLUGIN_CATEGORY_SYNTH) | ||||
| @@ -696,6 +573,12 @@ public: | |||||
| fUI.controller = controller; | fUI.controller = controller; | ||||
| fUI.isEmbed = isEmbed; | fUI.isEmbed = isEmbed; | ||||
| if (fHost.uiName != nullptr) | |||||
| { | |||||
| delete[] fHost.uiName; | |||||
| fHost.uiName = nullptr; | |||||
| } | |||||
| #ifdef CARLA_OS_LINUX | #ifdef CARLA_OS_LINUX | ||||
| // --------------------------------------------------------------- | // --------------------------------------------------------------- | ||||
| // show embed UI if needed | // show embed UI if needed | ||||
| @@ -810,22 +693,6 @@ public: | |||||
| fDescriptor->ui_set_parameter_value(fHandle, portIndex-fUI.portOffset, value); | fDescriptor->ui_set_parameter_value(fHandle, portIndex-fUI.portOffset, value); | ||||
| } | } | ||||
| void lv2ui_cleanup() | |||||
| { | |||||
| if (fUI.isVisible) | |||||
| handleUiHide(); | |||||
| fUI.host = nullptr; | |||||
| fUI.writeFunction = nullptr; | |||||
| fUI.controller = nullptr; | |||||
| if (fHost.uiName != nullptr) | |||||
| { | |||||
| delete[] fHost.uiName; | |||||
| fHost.uiName = nullptr; | |||||
| } | |||||
| } | |||||
| // ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||||
| void lv2ui_select_program(uint32_t bank, uint32_t program) const | void lv2ui_select_program(uint32_t bank, uint32_t program) const | ||||
| @@ -838,30 +705,7 @@ public: | |||||
| fDescriptor->ui_set_midi_program(fHandle, 0, bank, program); | fDescriptor->ui_set_midi_program(fHandle, 0, bank, program); | ||||
| } | } | ||||
| // ------------------------------------------------------------------- | |||||
| int lv2ui_idle() const | |||||
| { | |||||
| if (! fUI.isVisible) | |||||
| return 1; | |||||
| handleUiRun(); | |||||
| return 0; | |||||
| } | |||||
| int lv2ui_show() | |||||
| { | |||||
| handleUiShow(); | |||||
| return 0; | |||||
| } | |||||
| int lv2ui_hide() | |||||
| { | |||||
| handleUiHide(); | |||||
| return 0; | |||||
| } | |||||
| // ------------------------------------------------------------------- | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| protected: | protected: | ||||
| void handleUiRun() const override | void handleUiRun() const override | ||||
| @@ -886,7 +730,25 @@ protected: | |||||
| fUI.isVisible = false; | fUI.isVisible = false; | ||||
| } | } | ||||
| // ------------------------------------------------------------------- | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| void handleBufferSizeChanged(const uint32_t bufferSize) override | |||||
| { | |||||
| if (fDescriptor->dispatcher == nullptr) | |||||
| return; | |||||
| fDescriptor->dispatcher(fHandle, NATIVE_PLUGIN_OPCODE_BUFFER_SIZE_CHANGED, 0, bufferSize, nullptr, 0.0f); | |||||
| } | |||||
| void handleSampleRateChanged(const double sampleRate) override | |||||
| { | |||||
| if (fDescriptor->dispatcher == nullptr) | |||||
| return; | |||||
| fDescriptor->dispatcher(fHandle, NATIVE_PLUGIN_OPCODE_SAMPLE_RATE_CHANGED, 0, 0, nullptr, (float)sampleRate); | |||||
| } | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| bool handleWriteMidiEvent(const NativeMidiEvent* const event) | bool handleWriteMidiEvent(const NativeMidiEvent* const event) | ||||
| { | { | ||||
| @@ -1010,48 +872,6 @@ private: | |||||
| uint32_t fMidiEventCount; | uint32_t fMidiEventCount; | ||||
| NativeMidiEvent fMidiEvents[kMaxMidiEvents]; | NativeMidiEvent fMidiEvents[kMaxMidiEvents]; | ||||
| NativeTimeInfo fTimeInfo; | |||||
| struct Lv2PositionData { | |||||
| int32_t bar; | |||||
| float bar_f; | |||||
| float barBeat; | |||||
| uint32_t beatUnit; | |||||
| float beatsPerBar; | |||||
| double beatsPerMinute; | |||||
| uint64_t frame; | |||||
| double speed; | |||||
| double ticksPerBeat; | |||||
| Lv2PositionData() | |||||
| : bar(-1), | |||||
| bar_f(-1.0f), | |||||
| barBeat(-1.0f), | |||||
| beatUnit(0), | |||||
| beatsPerBar(0.0f), | |||||
| beatsPerMinute(-1.0), | |||||
| frame(0), | |||||
| speed(0.0), | |||||
| ticksPerBeat(-1.0) {} | |||||
| } fLastPositionData; | |||||
| struct UI { | |||||
| const LV2_External_UI_Host* host; | |||||
| LV2UI_Write_Function writeFunction; | |||||
| LV2UI_Controller controller; | |||||
| uint32_t portOffset; | |||||
| bool isEmbed; | |||||
| bool isVisible; | |||||
| UI() | |||||
| : host(nullptr), | |||||
| writeFunction(nullptr), | |||||
| controller(nullptr), | |||||
| portOffset(0), | |||||
| isEmbed(false), | |||||
| isVisible(false) {} | |||||
| } fUI; | |||||
| struct Ports { | struct Ports { | ||||
| // need to save current state | // need to save current state | ||||
| @@ -114,6 +114,10 @@ enum PluginBridgePortType { | |||||
| kPluginBridgePortTypeCount | kPluginBridgePortTypeCount | ||||
| }; | }; | ||||
| enum PluginBridgeTimeInfoFlags { | |||||
| kPluginBridgeTimeInfoValidBBT = 0x1 | |||||
| }; | |||||
| // ------------------------------------------------------------------------------------------------------------------- | // ------------------------------------------------------------------------------------------------------------------- | ||||
| struct BridgeSemaphore { | struct BridgeSemaphore { | ||||
| @@ -132,7 +136,7 @@ struct BridgeTimeInfo { | |||||
| uint64_t playing; | uint64_t playing; | ||||
| uint64_t frame; | uint64_t frame; | ||||
| uint64_t usecs; | uint64_t usecs; | ||||
| uint32_t valid; | |||||
| uint32_t validFlags; | |||||
| // bbt | // bbt | ||||
| int32_t bar, beat, tick; | int32_t bar, beat, tick; | ||||
| float beatsPerBar, beatType; | float beatsPerBar, beatType; | ||||
| @@ -525,6 +525,7 @@ public: | |||||
| # pragma GCC diagnostic push | # pragma GCC diagnostic push | ||||
| # pragma GCC diagnostic ignored "-Weffc++" | # pragma GCC diagnostic ignored "-Weffc++" | ||||
| #endif | #endif | ||||
| template<class TimeInfoStruct> | |||||
| class Lv2PluginBaseClass : public LV2_External_UI_Widget_Compat | class Lv2PluginBaseClass : public LV2_External_UI_Widget_Compat | ||||
| { | { | ||||
| #if defined(__clang__) | #if defined(__clang__) | ||||
| @@ -534,11 +535,16 @@ class Lv2PluginBaseClass : public LV2_External_UI_Widget_Compat | |||||
| #endif | #endif | ||||
| public: | public: | ||||
| Lv2PluginBaseClass(const double sampleRate, const LV2_Feature* const* const features) | Lv2PluginBaseClass(const double sampleRate, const LV2_Feature* const* const features) | ||||
| : fIsOffline(false), | |||||
| : fIsActive(false), | |||||
| fIsOffline(false), | |||||
| fUsingNominal(false), | |||||
| fBufferSize(0), | fBufferSize(0), | ||||
| fSampleRate(sampleRate), | fSampleRate(sampleRate), | ||||
| fUsingNominal(false), | |||||
| fUridMap(nullptr) | |||||
| fUridMap(nullptr), | |||||
| fTimeInfo(), | |||||
| fLastPositionData(), | |||||
| fURIs(), | |||||
| fUI() | |||||
| { | { | ||||
| run = extui_run; | run = extui_run; | ||||
| show = extui_show; | show = extui_show; | ||||
| @@ -620,6 +626,9 @@ public: | |||||
| fUridMap = uridMap; | fUridMap = uridMap; | ||||
| fURIs.map(uridMap); | fURIs.map(uridMap); | ||||
| carla_zeroStruct(fTimeInfo); | |||||
| carla_zeroStruct(fLastPositionData); | |||||
| } | } | ||||
| virtual ~Lv2PluginBaseClass() | virtual ~Lv2PluginBaseClass() | ||||
| @@ -631,20 +640,245 @@ public: | |||||
| return fUridMap != nullptr && fBufferSize != 0; | return fUridMap != nullptr && fBufferSize != 0; | ||||
| } | } | ||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| bool lv2_pre_run(const uint32_t /*frames*/) | |||||
| { | |||||
| return true; | |||||
| } | |||||
| void lv2_post_run(const uint32_t frames) | |||||
| { | |||||
| // update timePos for next callback | |||||
| if (carla_isZero(fLastPositionData.speed)) | |||||
| return; | |||||
| if (fLastPositionData.speed > 0.0) | |||||
| { | |||||
| // playing forwards | |||||
| fLastPositionData.frame += frames; | |||||
| } | |||||
| else | |||||
| { | |||||
| // playing backwards | |||||
| if (frames >= fLastPositionData.frame) | |||||
| fLastPositionData.frame = 0; | |||||
| else | |||||
| fLastPositionData.frame -= frames; | |||||
| } | |||||
| fTimeInfo.frame = fLastPositionData.frame; | |||||
| if (fTimeInfo.bbt.valid) | |||||
| { | |||||
| const double beatsPerMinute = fLastPositionData.beatsPerMinute * fLastPositionData.speed; | |||||
| const double framesPerBeat = 60.0 * fSampleRate / beatsPerMinute; | |||||
| const double addedBarBeats = double(frames) / framesPerBeat; | |||||
| if (fLastPositionData.barBeat >= 0.0f) | |||||
| { | |||||
| fLastPositionData.barBeat = std::fmod(fLastPositionData.barBeat+static_cast<float>(addedBarBeats), | |||||
| fLastPositionData.beatsPerBar); | |||||
| const double rest = std::fmod(fLastPositionData.barBeat, 1.0f); | |||||
| fTimeInfo.bbt.beat = static_cast<int32_t>(fLastPositionData.barBeat-rest+1.0); | |||||
| fTimeInfo.bbt.tick = static_cast<int32_t>(rest*fTimeInfo.bbt.ticksPerBeat+0.5); | |||||
| if (fLastPositionData.bar_f >= 0.0f) | |||||
| { | |||||
| fLastPositionData.bar_f += std::floor((fLastPositionData.barBeat+static_cast<float>(addedBarBeats))/ | |||||
| fLastPositionData.beatsPerBar); | |||||
| if (fLastPositionData.bar_f <= 0.0f) | |||||
| { | |||||
| fLastPositionData.bar = 0; | |||||
| fLastPositionData.bar_f = 0.0f; | |||||
| } | |||||
| else | |||||
| { | |||||
| fLastPositionData.bar = static_cast<int32_t>(fLastPositionData.bar_f+0.5f); | |||||
| } | |||||
| fTimeInfo.bbt.bar = fLastPositionData.bar + 1; | |||||
| fTimeInfo.bbt.barStartTick = fTimeInfo.bbt.ticksPerBeat * | |||||
| fTimeInfo.bbt.beatsPerBar * | |||||
| (fTimeInfo.bbt.bar-1); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| uint32_t lv2_get_options(LV2_Options_Option* const /*options*/) const | |||||
| { | |||||
| // currently unused | |||||
| return LV2_OPTIONS_SUCCESS; | |||||
| } | |||||
| uint32_t lv2_set_options(const LV2_Options_Option* const options) | |||||
| { | |||||
| for (int i=0; options[i].key != 0; ++i) | |||||
| { | |||||
| if (options[i].key == fUridMap->map(fUridMap->handle, LV2_BUF_SIZE__nominalBlockLength)) | |||||
| { | |||||
| if (options[i].type == fURIs.atomInt) | |||||
| { | |||||
| const int value(*(const int*)options[i].value); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(value > 0); | |||||
| const uint32_t newBufferSize = static_cast<uint32_t>(value); | |||||
| if (fBufferSize != newBufferSize) | |||||
| { | |||||
| fBufferSize = newBufferSize; | |||||
| handleBufferSizeChanged(newBufferSize); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| carla_stderr("Host changed nominalBlockLength but with wrong value type"); | |||||
| } | |||||
| } | |||||
| else if (options[i].key == fUridMap->map(fUridMap->handle, LV2_BUF_SIZE__maxBlockLength) && ! fUsingNominal) | |||||
| { | |||||
| if (options[i].type == fURIs.atomInt) | |||||
| { | |||||
| const int value(*(const int*)options[i].value); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(value > 0); | |||||
| const uint32_t newBufferSize = static_cast<uint32_t>(value); | |||||
| if (fBufferSize != newBufferSize) | |||||
| { | |||||
| fBufferSize = newBufferSize; | |||||
| handleBufferSizeChanged(newBufferSize); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| carla_stderr("Host changed maxBlockLength but with wrong value type"); | |||||
| } | |||||
| } | |||||
| else if (options[i].key == fUridMap->map(fUridMap->handle, LV2_PARAMETERS__sampleRate)) | |||||
| { | |||||
| if (options[i].type == fURIs.atomFloat) | |||||
| { | |||||
| const double value(*(const float*)options[i].value); | |||||
| CARLA_SAFE_ASSERT_CONTINUE(value > 0.0); | |||||
| if (carla_isNotEqual(fSampleRate, value)) | |||||
| { | |||||
| fSampleRate = value; | |||||
| handleSampleRateChanged(value); | |||||
| } | |||||
| } | |||||
| else | |||||
| { | |||||
| carla_stderr("Host changed sampleRate but with wrong value type"); | |||||
| } | |||||
| } | |||||
| } | |||||
| return LV2_OPTIONS_SUCCESS; | |||||
| } | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| int lv2ui_idle() const | |||||
| { | |||||
| if (! fUI.isVisible) | |||||
| return 1; | |||||
| handleUiRun(); | |||||
| return 0; | |||||
| } | |||||
| int lv2ui_show() | |||||
| { | |||||
| handleUiShow(); | |||||
| return 0; | |||||
| } | |||||
| int lv2ui_hide() | |||||
| { | |||||
| handleUiHide(); | |||||
| return 0; | |||||
| } | |||||
| void lv2ui_cleanup() | |||||
| { | |||||
| if (fUI.isVisible) | |||||
| handleUiHide(); | |||||
| fUI.host = nullptr; | |||||
| fUI.writeFunction = nullptr; | |||||
| fUI.controller = nullptr; | |||||
| } | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| protected: | protected: | ||||
| virtual void handleUiRun() const = 0; | virtual void handleUiRun() const = 0; | ||||
| virtual void handleUiShow() = 0; | virtual void handleUiShow() = 0; | ||||
| virtual void handleUiHide() = 0; | virtual void handleUiHide() = 0; | ||||
| virtual void handleBufferSizeChanged(const uint32_t bufferSize) = 0; | |||||
| virtual void handleSampleRateChanged(const double sampleRate) = 0; | |||||
| // LV2 host data | // LV2 host data | ||||
| bool fIsOffline; | |||||
| bool fIsActive : 1; | |||||
| bool fIsOffline : 1; | |||||
| bool fUsingNominal : 1; | |||||
| uint32_t fBufferSize; | uint32_t fBufferSize; | ||||
| double fSampleRate; | double fSampleRate; | ||||
| bool fUsingNominal; | |||||
| // LV2 host features | // LV2 host features | ||||
| const LV2_URID_Map* fUridMap; | const LV2_URID_Map* fUridMap; | ||||
| // Time info stuff | |||||
| TimeInfoStruct fTimeInfo; | |||||
| struct Lv2PositionData { | |||||
| int32_t bar; | |||||
| float bar_f; | |||||
| float barBeat; | |||||
| uint32_t beatUnit; | |||||
| float beatsPerBar; | |||||
| double beatsPerMinute; | |||||
| uint64_t frame; | |||||
| double speed; | |||||
| double ticksPerBeat; | |||||
| Lv2PositionData() | |||||
| : bar(-1), | |||||
| bar_f(-1.0f), | |||||
| barBeat(-1.0f), | |||||
| beatUnit(0), | |||||
| beatsPerBar(0.0f), | |||||
| beatsPerMinute(-1.0), | |||||
| frame(0), | |||||
| speed(0.0), | |||||
| ticksPerBeat(-1.0) {} | |||||
| } fLastPositionData; | |||||
| void resetTimeInfo() | |||||
| { | |||||
| carla_zeroStruct(fLastPositionData); | |||||
| carla_zeroStruct(fTimeInfo); | |||||
| // hosts may not send all values, resulting on some invalid data | |||||
| fTimeInfo.bbt.bar = 1; | |||||
| fTimeInfo.bbt.beat = 1; | |||||
| fTimeInfo.bbt.beatsPerBar = 4; | |||||
| fTimeInfo.bbt.beatType = 4; | |||||
| fTimeInfo.bbt.ticksPerBeat = fLastPositionData.ticksPerBeat = 960.0; | |||||
| fTimeInfo.bbt.beatsPerMinute = fLastPositionData.beatsPerMinute = 120.0; | |||||
| } | |||||
| // Rest of host<->plugin support | |||||
| struct URIDs { | struct URIDs { | ||||
| LV2_URID atomBlank; | LV2_URID atomBlank; | ||||
| LV2_URID atomObject; | LV2_URID atomObject; | ||||
| @@ -708,6 +942,23 @@ protected: | |||||
| } | } | ||||
| } fURIs; | } fURIs; | ||||
| struct UI { | |||||
| const LV2_External_UI_Host* host; | |||||
| LV2UI_Write_Function writeFunction; | |||||
| LV2UI_Controller controller; | |||||
| uint32_t portOffset; | |||||
| bool isEmbed; | |||||
| bool isVisible; | |||||
| UI() | |||||
| : host(nullptr), | |||||
| writeFunction(nullptr), | |||||
| controller(nullptr), | |||||
| portOffset(0), | |||||
| isEmbed(false), | |||||
| isVisible(false) {} | |||||
| } fUI; | |||||
| private: | private: | ||||
| // ---------------------------------------------------------------------------------------------------------------- | // ---------------------------------------------------------------------------------------------------------------- | ||||