| @@ -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(): | |||