@@ -291,6 +291,7 @@ struct CARLA_API EngineTimeInfoBBT { | |||
#ifndef DOXYGEN | |||
EngineTimeInfoBBT() noexcept; | |||
EngineTimeInfoBBT(const EngineTimeInfoBBT&) noexcept; | |||
#endif | |||
}; | |||
@@ -310,6 +311,8 @@ struct CARLA_API EngineTimeInfo { | |||
#ifndef DOXYGEN | |||
EngineTimeInfo() noexcept; | |||
EngineTimeInfo(const EngineTimeInfo&) noexcept; | |||
EngineTimeInfo& operator=(const EngineTimeInfo&) noexcept; | |||
// quick operator, doesn't check all values | |||
bool operator==(const EngineTimeInfo& timeInfo) const noexcept; | |||
@@ -922,7 +925,7 @@ public: | |||
/*! | |||
* Get the current Time information (read-only). | |||
*/ | |||
const EngineTimeInfo& getTimeInfo() const noexcept; | |||
virtual EngineTimeInfo getTimeInfo() const noexcept; | |||
// ------------------------------------------------------------------- | |||
// Information (peaks) | |||
@@ -1127,7 +1127,7 @@ const EngineOptions& CarlaEngine::getOptions() const noexcept | |||
return pData->options; | |||
} | |||
const EngineTimeInfo& CarlaEngine::getTimeInfo() const noexcept | |||
EngineTimeInfo CarlaEngine::getTimeInfo() const noexcept | |||
{ | |||
return pData->timeInfo; | |||
} | |||
@@ -304,6 +304,17 @@ EngineTimeInfoBBT::EngineTimeInfoBBT() noexcept | |||
ticksPerBeat(0.0), | |||
beatsPerMinute(0.0) {} | |||
EngineTimeInfoBBT::EngineTimeInfoBBT(const EngineTimeInfoBBT& bbt) noexcept | |||
: valid(bbt.valid), | |||
bar(bbt.bar), | |||
beat(bbt.beat), | |||
tick(bbt.tick), | |||
barStartTick(bbt.barStartTick), | |||
beatsPerBar(bbt.beatsPerBar), | |||
beatType(bbt.beatType), | |||
ticksPerBeat(bbt.ticksPerBeat), | |||
beatsPerMinute(bbt.beatsPerMinute) {} | |||
// ----------------------------------------------------------------------- | |||
// EngineTimeInfo | |||
@@ -321,6 +332,30 @@ void EngineTimeInfo::clear() noexcept | |||
carla_zeroStruct(bbt); | |||
} | |||
EngineTimeInfo::EngineTimeInfo(const EngineTimeInfo& info) noexcept | |||
: playing(info.playing), | |||
frame(info.frame), | |||
usecs(info.usecs), | |||
bbt(info.bbt) {} | |||
EngineTimeInfo& EngineTimeInfo::operator=(const EngineTimeInfo& info) noexcept | |||
{ | |||
playing = info.playing; | |||
frame = info.frame; | |||
usecs = info.usecs; | |||
bbt.valid = info.bbt.valid; | |||
bbt.bar = info.bbt.bar; | |||
bbt.tick = info.bbt.tick; | |||
bbt.tick = info.bbt.tick; | |||
bbt.barStartTick = info.bbt.barStartTick; | |||
bbt.beatsPerBar = info.bbt.beatsPerBar; | |||
bbt.beatType = info.bbt.beatType; | |||
bbt.ticksPerBeat = info.bbt.ticksPerBeat; | |||
bbt.beatsPerMinute = info.bbt.beatsPerMinute; | |||
return *this; | |||
} | |||
bool EngineTimeInfo::operator==(const EngineTimeInfo& timeInfo) const noexcept | |||
{ | |||
if (timeInfo.playing != playing || timeInfo.frame != frame || timeInfo.bbt.valid != bbt.valid) | |||
@@ -176,10 +176,11 @@ void EngineInternalTime::fillEngineTimeInfo(const uint32_t newFrames) noexcept | |||
double ticktmp; | |||
timeInfo.usecs = 0; | |||
if (transportMode == ENGINE_TRANSPORT_MODE_INTERNAL) | |||
{ | |||
timeInfo.usecs = 0; | |||
timeInfo.frame = nextFrame; | |||
} | |||
if (needsReset) | |||
{ | |||
@@ -256,74 +257,19 @@ void EngineInternalTime::fillJackTimeInfo(jack_position_t* const pos, const uint | |||
{ | |||
CARLA_SAFE_ASSERT_RETURN(carla_isNotZero(sampleRate),); | |||
CARLA_SAFE_ASSERT_RETURN(newFrames > 0,); | |||
double ticktmp; | |||
if (needsReset) | |||
{ | |||
pos->valid = JackPositionBBT; | |||
pos->beat_type = 4.0f; | |||
pos->ticks_per_beat = kTicksPerBeat; | |||
double abs_beat, abs_tick; | |||
#if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE) | |||
if (hylia.enabled) | |||
{ | |||
if (hylia.timeInfo.beat >= 0.0) | |||
{ | |||
abs_beat = hylia.timeInfo.beat; | |||
abs_tick = abs_beat * kTicksPerBeat; | |||
} | |||
else | |||
{ | |||
abs_beat = 0.0; | |||
abs_tick = 0.0; | |||
timeInfo.playing = false; | |||
} | |||
} | |||
else | |||
#endif | |||
{ | |||
const double min = static_cast<double>(pos->frame) / (sampleRate * 60.0); | |||
abs_beat = min * beatsPerMinute; | |||
abs_tick = abs_beat * kTicksPerBeat; | |||
} | |||
const double bar = std::floor(abs_beat / beatsPerBar); | |||
const double beat = std::floor(std::fmod(abs_beat, beatsPerBar)); | |||
pos->bar = static_cast<int32_t>(bar) + 1; | |||
pos->beat = static_cast<int32_t>(beat) + 1; | |||
pos->bar_start_tick = ((bar * beatsPerBar) + beat) * kTicksPerBeat; | |||
ticktmp = abs_tick - pos->bar_start_tick; | |||
} | |||
else if (timeInfo.playing) | |||
{ | |||
ticktmp = tick + (newFrames * kTicksPerBeat * beatsPerMinute / (sampleRate * 60.0)); | |||
while (ticktmp >= kTicksPerBeat) | |||
{ | |||
ticktmp -= kTicksPerBeat; | |||
if (++pos->beat > beatsPerBar) | |||
{ | |||
++pos->bar; | |||
pos->beat = 1; | |||
pos->bar_start_tick += beatsPerBar * kTicksPerBeat; | |||
} | |||
} | |||
} | |||
else | |||
{ | |||
ticktmp = tick; | |||
} | |||
pos->beats_per_bar = static_cast<float>(beatsPerBar); | |||
CARLA_SAFE_ASSERT(transportMode == ENGINE_TRANSPORT_MODE_JACK); | |||
fillEngineTimeInfo(newFrames); | |||
pos->valid = JackPositionBBT; | |||
pos->bar = timeInfo.bbt.bar; | |||
pos->beat = timeInfo.bbt.beat; | |||
pos->tick = static_cast<int32_t>(tick + 0.5); | |||
pos->bar_start_tick = timeInfo.bbt.barStartTick; | |||
pos->beats_per_bar = timeInfo.bbt.beatsPerBar; | |||
pos->beat_type = timeInfo.bbt.beatType; | |||
pos->ticks_per_beat = kTicksPerBeat; | |||
pos->beats_per_minute = beatsPerMinute; | |||
pos->tick = ticktmp; | |||
tick = ticktmp; | |||
} | |||
void EngineInternalTime::preProcess(const uint32_t numFrames) | |||
@@ -811,6 +811,7 @@ public: | |||
fIsRunning(false) | |||
#else | |||
fTimebaseMaster(false), | |||
fTimebaseRolling(false), | |||
fUsedGroups(), | |||
fUsedPorts(), | |||
fUsedConnections(), | |||
@@ -950,6 +951,8 @@ public: | |||
jackbridge_set_process_callback(fClient, carla_jack_process_callback, this); | |||
jackbridge_on_shutdown(fClient, carla_jack_shutdown_callback, this); | |||
fTimebaseRolling = false; | |||
if (opts.transportMode == ENGINE_TRANSPORT_MODE_JACK) | |||
fTimebaseMaster = jackbridge_set_timebase_callback(fClient, true, carla_jack_timebase_callback, this); | |||
else | |||
@@ -1086,6 +1089,55 @@ public: | |||
return "JACK"; | |||
} | |||
EngineTimeInfo getTimeInfo() const noexcept override | |||
{ | |||
if (pData->options.transportMode != ENGINE_TRANSPORT_MODE_JACK) | |||
return CarlaEngine::getTimeInfo(); | |||
if (pData->options.processMode != ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS) | |||
return CarlaEngine::getTimeInfo(); | |||
jack_position_t jpos; | |||
// invalidate | |||
jpos.unique_1 = 1; | |||
jpos.unique_2 = 2; | |||
EngineTimeInfo timeInfo; | |||
const bool playing = jackbridge_transport_query(fClient, &jpos) == JackTransportRolling; | |||
if (jpos.unique_1 != jpos.unique_2) | |||
{ | |||
timeInfo.playing = false; | |||
timeInfo.frame = 0; | |||
timeInfo.usecs = 0; | |||
timeInfo.bbt.valid = false; | |||
return timeInfo; | |||
} | |||
timeInfo.playing = playing; | |||
timeInfo.frame = jpos.frame; | |||
timeInfo.usecs = jpos.usecs; | |||
if (jpos.valid & JackPositionBBT) | |||
{ | |||
timeInfo.bbt.valid = true; | |||
timeInfo.bbt.bar = jpos.bar; | |||
timeInfo.bbt.beat = jpos.beat; | |||
timeInfo.bbt.tick = jpos.tick; | |||
timeInfo.bbt.barStartTick = jpos.bar_start_tick; | |||
timeInfo.bbt.beatsPerBar = jpos.beats_per_bar; | |||
timeInfo.bbt.beatType = jpos.beat_type; | |||
timeInfo.bbt.ticksPerBeat = jpos.ticks_per_beat; | |||
timeInfo.bbt.beatsPerMinute = jpos.beats_per_minute; | |||
} | |||
else | |||
{ | |||
timeInfo.bbt.valid = false; | |||
} | |||
return timeInfo; | |||
} | |||
#ifndef BUILD_BRIDGE | |||
void setOption(const EngineOption option, const int value, const char* const valueStr) noexcept override | |||
{ | |||
@@ -1392,7 +1444,7 @@ public: | |||
void transportPlay() noexcept override | |||
{ | |||
if (pData->options.transportMode == ENGINE_TRANSPORT_MODE_INTERNAL) | |||
if (pData->options.transportMode != ENGINE_TRANSPORT_MODE_JACK) | |||
return CarlaEngine::transportPlay(); | |||
if (fClient != nullptr) | |||
@@ -1412,7 +1464,7 @@ public: | |||
void transportPause() noexcept override | |||
{ | |||
if (pData->options.transportMode == ENGINE_TRANSPORT_MODE_INTERNAL) | |||
if (pData->options.transportMode != ENGINE_TRANSPORT_MODE_JACK) | |||
return CarlaEngine::transportPause(); | |||
if (fClient != nullptr) | |||
@@ -1425,9 +1477,10 @@ public: | |||
void transportBPM(const double bpm) noexcept override | |||
{ | |||
CarlaEngine::transportBPM(bpm); | |||
if (pData->options.transportMode != ENGINE_TRANSPORT_MODE_JACK || fTimebaseMaster) | |||
return CarlaEngine::transportBPM(bpm); | |||
if (fClient == nullptr || fTimebaseMaster) | |||
if (fClient == nullptr) | |||
return; | |||
jack_position_t jpos; | |||
@@ -1450,7 +1503,7 @@ public: | |||
void transportRelocate(const uint64_t frame) noexcept override | |||
{ | |||
if (pData->options.transportMode == ENGINE_TRANSPORT_MODE_INTERNAL) | |||
if (pData->options.transportMode != ENGINE_TRANSPORT_MODE_JACK) | |||
return CarlaEngine::transportRelocate(frame); | |||
if (fClient != nullptr) | |||
@@ -1557,57 +1610,12 @@ protected: | |||
offlineModeChanged(isFreewheel); | |||
} | |||
void saveTransportInfo() | |||
{ | |||
if (pData->options.transportMode != ENGINE_TRANSPORT_MODE_JACK) | |||
return; | |||
jack_position_t jpos; | |||
// invalidate | |||
jpos.unique_1 = 1; | |||
jpos.unique_2 = 2; | |||
pData->timeInfo.playing = (jackbridge_transport_query(fClient, &jpos) == JackTransportRolling); | |||
if (jpos.unique_1 == jpos.unique_2) | |||
{ | |||
pData->timeInfo.frame = jpos.frame; | |||
pData->timeInfo.usecs = jpos.usecs; | |||
if (jpos.valid & JackPositionBBT) | |||
{ | |||
pData->timeInfo.bbt.valid = true; | |||
pData->timeInfo.bbt.bar = jpos.bar; | |||
pData->timeInfo.bbt.beat = jpos.beat; | |||
pData->timeInfo.bbt.tick = jpos.tick; | |||
pData->timeInfo.bbt.barStartTick = jpos.bar_start_tick; | |||
pData->timeInfo.bbt.beatsPerBar = jpos.beats_per_bar; | |||
pData->timeInfo.bbt.beatType = jpos.beat_type; | |||
pData->timeInfo.bbt.ticksPerBeat = jpos.ticks_per_beat; | |||
pData->timeInfo.bbt.beatsPerMinute = jpos.beats_per_minute; | |||
} | |||
else | |||
{ | |||
pData->timeInfo.bbt.valid = false; | |||
} | |||
} | |||
else | |||
{ | |||
pData->timeInfo.frame = 0; | |||
pData->timeInfo.usecs = 0; | |||
pData->timeInfo.bbt.valid = false; | |||
} | |||
} | |||
void handleJackProcessCallback(const uint32_t nframes) | |||
{ | |||
const PendingRtEventsRunner prt(this, nframes); | |||
CARLA_SAFE_ASSERT_RETURN(nframes == pData->bufferSize,); | |||
saveTransportInfo(); | |||
#ifdef BUILD_BRIDGE | |||
CarlaPlugin* const plugin(pData->plugins[0].plugin); | |||
@@ -1783,6 +1791,18 @@ protected: | |||
} | |||
} | |||
} | |||
if (fTimebaseMaster) | |||
{ | |||
const bool playing = jackbridge_transport_query(fClient, nullptr) == JackTransportRolling; | |||
if (fTimebaseRolling != playing) | |||
{ | |||
carla_stdout("state changed SAVE %i", playing); | |||
fTimebaseRolling = playing; | |||
pData->timeInfo.playing = playing; | |||
} | |||
} | |||
#endif // ! BUILD_BRIDGE | |||
} | |||
@@ -1797,6 +1817,9 @@ protected: | |||
if (new_pos) | |||
pData->time.setNeedsReset(); | |||
pData->timeInfo.playing = fTimebaseRolling; | |||
pData->timeInfo.frame = pos->frame; | |||
pData->timeInfo.usecs = pos->usecs; | |||
pData->time.fillJackTimeInfo(pos, nframes); | |||
} | |||
@@ -2055,6 +2078,8 @@ private: | |||
jack_port_t* fRackPorts[kRackPortCount]; | |||
bool fTimebaseMaster; | |||
bool fTimebaseRolling; | |||
PatchbayGroupList fUsedGroups; | |||
PatchbayPortList fUsedPorts; | |||
PatchbayConnectionList fUsedConnections; | |||
@@ -2486,15 +2511,15 @@ private: | |||
handlePtr->handleJackFreewheelCallback(bool(starting)); | |||
} | |||
static int JACKBRIDGE_API carla_jack_process_callback(jack_nframes_t nframes, void* arg) __attribute__((annotate("realtime"))) | |||
static void JACKBRIDGE_API carla_jack_latency_callback(jack_latency_callback_mode_t mode, void* arg) | |||
{ | |||
handlePtr->handleJackProcessCallback(nframes); | |||
return 0; | |||
handlePtr->handleJackLatencyCallback(mode); | |||
} | |||
static void JACKBRIDGE_API carla_jack_latency_callback(jack_latency_callback_mode_t mode, void* arg) | |||
static int JACKBRIDGE_API carla_jack_process_callback(jack_nframes_t nframes, void* arg) __attribute__((annotate("realtime"))) | |||
{ | |||
handlePtr->handleJackLatencyCallback(mode); | |||
handlePtr->handleJackProcessCallback(nframes); | |||
return 0; | |||
} | |||
#ifndef BUILD_BRIDGE | |||
@@ -2567,7 +2592,6 @@ private: | |||
if (plugin->tryLock(engine->fFreewheel)) | |||
{ | |||
plugin->initBuffers(); | |||
engine->saveTransportInfo(); | |||
engine->processPlugin(plugin, nframes); | |||
plugin->unlock(); | |||
} | |||
@@ -1467,7 +1467,7 @@ public: | |||
// -------------------------------------------------------------------------------------------------------- | |||
// TimeInfo | |||
const EngineTimeInfo& timeInfo(pData->engine->getTimeInfo()); | |||
const EngineTimeInfo timeInfo(pData->engine->getTimeInfo()); | |||
BridgeTimeInfo& bridgeTimeInfo(fShmRtClientControl.data->timeInfo); | |||
bridgeTimeInfo.playing = timeInfo.playing; | |||
@@ -940,7 +940,7 @@ public: | |||
// -------------------------------------------------------------------------------------------------------- | |||
// TimeInfo | |||
const EngineTimeInfo& timeInfo(pData->engine->getTimeInfo()); | |||
const EngineTimeInfo timeInfo(pData->engine->getTimeInfo()); | |||
BridgeTimeInfo& bridgeTimeInfo(fShmRtClientControl.data->timeInfo); | |||
bridgeTimeInfo.playing = timeInfo.playing; | |||
@@ -2779,7 +2779,7 @@ public: | |||
// -------------------------------------------------------------------------------------------------------- | |||
// TimeInfo | |||
const EngineTimeInfo& timeInfo(pData->engine->getTimeInfo()); | |||
const EngineTimeInfo timeInfo(pData->engine->getTimeInfo()); | |||
if (fFirstActive || fLastTimeInfo != timeInfo) | |||
{ | |||
@@ -1537,7 +1537,7 @@ public: | |||
// -------------------------------------------------------------------------------------------------------- | |||
// Set TimeInfo | |||
const EngineTimeInfo& timeInfo(pData->engine->getTimeInfo()); | |||
const EngineTimeInfo timeInfo(pData->engine->getTimeInfo()); | |||
fTimeInfo.playing = timeInfo.playing; | |||
fTimeInfo.frame = timeInfo.frame; | |||
@@ -1563,6 +1563,39 @@ public: | |||
fTimeInfo.bbt.valid = false; | |||
} | |||
#if 0 | |||
// This test code has proven to be quite useful | |||
// So I am leaving it behind, I might need it again.. | |||
if (pData->id == 1) | |||
{ | |||
static int64_t last_frame = timeInfo.frame; | |||
static int64_t last_dev_frame = 0; | |||
static double last_val = timeInfo.bbt.barStartTick + ((timeInfo.bbt.beat-1) * timeInfo.bbt.ticksPerBeat) + timeInfo.bbt.tick; | |||
static double last_dev_val = 0.0; | |||
int64_t cur_frame = timeInfo.frame; | |||
int64_t cur_dev_frame = cur_frame - last_frame; | |||
double cur_val = timeInfo.bbt.barStartTick + ((timeInfo.bbt.beat-1) * timeInfo.bbt.ticksPerBeat) + timeInfo.bbt.tick; | |||
double cur_dev_val = cur_val - last_val; | |||
if (std::abs(last_dev_val - cur_dev_val) >= 0.0001 || last_dev_frame != cur_dev_frame) | |||
{ | |||
carla_stdout("currently %u at %u => %f : DEV1: %li : DEV2: %f", | |||
frames, | |||
timeInfo.frame, | |||
cur_val, | |||
cur_dev_frame, | |||
cur_dev_val); | |||
} | |||
last_val = cur_val; | |||
last_dev_val = cur_dev_val; | |||
last_frame = cur_frame; | |||
last_dev_frame = cur_dev_frame; | |||
} | |||
#endif | |||
// -------------------------------------------------------------------------------------------------------- | |||
// Event Input and Processing | |||
@@ -1121,7 +1121,7 @@ public: | |||
// -------------------------------------------------------------------------------------------------------- | |||
// Set TimeInfo | |||
const EngineTimeInfo& timeInfo(pData->engine->getTimeInfo()); | |||
const EngineTimeInfo timeInfo(pData->engine->getTimeInfo()); | |||
fTimeInfo.flags = kVstTransportChanged; | |||