Zrythm v2.0.0-alpha.1
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
tempo_map.h
1// SPDX-FileCopyrightText: © 2025-2026 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include <cstdint>
7#include <span>
8#include <string_view>
9#include <vector>
10
11#include "dsp/tick_types.h"
12#include "utils/units.h"
13
14#include <nlohmann/json_fwd.hpp>
15
16namespace zrythm::dsp
17{
18
41template <units::tick_t::NTTP PPQ> class FixedPpqTempoMap
42{
43 friend class TempoMapWrapper;
44
45public:
47 enum class CurveType : std::uint8_t
48 {
51 };
52
55 {
56 units::tick_t tick;
57 units::bpm_t bpm{};
59
60 friend void to_json (nlohmann::json &j, const TempoEvent &e);
61 friend void from_json (const nlohmann::json &j, TempoEvent &e);
62 };
63
66 {
67 units::tick_t tick;
68 int numerator{};
70
71 constexpr auto quarters_per_bar () const
72 {
73 return (numerator * 4) / denominator;
74 }
75
76 constexpr auto ticks_per_bar () const
77 {
78 return quarters_per_bar () * FixedPpqTempoMap::get_ppq ();
79 }
80
81 constexpr auto ticks_per_beat () const
82 {
83 return ticks_per_bar () / numerator;
84 }
85
86 constexpr auto
87 is_different_time_signature (const TimeSignatureEvent &other) const
88 {
89 return numerator != other.numerator || denominator != other.denominator;
90 }
91
92 friend void to_json (nlohmann::json &j, const TimeSignatureEvent &e);
93 friend void from_json (const nlohmann::json &j, TimeSignatureEvent &e);
94 };
95
98 {
99 int bar{ 1 };
100 int beat{ 1 };
101 int sixteenth{ 1 };
102 int tick{};
103
104 friend bool
105 operator== (const MusicalPosition &lhs, const MusicalPosition &rhs) =
106 default;
107 };
108
113 explicit FixedPpqTempoMap (units::precise_sample_rate_t sampleRate)
114 : sample_rate_ (sampleRate)
115 {
116 rebuild_time_signature_cache ();
117 }
118
120 void set_sample_rate (units::precise_sample_rate_t sampleRate)
121 {
122 sample_rate_ = sampleRate;
123 }
124
136 void add_tempo_event (units::tick_t tick, units::bpm_t bpm, CurveType curve);
137
139 void remove_tempo_event (units::tick_t tick);
140
155 void
156 add_time_signature_event (units::tick_t tick, int numerator, int denominator);
157
159 void remove_time_signature_event (units::tick_t tick);
160
162 auto tick_to_seconds (TimelineTick tick) const -> units::precise_second_t;
163
165 units::precise_sample_t tick_to_samples (TimelineTick tick) const
166 {
167 return tick_to_seconds (tick) * sample_rate_;
168 }
169
170 units::sample_t tick_to_samples_rounded (TimelineTick tick) const
171 {
172 return au::round_as<int64_t> (units::samples, tick_to_samples (tick));
173 }
174
176 TimelineTick seconds_to_tick (units::precise_second_t seconds) const;
177
179 TimelineTick samples_to_tick (units::precise_sample_t samples) const
180 {
181 const auto seconds = samples / sample_rate_;
182 return seconds_to_tick (seconds);
183 }
184
190 TimeSignatureEvent time_signature_at_tick (units::tick_t tick) const;
191
197 units::bpm_t tempo_at_tick (units::tick_t tick) const;
198
206 std::span<const TempoEvent> tempo_events () const noexcept { return events_; }
207
215 std::span<const TimeSignatureEvent> time_signature_events () const noexcept
216 {
217 return time_sig_events_;
218 }
219
225 MusicalPosition tick_to_musical_position (units::tick_t tick) const;
226
227 MusicalPosition samples_to_musical_position (units::sample_t samples) const;
228
236 TimelineTickI musical_position_to_tick (const MusicalPosition &pos) const;
237
239 static consteval auto get_ppq () { return from_nttp (PPQ); }
240
242 auto get_sample_rate () const { return sample_rate_; }
243
245 units::bpm_t base_bpm () const { return base_bpm_; }
246
248 void set_base_bpm (units::bpm_t bpm)
249 {
250 base_bpm_ = bpm;
251 rebuild_cumulative_times ();
252 }
253
255 const TimeSignatureEvent &base_time_signature () const
256 {
257 return base_time_sig_;
258 }
259
261 void set_base_time_signature (int numerator, int denominator)
262 {
263 throw_if_invalid_time_signature (numerator, denominator);
264 base_time_sig_.numerator = numerator;
265 base_time_sig_.denominator = denominator;
266 rebuild_time_signature_cache ();
267 }
268
278 std::span<const TimeSignatureEvent>
280 {
281 return effective_time_sig_events_;
282 }
283
284private:
286 void rebuild_cumulative_times ();
287
290 void rebuild_time_signature_cache ();
291
295 static void throw_if_invalid_time_signature (int numerator, int denominator);
296
298 units::precise_second_t compute_segment_time (
299 const TempoEvent &start,
300 const TempoEvent &end,
301 units::tick_t segmentTicks) const;
302
304 void clear_tempo_events ()
305 {
306 events_.clear ();
307 cumulative_seconds_.clear ();
308 }
309
311 void clear_time_signature_events ()
312 {
313 time_sig_events_.clear ();
314 rebuild_time_signature_cache ();
315 }
316
317 static constexpr std::string_view kTempoChangesKey = "tempoChanges";
318 static constexpr std::string_view kTimeSignaturesKey = "timeSignatures";
319 static constexpr std::string_view kBaseBpmKey = "baseBpm";
320 static constexpr std::string_view kBaseTimeSignatureKey = "baseTimeSignature";
321 friend void to_json (nlohmann::json &j, const FixedPpqTempoMap &tempo_map);
322 friend void from_json (const nlohmann::json &j, FixedPpqTempoMap &tempo_map);
323
324private:
325 units::precise_sample_rate_t sample_rate_;
326 static constexpr auto ticks_per_sixteenth_ =
327 from_nttp (PPQ) / 4;
328
329 // Base tempo and time signature anchored at tick 0. These govern the region
330 // from tick 0 up to the first inserted event (constant), unless an inserted
331 // event exists at tick 0.
332 units::bpm_t base_bpm_{ units::bpm (120.0) };
333 TimeSignatureEvent base_time_sig_{ units::ticks (0), 4, 4 };
334
335 std::vector<TempoEvent> events_;
336 std::vector<TimeSignatureEvent> time_sig_events_;
337 std::vector<units::precise_second_t>
338 cumulative_seconds_;
339 // Cache of time-signature events with the base signature prepended at tick 0
340 // when no inserted event sits there. Rebuilt on every mutation so the read
341 // path (@ref effective_time_signature_events) stays allocation-free.
342 std::vector<TimeSignatureEvent> effective_time_sig_events_;
343};
344
348using TempoMap = FixedPpqTempoMap<units::PPQ>;
349
350extern template class FixedPpqTempoMap<units::PPQ>;
351}
Manages tempo and time signature events for a DAW timeline.
Definition tempo_map.h:42
auto tick_to_seconds(TimelineTick tick) const -> units::precise_second_t
Convert timeline ticks to seconds.
void add_tempo_event(units::tick_t tick, units::bpm_t bpm, CurveType curve)
Add a tempo event.
void remove_tempo_event(units::tick_t tick)
Remove a tempo event at the specified tick.
TimeSignatureEvent time_signature_at_tick(units::tick_t tick) const
Get the time signature event active at the given tick.
void add_time_signature_event(units::tick_t tick, int numerator, int denominator)
Add a time signature event.
CurveType
Tempo curve type (constant or linear ramp).
Definition tempo_map.h:48
FixedPpqTempoMap(units::precise_sample_rate_t sampleRate)
Construct a new FixedPpqTempoMap object.
Definition tempo_map.h:113
std::span< const TimeSignatureEvent > time_signature_events() const noexcept
Read-only view of all inserted time signature events, sorted ascending by tick.
Definition tempo_map.h:215
std::span< const TempoEvent > tempo_events() const noexcept
Read-only view of all inserted tempo events, sorted ascending by tick.
Definition tempo_map.h:206
const TimeSignatureEvent & base_time_signature() const
Base time signature at tick 0.
Definition tempo_map.h:255
TimelineTickI musical_position_to_tick(const MusicalPosition &pos) const
Convert musical position to ticks.
auto get_sample_rate() const
Get current sample rate.
Definition tempo_map.h:242
std::span< const TimeSignatureEvent > effective_time_signature_events() const noexcept
Read-only view of the time signature events with the base signature prepended at tick 0 when no inser...
Definition tempo_map.h:279
void set_base_bpm(units::bpm_t bpm)
Set the base tempo at tick 0 and recompute the lead-segment timing.
Definition tempo_map.h:248
MusicalPosition tick_to_musical_position(units::tick_t tick) const
Convert ticks to musical position (bar:beat:sixteenth:tick).
TimelineTick samples_to_tick(units::precise_sample_t samples) const
Convert samples to timeline ticks.
Definition tempo_map.h:179
void remove_time_signature_event(units::tick_t tick)
Remove a time signature event at the specified tick.
void set_base_time_signature(int numerator, int denominator)
Set the base time signature at tick 0.
Definition tempo_map.h:261
units::bpm_t tempo_at_tick(units::tick_t tick) const
Get the tempo (BPM) active at the given tick.
units::precise_sample_t tick_to_samples(TimelineTick tick) const
Convert timeline ticks to samples.
Definition tempo_map.h:165
TimelineTick seconds_to_tick(units::precise_second_t seconds) const
Convert seconds to timeline ticks.
static consteval auto get_ppq()
Get pulses per quarter note.
Definition tempo_map.h:239
units::bpm_t base_bpm() const
Base tempo at tick 0.
Definition tempo_map.h:245
void set_sample_rate(units::precise_sample_rate_t sampleRate)
Set the sample rate.
Definition tempo_map.h:120
Musical position representation.
Definition tempo_map.h:98
int tick
Ticks in sixteenth (0-indexed).
Definition tempo_map.h:102
int sixteenth
Sixteenth in beat (1-indexed).
Definition tempo_map.h:101
CurveType curve
Curve type from this event to the next.
Definition tempo_map.h:58
units::tick_t tick
Position in ticks.
Definition tempo_map.h:56
Time signature event definition.
Definition tempo_map.h:66
units::tick_t tick
Position in ticks.
Definition tempo_map.h:67