@@ -627,9 +627,6 @@ | |||
<layout class="QVBoxLayout" name="verticalLayout_5"> | |||
<item> | |||
<widget class="QDoubleSpinBox" name="dsb_transport_bpm"> | |||
<property name="enabled"> | |||
<bool>false</bool> | |||
</property> | |||
<property name="suffix"> | |||
<string> BPM</string> | |||
</property> | |||
@@ -988,6 +988,11 @@ public: | |||
*/ | |||
virtual void transportPause() noexcept; | |||
/*! | |||
* Set the engine transport bpm to @a bpm. | |||
*/ | |||
virtual void transportBPM(const double bpm) noexcept; | |||
/*! | |||
* Relocate the engine transport to @a frames. | |||
*/ | |||
@@ -433,6 +433,11 @@ CARLA_EXPORT void carla_transport_play(); | |||
*/ | |||
CARLA_EXPORT void carla_transport_pause(); | |||
/*! | |||
* Set the engine transport bpm. | |||
*/ | |||
CARLA_EXPORT void carla_transport_bpm(double bpm); | |||
/*! | |||
* Relocate the engine transport to a specific frame. | |||
*/ | |||
@@ -743,6 +743,14 @@ void carla_transport_pause() | |||
gStandalone.engine->transportPause(); | |||
} | |||
void carla_transport_bpm(double bpm) | |||
{ | |||
CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr && gStandalone.engine->isRunning(),); | |||
carla_debug("carla_transport_bpm(%f)", bpm); | |||
gStandalone.engine->transportBPM(bpm); | |||
} | |||
void carla_transport_relocate(uint64_t frame) | |||
{ | |||
CARLA_SAFE_ASSERT_RETURN(gStandalone.engine != nullptr && gStandalone.engine->isRunning(),); | |||
@@ -1351,6 +1351,13 @@ void CarlaEngine::transportPause() noexcept | |||
pData->time.setNeedsReset(); | |||
} | |||
void CarlaEngine::transportBPM(const double bpm) noexcept | |||
{ | |||
try { | |||
pData->time.setBPM(bpm); | |||
} CARLA_SAFE_EXCEPTION("CarlaEngine::transportBPM"); | |||
} | |||
void CarlaEngine::transportRelocate(const uint64_t frame) noexcept | |||
{ | |||
pData->time.relocate(frame); | |||
@@ -136,6 +136,16 @@ void EngineInternalTime::enableLink(const bool enable) | |||
needsReset = true; | |||
} | |||
void EngineInternalTime::setBPM(const double bpm) | |||
{ | |||
beatsPerMinute = bpm; | |||
#if defined(HAVE_HYLIA) && !defined(BUILD_BRIDGE) | |||
if (hylia.instance != nullptr) | |||
hylia_set_beats_per_minute(hylia.instance, bpm); | |||
#endif | |||
} | |||
void EngineInternalTime::setNeedsReset() noexcept | |||
{ | |||
needsReset = true; | |||
@@ -163,10 +173,8 @@ void EngineInternalTime::fillEngineTimeInfo(const uint32_t newFrames) noexcept | |||
if (needsReset) | |||
{ | |||
timeInfo.valid = EngineTimeInfo::kValidBBT; | |||
timeInfo.bbt.beatsPerBar = beatsPerBar; | |||
timeInfo.bbt.beatType = 4.0f; | |||
timeInfo.bbt.ticksPerBeat = kTicksPerBeat; | |||
timeInfo.bbt.beatsPerMinute = beatsPerMinute; | |||
double abs_beat, abs_tick; | |||
@@ -195,8 +203,8 @@ void EngineInternalTime::fillEngineTimeInfo(const uint32_t newFrames) noexcept | |||
needsReset = false; | |||
} | |||
timeInfo.bbt.bar = (int32_t)(std::floor(abs_beat / timeInfo.bbt.beatsPerBar) + 0.5); | |||
timeInfo.bbt.beat = (int32_t)(abs_beat - (timeInfo.bbt.bar * timeInfo.bbt.beatsPerBar) + 1.5); | |||
timeInfo.bbt.bar = (int32_t)(std::floor(abs_beat / beatsPerBar) + 0.5); | |||
timeInfo.bbt.beat = (int32_t)(abs_beat - (timeInfo.bbt.bar * beatsPerBar) + 1.5); | |||
timeInfo.bbt.barStartTick = timeInfo.bbt.bar * beatsPerBar * kTicksPerBeat; | |||
++timeInfo.bbt.bar; | |||
@@ -220,6 +228,8 @@ void EngineInternalTime::fillEngineTimeInfo(const uint32_t newFrames) noexcept | |||
} | |||
} | |||
timeInfo.bbt.beatsPerBar = beatsPerBar; | |||
timeInfo.bbt.beatsPerMinute = beatsPerMinute; | |||
timeInfo.bbt.tick = (int32_t)(ticktmp + 0.5); | |||
tick = ticktmp; | |||
@@ -237,10 +247,8 @@ void EngineInternalTime::fillJackTimeInfo(jack_position_t* const pos, const uint | |||
if (needsReset) | |||
{ | |||
pos->valid = JackPositionBBT; | |||
pos->beats_per_bar = beatsPerBar; | |||
pos->beat_type = 4.0f; | |||
pos->ticks_per_beat = kTicksPerBeat; | |||
pos->beats_per_minute = beatsPerMinute; | |||
double abs_beat, abs_tick; | |||
@@ -269,9 +277,9 @@ void EngineInternalTime::fillJackTimeInfo(jack_position_t* const pos, const uint | |||
needsReset = false; | |||
} | |||
pos->bar = (int32_t)(std::floor(abs_beat / pos->beats_per_bar) + 0.5); | |||
pos->beat = (int32_t)(abs_beat - (pos->bar * pos->beats_per_bar) + 1.5); | |||
pos->bar_start_tick = pos->bar * pos->beats_per_bar * kTicksPerBeat; | |||
pos->bar = (int32_t)(std::floor(abs_beat / beatsPerBar) + 0.5); | |||
pos->beat = (int32_t)(abs_beat - (pos->bar * beatsPerBar) + 1.5); | |||
pos->bar_start_tick = pos->bar * beatsPerBar * kTicksPerBeat; | |||
++pos->bar; | |||
//ticktmp = abs_tick - pos->bar_start_tick; | |||
@@ -294,6 +302,8 @@ void EngineInternalTime::fillJackTimeInfo(jack_position_t* const pos, const uint | |||
} | |||
} | |||
pos->beats_per_bar = beatsPerBar; | |||
pos->beats_per_minute = beatsPerMinute; | |||
pos->tick = (int32_t)(ticktmp + 0.5); | |||
tick = ticktmp; | |||
} | |||
@@ -121,6 +121,7 @@ public: | |||
void updateAudioValues(const uint32_t bufferSize, const double sampleRate); | |||
void enableLink(const bool enable); | |||
void setBPM(const double bpm); | |||
void setNeedsReset() noexcept; | |||
void relocate(const uint64_t frame) noexcept; | |||
@@ -816,6 +816,7 @@ public: | |||
#ifdef BUILD_BRIDGE | |||
fIsRunning(false) | |||
#else | |||
fTimebaseMaster(false), | |||
fUsedGroups(), | |||
fUsedPorts(), | |||
fUsedConnections(), | |||
@@ -951,9 +952,10 @@ public: | |||
jackbridge_set_freewheel_callback(fClient, carla_jack_freewheel_callback, this); | |||
jackbridge_set_latency_callback(fClient, carla_jack_latency_callback, this); | |||
jackbridge_set_process_callback(fClient, carla_jack_process_callback, this); | |||
jackbridge_set_timebase_callback(fClient, true, carla_jack_timebase_callback, this); | |||
jackbridge_on_shutdown(fClient, carla_jack_shutdown_callback, this); | |||
fTimebaseMaster = jackbridge_set_timebase_callback(fClient, true, carla_jack_timebase_callback, this); | |||
if (pData->options.processMode != ENGINE_PROCESS_MODE_PATCHBAY) | |||
initJackPatchbay(jackClientName); | |||
@@ -1373,7 +1375,7 @@ public: | |||
{ | |||
// old timebase master no longer active, make ourselves master again | |||
pData->time.setNeedsReset(); | |||
jackbridge_set_timebase_callback(fClient, true, carla_jack_timebase_callback, this); | |||
fTimebaseMaster = jackbridge_set_timebase_callback(fClient, true, carla_jack_timebase_callback, this); | |||
} | |||
try { | |||
@@ -1395,6 +1397,31 @@ public: | |||
} | |||
} | |||
void transportBPM(const double bpm) noexcept override | |||
{ | |||
CarlaEngine::transportBPM(bpm); | |||
if (fClient == nullptr || fTimebaseMaster) | |||
return; | |||
jack_position_t jpos; | |||
// invalidate | |||
jpos.unique_1 = 1; | |||
jpos.unique_2 = 2; | |||
jackbridge_transport_query(fClient, &jpos); | |||
if (jpos.unique_1 == jpos.unique_2 && (jpos.valid & JackPositionBBT) != 0) | |||
{ | |||
carla_stdout("NOTE: Changing BPM without being JACK timebase master"); | |||
jpos.beats_per_minute = bpm; | |||
try { | |||
jackbridge_transport_reposition(fClient, &jpos); | |||
} catch(...) {} | |||
} | |||
} | |||
void transportRelocate(const uint64_t frame) noexcept override | |||
{ | |||
if (pData->options.transportMode == ENGINE_TRANSPORT_MODE_INTERNAL) | |||
@@ -1984,6 +2011,7 @@ private: | |||
jack_port_t* fRackPorts[kRackPortCount]; | |||
bool fTimebaseMaster; | |||
PatchbayGroupList fUsedGroups; | |||
PatchbayPortList fUsedPorts; | |||
PatchbayConnectionList fUsedConnections; | |||
@@ -236,6 +236,14 @@ protected: | |||
{ | |||
fEngine->transportPause(); | |||
} | |||
else if (std::strcmp(msg, "transport_bpm") == 0) | |||
{ | |||
double bpm; | |||
CARLA_SAFE_ASSERT_RETURN(readNextLineAsDouble(bpm), true); | |||
fEngine->transportBPM(bpm); | |||
} | |||
else if (std::strcmp(msg, "transport_relocate") == 0) | |||
{ | |||
uint64_t frame; | |||
@@ -1382,6 +1382,11 @@ class CarlaHostMeta(object): | |||
def transport_pause(self): | |||
raise NotImplementedError | |||
# Pause the engine transport. | |||
@abstractmethod | |||
def transport_bpm(self, bpm): | |||
raise NotImplementedError | |||
# Relocate the engine transport to a specific frame. | |||
@abstractmethod | |||
def transport_relocate(self, frame): | |||
@@ -1927,6 +1932,9 @@ class CarlaHostNull(CarlaHostMeta): | |||
def transport_pause(self): | |||
return | |||
def transport_bpm(self, bpm): | |||
return | |||
def transport_relocate(self, frame): | |||
return | |||
@@ -2206,6 +2214,9 @@ class CarlaHostDLL(CarlaHostMeta): | |||
self.lib.carla_transport_pause.argtypes = None | |||
self.lib.carla_transport_pause.restype = None | |||
self.lib.carla_transport_bpm.argtypes = [c_double] | |||
self.lib.carla_transport_bpm.restype = None | |||
self.lib.carla_transport_relocate.argtypes = [c_uint64] | |||
self.lib.carla_transport_relocate.restype = None | |||
@@ -2477,6 +2488,9 @@ class CarlaHostDLL(CarlaHostMeta): | |||
def transport_pause(self): | |||
self.lib.carla_transport_pause() | |||
def transport_bpm(self, bpm): | |||
self.lib.carla_transport_bpm(bpm) | |||
def transport_relocate(self, frame): | |||
self.lib.carla_transport_relocate(frame) | |||
@@ -2812,6 +2826,9 @@ class CarlaHostPlugin(CarlaHostMeta): | |||
def transport_pause(self): | |||
self.sendMsg(["transport_pause"]) | |||
def transport_bpm(self, bpm): | |||
self.sendMsg(["transport_bpm", bpm]) | |||
def transport_relocate(self, frame): | |||
self.sendMsg(["transport_relocate"]) | |||
@@ -292,7 +292,7 @@ class HostWindow(QMainWindow): | |||
self.ui.l_transport_frame.setMinimumWidth(minValueWidth + 3) | |||
self.ui.l_transport_time.setMinimumWidth(minValueWidth + 3) | |||
if host.isPlugin: | |||
if host.isControl or host.isPlugin: | |||
self.ui.b_transport_play.setEnabled(False) | |||
self.ui.b_transport_stop.setEnabled(False) | |||
self.ui.b_transport_backwards.setEnabled(False) | |||
@@ -441,6 +441,7 @@ class HostWindow(QMainWindow): | |||
self.ui.b_transport_stop.clicked.connect(self.slot_transportStop) | |||
self.ui.b_transport_backwards.clicked.connect(self.slot_transportBackwards) | |||
self.ui.b_transport_forwards.clicked.connect(self.slot_transportForwards) | |||
self.ui.dsb_transport_bpm.valueChanged.connect(self.slot_transportBpmChanged) | |||
self.ui.cb_transport_jack.clicked.connect(self.slot_transportJackEnabled) | |||
self.ui.cb_transport_link.clicked.connect(self.slot_transportLinkEnabled) | |||
@@ -1683,7 +1684,9 @@ class HostWindow(QMainWindow): | |||
self.fLastTransportBPM = bpm | |||
if bpm > 0.0: | |||
self.ui.dsb_transport_bpm.blockSignals(True) | |||
self.ui.dsb_transport_bpm.setValue(bpm) | |||
self.ui.dsb_transport_bpm.blockSignals(False) | |||
self.ui.dsb_transport_bpm.setStyleSheet("") | |||
else: | |||
self.ui.dsb_transport_bpm.setStyleSheet("QDoubleSpinBox { color: palette(mid); }") | |||
@@ -1725,6 +1728,10 @@ class HostWindow(QMainWindow): | |||
self.host.transport_relocate(newFrame) | |||
@pyqtSlot(float) | |||
def slot_transportBpmChanged(self, newValue): | |||
self.host.transport_bpm(newValue) | |||
@pyqtSlot() | |||
def slot_transportForwards(self): | |||
if self.fSampleRate == 0.0 or self.host.isPlugin or not self.host.is_engine_running(): | |||