Zrythm v2.0.0-DEV
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 <string_view>
8#include <vector>
9
10#include "utils/units.h"
11
12#include <nlohmann/json_fwd.hpp>
13
14namespace zrythm::dsp
15{
16
36template <units::tick_t::NTTP PPQ> class FixedPpqTempoMap
37{
38 friend class TempoMapWrapper;
39
40public:
42 enum class CurveType : std::uint8_t
43 {
46 };
47
50 {
51 units::tick_t tick;
52 double bpm{};
54
55 friend void to_json (nlohmann::json &j, const TempoEvent &e);
56 friend void from_json (const nlohmann::json &j, TempoEvent &e);
57 };
58
61 {
62 units::tick_t tick;
63 int numerator{};
65
66 constexpr auto quarters_per_bar () const
67 {
68 return (numerator * 4) / denominator;
69 }
70
71 constexpr auto ticks_per_bar () const
72 {
73 return units::ticks (quarters_per_bar () * FixedPpqTempoMap::get_ppq ());
74 }
75
76 constexpr auto ticks_per_beat () const
77 {
78 return ticks_per_bar () / numerator;
79 }
80
81 constexpr auto
82 is_different_time_signature (const TimeSignatureEvent &other) const
83 {
84 return numerator != other.numerator || denominator != other.denominator;
85 }
86
87 friend void to_json (nlohmann::json &j, const TimeSignatureEvent &e);
88 friend void from_json (const nlohmann::json &j, TimeSignatureEvent &e);
89 };
90
93 {
94 int bar{ 1 };
95 int beat{ 1 };
96 int sixteenth{ 1 };
97 int tick{};
98
99 friend bool
100 operator== (const MusicalPosition &lhs, const MusicalPosition &rhs) =
101 default;
102 };
103
108 explicit FixedPpqTempoMap (units::precise_sample_rate_t sampleRate)
109 : sample_rate_ (sampleRate)
110 {
111 }
112
114 void set_sample_rate (units::precise_sample_rate_t sampleRate)
115 {
116 sample_rate_ = sampleRate;
117 }
118
130 void add_tempo_event (units::tick_t tick, double bpm, CurveType curve);
131
133 void remove_tempo_event (units::tick_t tick);
134
149 void
150 add_time_signature_event (units::tick_t tick, int numerator, int denominator);
151
153 void remove_time_signature_event (units::tick_t tick);
154
156 auto
157 tick_to_seconds (units::precise_tick_t tick) const -> units::precise_second_t;
158
160 units::precise_sample_t tick_to_samples (units::precise_tick_t tick) const
161 {
162 return tick_to_seconds (tick) * sample_rate_;
163 }
164
165 units::sample_t tick_to_samples_rounded (units::precise_tick_t tick) const
166 {
167 return au::round_as<int64_t> (units::samples, tick_to_samples (tick));
168 }
169
171 units::precise_tick_t seconds_to_tick (units::precise_second_t seconds) const;
172
174 units::precise_tick_t samples_to_tick (units::precise_sample_t samples) const
175 {
176 const auto seconds = samples / sample_rate_;
177 return seconds_to_tick (seconds);
178 }
179
185 TimeSignatureEvent time_signature_at_tick (units::tick_t tick) const;
186
192 double tempo_at_tick (units::tick_t tick) const;
193
199 MusicalPosition tick_to_musical_position (units::tick_t tick) const;
200
201 MusicalPosition samples_to_musical_position (units::sample_t samples) const;
202
210 units::tick_t musical_position_to_tick (const MusicalPosition &pos) const;
211
213 static consteval int get_ppq () { return from_nttp (PPQ).in (units::ticks); }
214
216 double get_sample_rate () const
217 {
218 return sample_rate_.in (units::sample_rate);
219 }
220
221 void set_default_bpm (double bpm) { default_tempo_.bpm = bpm; }
222 void set_default_time_signature (int numerator, int denominator)
223 {
224 default_time_sig_.numerator = numerator;
225 default_time_sig_.denominator = denominator;
226 }
227
228private:
230 const std::vector<TempoEvent> &get_tempo_events () const { return events_; }
231
233 const std::vector<TimeSignatureEvent> &get_time_signature_events () const
234 {
235 return time_sig_events_;
236 }
237
239 void rebuild_cumulative_times ();
240
242 units::precise_second_t compute_segment_time (
243 const TempoEvent &start,
244 const TempoEvent &end,
245 units::tick_t segmentTicks) const;
246
247 auto get_events_or_default () const;
248 auto get_time_signature_events_or_default () const;
249
251 void clear_tempo_events ()
252 {
253 events_.clear ();
254 cumulative_seconds_.clear ();
255 }
256
258 void clear_time_signature_events () { time_sig_events_.clear (); }
259
260 static constexpr std::string_view kTempoChangesKey = "tempoChanges";
261 static constexpr std::string_view kTimeSignaturesKey = "timeSignatures";
262 friend void to_json (nlohmann::json &j, const FixedPpqTempoMap &tempo_map);
263 friend void from_json (const nlohmann::json &j, FixedPpqTempoMap &tempo_map);
264
265private:
266 units::precise_sample_rate_t sample_rate_;
267 static constexpr auto ticks_per_sixteenth_ =
268 from_nttp (PPQ) / 4;
269
270 static constexpr auto DEFAULT_BPM_EVENT = TempoEvent{
271 units::ticks (0), 120.0, CurveType::Constant
272 };
273 static constexpr auto DEFAULT_TIME_SIG_EVENT =
274 TimeSignatureEvent{ units::ticks (0), 4, 4 };
275 static constexpr auto DEFAULT_CUMULATIVE_SECONDS = units::seconds (0.0);
276
277 // Default tempo and time signature to be used when no events are present
278 TempoEvent default_tempo_{ DEFAULT_BPM_EVENT };
279 TimeSignatureEvent default_time_sig_{ DEFAULT_TIME_SIG_EVENT };
280
281 std::vector<TempoEvent> events_;
282 std::vector<TimeSignatureEvent> time_sig_events_;
283 std::vector<units::precise_second_t>
284 cumulative_seconds_;
285};
286
290using TempoMap = FixedPpqTempoMap<units::PPQ>;
291
292extern template class FixedPpqTempoMap<units::PPQ>;
293}
Manages tempo and time signature events for a DAW timeline.
Definition tempo_map.h:37
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:43
void add_tempo_event(units::tick_t tick, double bpm, CurveType curve)
Add a tempo event.
FixedPpqTempoMap(units::precise_sample_rate_t sampleRate)
Construct a new FixedPpqTempoMap object.
Definition tempo_map.h:108
auto tick_to_seconds(units::precise_tick_t tick) const -> units::precise_second_t
Convert fractional ticks to seconds.
double tempo_at_tick(units::tick_t tick) const
Get the tempo event active at the given tick.
units::precise_tick_t samples_to_tick(units::precise_sample_t samples) const
Convert samples to fractional ticks.
Definition tempo_map.h:174
MusicalPosition tick_to_musical_position(units::tick_t tick) const
Convert ticks to musical position (bar:beat:sixteenth:tick).
void remove_time_signature_event(units::tick_t tick)
Remove a time signature event at the specified tick.
units::precise_tick_t seconds_to_tick(units::precise_second_t seconds) const
Convert seconds to fractional ticks.
static consteval int get_ppq()
Get pulses per quarter note.
Definition tempo_map.h:213
units::tick_t musical_position_to_tick(const MusicalPosition &pos) const
Convert musical position to ticks.
units::precise_sample_t tick_to_samples(units::precise_tick_t tick) const
Convert fractional ticks to samples.
Definition tempo_map.h:160
double get_sample_rate() const
Get current sample rate.
Definition tempo_map.h:216
void set_sample_rate(units::precise_sample_rate_t sampleRate)
Set the sample rate.
Definition tempo_map.h:114
Musical position representation.
Definition tempo_map.h:93
int tick
Ticks in sixteenth (0-indexed).
Definition tempo_map.h:97
int sixteenth
Sixteenth in beat (1-indexed).
Definition tempo_map.h:96
CurveType curve
Curve type from this event to the next.
Definition tempo_map.h:53
units::tick_t tick
Position in ticks.
Definition tempo_map.h:51
Time signature event definition.
Definition tempo_map.h:61
units::tick_t tick
Position in ticks.
Definition tempo_map.h:62