Zrythm v2.0.0-DEV
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>
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; }
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 QML_NAMED_ELEMENT (TempoMap)
116 QML_UNCREATABLE ("")
117
118public:
119 explicit TempoMapWrapper (TempoMap &tempo_map, QObject * parent = nullptr)
120 : QObject (parent), tempo_map_ (tempo_map)
121 {
123 }
124
125 QQmlListProperty<TempoEventWrapper> tempoEvents ()
126 {
127 return { this, &tempoEventWrappers_ };
128 }
129
130 QQmlListProperty<TimeSignatureEventWrapper> timeSignatureEvents ()
131 {
132 return { this, &timeSigEventWrappers_ };
133 }
134
138 Q_INVOKABLE int timeSignatureNumeratorAtTick (int64_t tick) const;
139 Q_INVOKABLE int timeSignatureDenominatorAtTick (int64_t tick) const;
140
141 Q_INVOKABLE double tempoAtTick (int64_t tick) const;
142
143 // this should be static but i don't know how to use it from QML then
144 Q_INVOKABLE int getPpq () { return TempoMap::get_ppq (); }
145
146 double sampleRate () const { return tempo_map_.get_sample_rate (); }
147 void setSampleRate (double sampleRate)
148 {
149 if (qFuzzyCompare (sampleRate, tempo_map_.get_sample_rate ()))
150 return;
151 tempo_map_.set_sample_rate (units::sample_rate (sampleRate));
152 Q_EMIT sampleRateChanged ();
153 }
154
155 Q_INVOKABLE void
156 addTempoEvent (qint64 tick, double bpm, TempoEventWrapper::CurveType curveType)
157 {
158 tempo_map_.add_tempo_event (
159 units::ticks (tick), bpm, static_cast<TempoMap::CurveType> (curveType));
160 rebuildTempoWrappers ();
161 Q_EMIT tempoEventsChanged ();
162 }
163
164 Q_INVOKABLE void
165 addTimeSignatureEvent (qint64 tick, int numerator, int denominator)
166 {
167 tempo_map_.add_time_signature_event (
168 units::ticks (tick), numerator, denominator);
169 rebuildTimeSigWrappers ();
170 Q_EMIT timeSignatureEventsChanged ();
171 }
172
173 Q_INVOKABLE void clearTempoEvents ()
174 {
175 tempo_map_.clear_tempo_events ();
176 rebuildTempoWrappers ();
177 Q_EMIT tempoEventsChanged ();
178 }
179
180 Q_INVOKABLE void clearTimeSignatureEvents ()
181 {
182 tempo_map_.clear_time_signature_events ();
183 rebuildTimeSigWrappers ();
184 Q_EMIT timeSignatureEventsChanged ();
185 }
186
187 Q_INVOKABLE MusicalPositionWrapper * getMusicalPosition (int64_t tick) const
188 {
189 return new MusicalPositionWrapper (
190 tempo_map_.tick_to_musical_position (units::ticks (tick)));
191 }
192
193 Q_INVOKABLE QString getMusicalPositionString (int64_t tick) const;
194
195 Q_INVOKABLE int64_t
196 getTickFromMusicalPosition (int bar, int beat, int sixteenth, int tick) const
197 {
198 return tempo_map_
199 .musical_position_to_tick (
200 TempoMap::MusicalPosition{
201 .bar = bar, .beat = beat, .sixteenth = sixteenth, .tick = tick })
202 .in (units::ticks);
203 }
204
205 // additional API when we want to avoid allocations and we only need 1 part
206
207 Q_INVOKABLE int getMusicalPositionBar (int64_t tick) const
208 {
209 return tempo_map_.tick_to_musical_position (units::ticks (tick)).bar;
210 }
211 Q_INVOKABLE int getMusicalPositionBeat (int64_t tick) const
212 {
213 return tempo_map_.tick_to_musical_position (units::ticks (tick)).beat;
214 }
215 Q_INVOKABLE int getMusicalPositionSixteenth (int64_t tick) const
216 {
217 return tempo_map_.tick_to_musical_position (units::ticks (tick)).sixteenth;
218 }
219 Q_INVOKABLE int getMusicalPositionTick (int64_t tick) const
220 {
221 return tempo_map_.tick_to_musical_position (units::ticks (tick)).tick;
222 }
223
226
227 void rebuildWrappers ()
229 rebuildTempoWrappers ();
230 rebuildTimeSigWrappers ();
231 }
232
234 const TempoMap &get_tempo_map () const { return tempo_map_; }
235
236Q_SIGNALS:
237 void tempoEventsChanged ();
238 void timeSignatureEventsChanged ();
239 void sampleRateChanged ();
240
241private:
242 void rebuildTempoWrappers ()
243 {
244 qDeleteAll (tempoEventWrappers_);
245 tempoEventWrappers_.clear ();
246 for (const auto &event : tempo_map_.get_tempo_events ())
247 {
248 tempoEventWrappers_.append (new TempoEventWrapper (event, this));
249 }
250 }
251
252 void rebuildTimeSigWrappers ()
253 {
254 qDeleteAll (timeSigEventWrappers_);
255 timeSigEventWrappers_.clear ();
256 for (const auto &event : tempo_map_.get_time_signature_events ())
257 {
258 timeSigEventWrappers_.append (
259 new TimeSignatureEventWrapper (event, this));
260 }
261 }
262
263private:
264 TempoMap &tempo_map_;
265 QList<TempoEventWrapper *> tempoEventWrappers_;
266 QList<TimeSignatureEventWrapper *> timeSigEventWrappers_;
267};
268
269} // namespace zrythm::dsp
270
271Q_DECLARE_METATYPE (zrythm::dsp::TempoMap::CurveType)
double get_sample_rate() const
Get current sample rate.
Definition tempo_map.h:627
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.