47class Transport :
public QObject
52 bool loopEnabled READ loopEnabled WRITE setLoopEnabled NOTIFY
55 bool recordEnabled READ recordEnabled WRITE setRecordEnabled NOTIFY
58 bool punchEnabled READ punchEnabled WRITE setPunchEnabled NOTIFY
61 zrythm::dsp::ITransport::PlayState playState READ getPlayState WRITE
62 setPlayState NOTIFY playStateChanged)
80 static constexpr auto REPEATED_BACKWARD_MS = au::milli (units::seconds) (240);
152 std::function<int ()> metronome_countin_bars_;
159 std::function<int ()> recording_preroll_bars_;
166 std::pair<units::sample_t, units::sample_t> loop_range,
167 std::pair<units::sample_t, units::sample_t> punch_range,
168 units::sample_t playhead_position,
171 PlayState play_state,
175 : loop_range_ (loop_range), punch_range_ (punch_range),
176 playhead_position_ (playhead_position),
179 play_state_ (play_state), loop_enabled_ (loop_enabled),
184 std::pair<units::sample_t, units::sample_t>
189 std::pair<units::sample_t, units::sample_t>
194 PlayState get_play_state () const noexcept
override {
return play_state_; }
198 return playhead_position_;
200 bool loop_enabled () const noexcept
override {
return loop_enabled_; }
202 bool punch_enabled () const noexcept
override {
return punch_enabled_; }
205 return recording_enabled_;
210 return recording_preroll_frames_remaining_;
215 return metronome_countin_frames_remaining_;
218 void set_play_state (dsp::ITransport::PlayState play_state)
220 play_state_ = play_state;
222 void set_position (units::sample_t position)
224 playhead_position_ = position;
226 void consume_metronome_countin_samples (units::sample_t samples)
228 metronome_countin_frames_remaining_ -= samples;
230 void consume_recording_preroll_samples (units::sample_t samples)
232 recording_preroll_frames_remaining_ -= samples;
236 std::pair<units::sample_t, units::sample_t> loop_range_;
237 std::pair<units::sample_t, units::sample_t> punch_range_;
238 units::sample_t playhead_position_;
239 units::sample_t recording_preroll_frames_remaining_;
240 units::sample_t metronome_countin_frames_remaining_;
241 PlayState play_state_;
244 bool recording_enabled_;
248 const dsp::TempoMapWrapper &tempo_map_wrapper,
250 QObject * parent =
nullptr);
256 bool loopEnabled ()
const {
return loop_; }
257 void setLoopEnabled (
bool enabled);
258 Q_SIGNAL
void loopEnabledChanged (
bool enabled);
260 bool recordEnabled ()
const {
return recording_; }
261 void setRecordEnabled (
bool enabled);
262 Q_SIGNAL
void recordEnabledChanged (
bool enabled);
264 bool punchEnabled ()
const {
return punch_mode_; }
265 void setPunchEnabled (
bool enabled);
266 Q_SIGNAL
void punchEnabledChanged (
bool enabled);
268 PlayState getPlayState ()
const {
return play_state_; }
269 void setPlayState (PlayState state);
270 Q_SIGNAL
void playStateChanged (PlayState state);
272 dsp::PlayheadQmlWrapper * playhead ()
const
274 return playhead_adapter_.get ();
276 dsp::TimelinePosition * cuePosition ()
const {
return cue_position_.get (); }
277 dsp::TimelinePosition * loopStartPosition ()
const
279 return loop_start_position_.get ();
281 dsp::TimelinePosition * loopEndPosition ()
const
283 return loop_end_position_.get ();
287 return punch_in_position_.get ();
291 return punch_out_position_.get ();
302 Q_INVOKABLE
void requestRoll () [[clang::blocking]];
313 Q_INVOKABLE
void movePlayhead (
double ticks,
bool setCuePoint);
322 get_playhead_position_in_audio_thread () const noexcept [[clang::nonblocking]]
324 return playhead_.position_during_processing_rounded ();
329 Q_INVOKABLE
bool isRolling ()
const
331 return play_state_ == PlayState::Rolling;
334 Q_INVOKABLE
bool isPaused ()
const
336 return play_state_ == PlayState::Paused;
345 units::sample_t nframes)
noexcept [[clang::nonblocking]];
347 void set_play_state_rt_safe (PlayState state);
361 void move_playhead (units::precise_tick_t target_ticks,
bool set_cue_point);
373 std::vector<units::precise_tick_t> marker_ticks;
374 static_assert (__cpp_lib_containers_ranges >= 202202L);
375 marker_ticks.append_range (extra_markers);
376 marker_ticks.emplace_back (units::ticks (cue_position_->ticks ()));
377 marker_ticks.emplace_back (units::ticks (loop_start_position_->ticks ()));
378 marker_ticks.emplace_back (units::ticks (loop_end_position_->ticks ()));
379 marker_ticks.emplace_back ();
380 std::ranges::sort (marker_ticks);
385 const auto &[index, marker_tick] :
386 marker_ticks | utils::views::enumerate | std::views::reverse)
388 if (marker_tick >= playhead_.position_ticks ())
392 isRolling () && index > 0
393 && (playhead_.get_tempo_map ().tick_to_seconds (
394 TimelineTick{ playhead_.position_ticks () })
395 - playhead_.get_tempo_map ().tick_to_seconds (
396 TimelineTick{ marker_tick }))
397 < REPEATED_BACKWARD_MS)
408 for (
const auto &marker : marker_ticks)
419 bool position_is_inside_punch_range (units::sample_t pos)
const;
421 auto playhead_ticks_before_pause () const [[clang::blocking]]
423 return playhead_before_pause_;
433 assert (countin_frames_remaining_ >= samples);
434 countin_frames_remaining_ -= samples;
444 assert (recording_preroll_frames_remaining_ >= samples);
445 recording_preroll_frames_remaining_ -= samples;
448 auto get_snapshot () const noexcept [[clang::nonblocking]]
450 decltype (rt_markers_)::ScopedAccess<farbot::ThreadType::realtime> access{
453 return TransportSnapshot{
454 { units::samples (access->loop_start_samples),
455 units::samples (access->loop_end_samples) },
456 { units::samples (access->punch_in_samples),
457 units::samples (access->punch_out_samples) },
458 get_playhead_position_in_audio_thread (),
459 recording_preroll_frames_remaining_,
460 countin_frames_remaining_,
468 friend void init_from (
470 const Transport &other,
474 static constexpr auto kPlayheadKey =
"playheadPosition"sv;
475 static constexpr auto kCuePosKey =
"cuePosition"sv;
476 static constexpr auto kLoopStartPosKey =
"loopStartPosition"sv;
477 static constexpr auto kLoopEndPosKey =
"loopEndPosition"sv;
478 static constexpr auto kPunchInPosKey =
"punchInPosition"sv;
479 static constexpr auto kPunchOutPosKey =
"punchOutPosition"sv;
480 friend void to_json (nlohmann::json &j,
const Transport &transport);
481 friend void from_json (
const nlohmann::json &j, Transport &transport);
487 bool can_user_move_playhead ()
const;
490 struct MarkerSnapshot
492 int64_t loop_start_samples{};
493 int64_t loop_end_samples{};
494 int64_t punch_in_samples{};
495 int64_t punch_out_samples{};
498 void update_rt_marker_snapshot ();
502 dsp::Playhead playhead_;
503 utils::QObjectUniquePtr<dsp::PlayheadQmlWrapper> playhead_adapter_;
506 utils::QObjectUniquePtr<dsp::TimelinePosition> cue_position_;
509 utils::QObjectUniquePtr<dsp::TimelinePosition> loop_start_position_;
512 utils::QObjectUniquePtr<dsp::TimelinePosition> loop_end_position_;
515 utils::QObjectUniquePtr<dsp::TimelinePosition> punch_in_position_;
518 utils::QObjectUniquePtr<dsp::TimelinePosition> punch_out_position_;
526 mutable farbot::RealtimeObject<
528 farbot::RealtimeObjectOptions::nonRealtimeMutatable>
535 bool punch_mode_{
false };
539 bool recording_{
false };
542 units::sample_t recording_preroll_frames_remaining_;
545 units::sample_t countin_frames_remaining_;
552 units::precise_tick_t playhead_before_pause_;
555 PlayState play_state_{ PlayState::Paused };
564 QTimer * property_notification_timer_ =
nullptr;
565 std::atomic<bool> needs_property_notification_{
false };