Zrythm v2.0.0-alpha.1
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
tempo_map_qml_adapter.h
1// SPDX-FileCopyrightText: © 2025 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include "dsp/tempo_map.h"
7#include "utils/qt.h"
8
9#include <QObject>
10#include <QtQml/QQmlListProperty>
11#include <QtQmlIntegration/qqmlintegration.h>
12
13namespace zrythm::dsp
14{
15
16class TempoEventWrapper : public QObject
17{
18 Q_OBJECT
19 Q_PROPERTY (qint64 tick READ tick CONSTANT)
20 Q_PROPERTY (double bpm READ bpm CONSTANT)
21 Q_PROPERTY (CurveType curve READ curve CONSTANT)
22 QML_ELEMENT
23 QML_UNCREATABLE ("")
24
25public:
26 explicit TempoEventWrapper (
27 const TempoMap::TempoEvent &event,
28 QObject * parent = nullptr)
29 : QObject (parent), event_ (event)
30 {
31 }
32
33 enum class CurveType : std::uint8_t
34 {
37 };
38 Q_ENUM (CurveType)
39
40 qint64 tick () const { return event_.tick.in (units::ticks); }
41 double bpm () const { return event_.bpm.in (units::bpm); }
42 CurveType curve () const
43 {
44 return static_cast<CurveType> (std::to_underlying (event_.curve));
45 }
46
47private:
48 TempoMap::TempoEvent event_;
49};
50
51class TimeSignatureEventWrapper : public QObject
52{
53 Q_OBJECT
54 Q_PROPERTY (qint64 tick READ tick CONSTANT)
55 Q_PROPERTY (int numerator READ numerator CONSTANT)
56 Q_PROPERTY (int denominator READ denominator CONSTANT)
57 QML_ELEMENT
58 QML_UNCREATABLE ("")
59
60public:
61 explicit TimeSignatureEventWrapper (
62 const TempoMap::TimeSignatureEvent &event,
63 QObject * parent = nullptr)
64 : QObject (parent), event_ (event)
65 {
66 }
67
68 qint64 tick () const { return event_.tick.in (units::ticks); }
69 int numerator () const { return event_.numerator; }
70 int denominator () const { return event_.denominator; }
71
72private:
73 TempoMap::TimeSignatureEvent event_;
74};
75
76class MusicalPositionWrapper : public QObject
77{
78 Q_OBJECT
79 Q_PROPERTY (int bar READ bar CONSTANT)
80 Q_PROPERTY (int beat READ beat CONSTANT)
81 Q_PROPERTY (int sixteenth READ sixteenth CONSTANT)
82 Q_PROPERTY (int tick READ tick CONSTANT)
83 QML_ELEMENT
84 QML_UNCREATABLE ("")
85
86public:
87 explicit MusicalPositionWrapper (
88 const TempoMap::MusicalPosition &pos,
89 QObject * parent = nullptr)
90 : QObject (parent), pos_ (pos)
91 {
92 }
93
94 int bar () const { return pos_.bar; }
95 int beat () const { return pos_.beat; }
96 int sixteenth () const { return pos_.sixteenth; }
97 int tick () const { return pos_.tick; }
98
99private:
100 TempoMap::MusicalPosition pos_;
101};
102
103class TempoMapWrapper : public QObject
104{
105 Q_OBJECT
106 Q_PROPERTY (
107 QQmlListProperty<TempoEventWrapper> tempoEvents READ tempoEvents NOTIFY
108 tempoEventsChanged)
109 Q_PROPERTY (
110 QQmlListProperty<TimeSignatureEventWrapper> timeSignatureEvents READ
111 timeSignatureEvents NOTIFY timeSignatureEventsChanged)
112 Q_PROPERTY (
113 double sampleRate READ sampleRate WRITE setSampleRate NOTIFY
114 sampleRateChanged)
115 Q_PROPERTY (double baseBpm READ baseBpm WRITE setBaseBpm NOTIFY baseBpmChanged)
116 Q_PROPERTY (
117 int baseTimeSignatureNumerator READ baseTimeSignatureNumerator WRITE
118 setBaseTimeSignatureNumerator NOTIFY baseTimeSignatureChanged)
119 Q_PROPERTY (
120 int baseTimeSignatureDenominator READ baseTimeSignatureDenominator WRITE
121 setBaseTimeSignatureDenominator NOTIFY baseTimeSignatureChanged)
122 QML_NAMED_ELEMENT (TempoMap)
123 QML_UNCREATABLE ("")
124
125public:
126 explicit TempoMapWrapper (TempoMap &tempo_map, QObject * parent = nullptr)
127 : QObject (parent), tempo_map_ (tempo_map)
128 {
130 }
131
132 QQmlListProperty<TempoEventWrapper> tempoEvents ()
133 {
134 return { this, &tempoEventWrappers_ };
136
137 QQmlListProperty<TimeSignatureEventWrapper> timeSignatureEvents ()
138 {
139 return { this, &timeSigEventWrappers_ };
140 }
141
145 Q_INVOKABLE int timeSignatureNumeratorAtTick (int64_t tick) const;
146 Q_INVOKABLE int timeSignatureDenominatorAtTick (int64_t tick) const;
147
148 Q_INVOKABLE double tempoAtTick (int64_t tick) const;
149
150 // this should be static but i don't know how to use it from QML then
151 Q_INVOKABLE int getPpq () const
152 {
153 return TempoMap::get_ppq ().in (units::ticks);
154 }
155
156 double sampleRate () const
157 {
158 return tempo_map_.get_sample_rate ().in (units::sample_rate);
159 }
160 void setSampleRate (double sampleRate)
161 {
162 if (
163 qFuzzyCompare (
164 sampleRate, tempo_map_.get_sample_rate ().in (units::sample_rate)))
165 return;
166 tempo_map_.set_sample_rate (units::sample_rate (sampleRate));
167 Q_EMIT sampleRateChanged ();
168 }
169
170 // Base tempo at tick 0 (governs the region from tick 0 up to the first
171 // inserted tempo event, or the whole timeline if none).
172 double baseBpm () const { return tempo_map_.base_bpm ().in (units::bpm); }
173 void setBaseBpm (double bpm)
174 {
175 if (qFuzzyCompare (bpm, baseBpm ()))
176 return;
177 tempo_map_.set_base_bpm (units::bpm (bpm));
178 rebuildTempoWrappers ();
179 Q_EMIT baseBpmChanged ();
180 Q_EMIT tempoEventsChanged ();
181 }
182
183 int baseTimeSignatureNumerator () const
184 {
185 return tempo_map_.base_time_signature ().numerator;
186 }
187 int baseTimeSignatureDenominator () const
188 {
189 return tempo_map_.base_time_signature ().denominator;
190 }
191 void setBaseTimeSignatureNumerator (int numerator)
192 {
193 if (numerator == baseTimeSignatureNumerator ())
194 return;
195 tempo_map_.set_base_time_signature (
196 numerator, baseTimeSignatureDenominator ());
197 rebuildTimeSigWrappers ();
198 Q_EMIT baseTimeSignatureChanged ();
199 Q_EMIT timeSignatureEventsChanged ();
200 }
201 void setBaseTimeSignatureDenominator (int denominator)
202 {
203 if (denominator == baseTimeSignatureDenominator ())
204 return;
205 tempo_map_.set_base_time_signature (
206 baseTimeSignatureNumerator (), denominator);
207 rebuildTimeSigWrappers ();
208 Q_EMIT baseTimeSignatureChanged ();
209 Q_EMIT timeSignatureEventsChanged ();
210 }
211
212 Q_INVOKABLE void
213 addTempoEvent (qint64 tick, double bpm, TempoEventWrapper::CurveType curveType)
214 {
215 tempo_map_.add_tempo_event (
216 units::ticks (tick), units::bpm (bpm),
217 static_cast<TempoMap::CurveType> (curveType));
218 rebuildTempoWrappers ();
219 Q_EMIT tempoEventsChanged ();
220 }
221
222 Q_INVOKABLE void
223 addTimeSignatureEvent (qint64 tick, int numerator, int denominator)
224 {
225 tempo_map_.add_time_signature_event (
226 units::ticks (tick), numerator, denominator);
227 rebuildTimeSigWrappers ();
228 Q_EMIT timeSignatureEventsChanged ();
229 }
230
231 Q_INVOKABLE void clearTempoEvents ()
232 {
233 tempo_map_.clear_tempo_events ();
234 rebuildTempoWrappers ();
235 Q_EMIT tempoEventsChanged ();
236 }
237
238 Q_INVOKABLE void clearTimeSignatureEvents ()
239 {
240 tempo_map_.clear_time_signature_events ();
241 rebuildTimeSigWrappers ();
242 Q_EMIT timeSignatureEventsChanged ();
243 }
244
245 Q_INVOKABLE MusicalPositionWrapper * getMusicalPosition (int64_t tick) const
246 {
247 return new MusicalPositionWrapper (
248 tempo_map_.tick_to_musical_position (units::ticks (tick)));
249 }
250
251 Q_INVOKABLE QString getMusicalPositionString (int64_t tick) const;
252
253 Q_INVOKABLE int64_t
254 getTickFromMusicalPosition (int bar, int beat, int sixteenth, int tick) const
255 {
256 return tempo_map_
257 .musical_position_to_tick (
258 TempoMap::MusicalPosition{
259 .bar = bar, .beat = beat, .sixteenth = sixteenth, .tick = tick })
260 .asQuantity ()
261 .in (units::ticks);
262 }
263
264 // additional API when we want to avoid allocations and we only need 1 part
265
266 Q_INVOKABLE int getMusicalPositionBar (int64_t tick) const
267 {
268 return tempo_map_.tick_to_musical_position (units::ticks (tick)).bar;
269 }
270 Q_INVOKABLE int getMusicalPositionBeat (int64_t tick) const
271 {
272 return tempo_map_.tick_to_musical_position (units::ticks (tick)).beat;
273 }
274 Q_INVOKABLE int getMusicalPositionSixteenth (int64_t tick) const
275 {
276 return tempo_map_.tick_to_musical_position (units::ticks (tick)).sixteenth;
277 }
278 Q_INVOKABLE int getMusicalPositionTick (int64_t tick) const
279 {
280 return tempo_map_.tick_to_musical_position (units::ticks (tick)).tick;
281 }
282
286 void rebuildWrappers ()
287 {
288 rebuildTempoWrappers ();
289 rebuildTimeSigWrappers ();
290 }
291
293 const TempoMap &get_tempo_map () const { return tempo_map_; }
294
295Q_SIGNALS:
296 void tempoEventsChanged ();
297 void timeSignatureEventsChanged ();
298 void sampleRateChanged ();
299 void baseBpmChanged ();
300 void baseTimeSignatureChanged ();
301
302private:
303 void rebuildTempoWrappers ()
304 {
305 qDeleteAll (tempoEventWrappers_);
306 tempoEventWrappers_.clear ();
307 for (const auto &event : tempo_map_.tempo_events ())
308 {
309 tempoEventWrappers_.append (new TempoEventWrapper (event, this));
310 }
311 }
312
313 void rebuildTimeSigWrappers ()
314 {
315 qDeleteAll (timeSigEventWrappers_);
316 timeSigEventWrappers_.clear ();
317 for (const auto &event : tempo_map_.time_signature_events ())
318 {
319 timeSigEventWrappers_.append (
320 new TimeSignatureEventWrapper (event, this));
321 }
322 }
323
324private:
325 TempoMap &tempo_map_;
326 QList<TempoEventWrapper *> tempoEventWrappers_;
327 QList<TimeSignatureEventWrapper *> timeSigEventWrappers_;
328};
329
330} // namespace zrythm::dsp
331
332Q_DECLARE_METATYPE (zrythm::dsp::TempoMap::CurveType)
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
MusicalPosition tick_to_musical_position(units::tick_t tick) const
Convert ticks to musical position (bar:beat:sixteenth:tick).
void rebuildWrappers()
To be called when the tempo map has been modified directly.
const TempoMap & get_tempo_map() const
Read-only access to tempo map.
Q_INVOKABLE int timeSignatureNumeratorAtTick(int64_t tick) const
Returns the time signature at the given tick.