try to clean up locking issues with TempoMap
[ardour.git] / libs / ardour / ardour / tempo.h
1 /*
2     Copyright (C) 2000 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #ifndef __ardour_tempo_h__
21 #define __ardour_tempo_h__
22
23 #include <list>
24 #include <string>
25 #include <vector>
26 #include <cmath>
27 #include <glibmm/thread.h>
28
29 #include "pbd/undo.h"
30 #include "pbd/stateful.h"
31 #include "pbd/statefuldestructible.h"
32
33 #include "evoral/types.hpp"
34
35 #include "ardour/ardour.h"
36
37 class XMLNode;
38
39 class BBTTest;
40 class FrameposPlusBeatsTest;
41 class TempoTest;
42
43 namespace ARDOUR {
44
45 class Meter;
46 class TempoMap;
47
48 class Tempo {
49   public:
50         Tempo (double bpm, double type=4.0) // defaulting to quarter note
51                 : _beats_per_minute (bpm), _note_type(type) {}
52
53         double beats_per_minute () const { return _beats_per_minute;}
54         double note_type () const { return _note_type;}
55         double frames_per_beat (framecnt_t sr) const;
56
57   protected:
58         double _beats_per_minute;
59         double _note_type;
60 };
61
62 class Meter {
63   public:
64         Meter (double dpb, double bt)
65                 : _divisions_per_bar (dpb), _note_type (bt) {}
66
67         double divisions_per_bar () const { return _divisions_per_bar; }
68         double note_divisor() const { return _note_type; }
69
70         double frames_per_bar (const Tempo&, framecnt_t sr) const;
71         double frames_per_division (const Tempo&, framecnt_t sr) const;
72
73   protected:
74         /** The number of divisions in a bar.  This is a floating point value because
75             there are musical traditions on our planet that do not limit
76             themselves to integral numbers of beats per bar.
77         */
78         double _divisions_per_bar;
79
80         /** The type of "note" that a division represents.  For example, 4.0 is
81             a quarter (crotchet) note, 8.0 is an eighth (quaver) note, etc.
82         */
83         double _note_type;
84 };
85
86 class MetricSection {
87   public:
88         MetricSection (const Timecode::BBT_Time& start)
89                 : _start (start), _frame (0), _movable (true) {}
90         MetricSection (framepos_t start)
91                 : _frame (start), _movable (true) {}
92
93         virtual ~MetricSection() {}
94
95         const Timecode::BBT_Time& start() const { return _start; }
96         framepos_t                frame() const { return _frame; }
97
98         void set_movable (bool yn) { _movable = yn; }
99         bool movable() const { return _movable; }
100
101         virtual void set_frame (framepos_t f) {
102                 _frame = f;
103         }
104
105         virtual void set_start (const Timecode::BBT_Time& w) {
106                 _start = w;
107         }
108
109         /* MeterSections are not stateful in the full sense,
110            but we do want them to control their own
111            XML state information.
112         */
113         virtual XMLNode& get_state() const = 0;
114
115         int compare (const MetricSection&) const;
116         bool operator== (const MetricSection& other) const;
117         bool operator!= (const MetricSection& other) const;
118
119   private:
120         Timecode::BBT_Time _start;
121         framepos_t         _frame;
122         bool               _movable;
123 };
124
125 class MeterSection : public MetricSection, public Meter {
126   public:
127         MeterSection (const Timecode::BBT_Time& start, double bpb, double note_type)
128                 : MetricSection (start), Meter (bpb, note_type) {}
129         MeterSection (framepos_t start, double bpb, double note_type)
130                 : MetricSection (start), Meter (bpb, note_type) {}
131         MeterSection (const XMLNode&);
132
133         static const std::string xml_state_node_name;
134
135         XMLNode& get_state() const;
136 };
137
138 class TempoSection : public MetricSection, public Tempo {
139   public:
140         TempoSection (const Timecode::BBT_Time& start, double qpm, double note_type)
141                 : MetricSection (start), Tempo (qpm, note_type), _bar_offset (-1.0)  {}
142         TempoSection (framepos_t start, double qpm, double note_type)
143                 : MetricSection (start), Tempo (qpm, note_type), _bar_offset (-1.0) {}
144         TempoSection (const XMLNode&);
145
146         static const std::string xml_state_node_name;
147
148         XMLNode& get_state() const;
149
150         void update_bar_offset_from_bbt (const Meter&);
151         void update_bbt_time_from_bar_offset (const Meter&);
152         double bar_offset() const { return _bar_offset; }
153
154   private:
155         /* this value provides a fractional offset into the bar in which
156            the tempo section is located in. A value of 0.0 indicates that
157            it occurs on the first beat of the bar, a value of 0.5 indicates
158            that it occurs halfway through the bar and so on.
159            
160            this enables us to keep the tempo change at the same relative
161            position within the bar if/when the meter changes.
162         */
163         double _bar_offset;
164 };
165
166 typedef std::list<MetricSection*> Metrics;
167
168 /** Helper class that we use to be able to keep track of which
169     meter *AND* tempo are in effect at a given point in time.
170 */
171 class TempoMetric {
172   public:
173         TempoMetric (const Meter& m, const Tempo& t) : _meter (&m), _tempo (&t), _frame (0) {}
174
175         void set_tempo (const Tempo& t)    { _tempo = &t; }
176         void set_meter (const Meter& m)    { _meter = &m; }
177         void set_frame (framepos_t f)      { _frame = f; }
178         void set_start (const Timecode::BBT_Time& t) { _start = t; }
179
180         const Meter&    meter() const { return *_meter; }
181         const Tempo&    tempo() const { return *_tempo; }
182         framepos_t      frame() const { return _frame; }
183         const Timecode::BBT_Time& start() const { return _start; }
184
185   private:
186         const Meter*       _meter;
187         const Tempo*       _tempo;
188         framepos_t         _frame;
189         Timecode::BBT_Time _start;
190 };
191
192 class TempoMap : public PBD::StatefulDestructible
193 {
194   public:
195         TempoMap (framecnt_t frame_rate);
196         ~TempoMap();
197
198         /* measure-based stuff */
199
200         enum BBTPointType {
201                 Bar,
202                 Beat,
203         };
204
205         struct BBTPoint {
206             framepos_t  frame;
207             const Meter* meter;
208             const Tempo* tempo;
209             uint32_t bar;
210             uint32_t beat;
211             
212             Timecode::BBT_Time bbt() const { return Timecode::BBT_Time (bar, beat, 0); }
213             operator Timecode::BBT_Time() const { return bbt(); }
214             operator framepos_t() const { return frame; }
215             
216             bool is_bar() const { return beat == 1; }
217
218             BBTPoint (const Meter& m, const Tempo& t, framepos_t f,
219                       uint32_t b, uint32_t e)
220                     : frame (f), meter (&m), tempo (&t), bar (b), beat (e) {}
221         };
222
223         typedef std::vector<BBTPoint> BBTPointList;
224
225         template<class T> void apply_with_metrics (T& obj, void (T::*method)(const Metrics&)) {
226                 Glib::RWLock::ReaderLock lm (lock);
227                 (obj.*method)(*metrics);
228         }
229
230         void map (BBTPointList::const_iterator&, BBTPointList::const_iterator&, 
231                   framepos_t start, framepos_t end);
232         
233         void       bbt_time (framepos_t when, Timecode::BBT_Time&);
234         framecnt_t frame_time (const Timecode::BBT_Time&);
235         framecnt_t bbt_duration_at (framepos_t, const Timecode::BBT_Time&, int dir);
236
237         static const Tempo& default_tempo() { return _default_tempo; }
238         static const Meter& default_meter() { return _default_meter; }
239
240         const Tempo& tempo_at (framepos_t) const;
241         const Meter& meter_at (framepos_t) const;
242
243         const TempoSection& tempo_section_at (framepos_t) const;
244
245         void add_tempo(const Tempo&, Timecode::BBT_Time where);
246         void add_meter(const Meter&, Timecode::BBT_Time where);
247
248         void remove_tempo(const TempoSection&, bool send_signal);
249         void remove_meter(const MeterSection&, bool send_signal);
250
251         void replace_tempo (const TempoSection&, const Tempo&, const Timecode::BBT_Time& where);
252         void replace_meter (const MeterSection&, const Meter&, const Timecode::BBT_Time& where);
253
254         framepos_t round_to_bar  (framepos_t frame, int dir);
255         framepos_t round_to_beat (framepos_t frame, int dir);
256         framepos_t round_to_beat_subdivision (framepos_t fr, int sub_num, int dir);
257         framepos_t round_to_tick (framepos_t frame, int dir);
258
259         void set_length (framepos_t frames);
260
261         XMLNode& get_state (void);
262         int set_state (const XMLNode&, int version);
263
264         void dump (std::ostream&) const;
265         void clear ();
266
267         TempoMetric metric_at (Timecode::BBT_Time bbt) const;
268         TempoMetric metric_at (framepos_t) const;
269
270         framepos_t framepos_plus_bbt (framepos_t pos, Timecode::BBT_Time b);
271         framepos_t framepos_plus_beats (framepos_t, Evoral::MusicalTime);
272         framepos_t framepos_minus_bbt (framepos_t pos, Timecode::BBT_Time b);
273         framepos_t framepos_minus_beats (framepos_t, Evoral::MusicalTime);
274         Evoral::MusicalTime framewalk_to_beats (framepos_t pos, framecnt_t distance);
275
276         void change_existing_tempo_at (framepos_t, double bpm, double note_type);
277         void change_initial_tempo (double bpm, double note_type);
278
279         void insert_time (framepos_t, framecnt_t);
280
281         int n_tempos () const;
282         int n_meters () const;
283
284         framecnt_t frame_rate () const { return _frame_rate; }
285
286   private:
287
288         friend class ::BBTTest;
289         friend class ::FrameposPlusBeatsTest;
290         friend class ::TempoTest;
291         
292         static Tempo    _default_tempo;
293         static Meter    _default_meter;
294
295         Metrics*             metrics;
296         framecnt_t          _frame_rate;
297         framepos_t           last_bbt_when;
298         bool                 last_bbt_valid;
299         Timecode::BBT_Time   last_bbt;
300         mutable Glib::RWLock lock;
301         BBTPointList*       _map;
302
303         void recompute_map (bool reassign_tempo_bbt, bool use_write_lock, framepos_t end = -1);
304         void require_map_to (framepos_t pos);
305         void require_map_to (const Timecode::BBT_Time&);
306
307         BBTPointList::const_iterator bbt_before_or_at (framepos_t);
308         BBTPointList::const_iterator bbt_after_or_at (framepos_t);
309         BBTPointList::const_iterator bbt_point_for (const Timecode::BBT_Time&);
310         
311         framepos_t round_to_type (framepos_t fr, int dir, BBTPointType);
312         
313         void bbt_time (framepos_t, Timecode::BBT_Time&, const BBTPointList::const_iterator&);
314         
315         framecnt_t bbt_duration_at_unlocked (const Timecode::BBT_Time& when, const Timecode::BBT_Time& bbt, int dir);
316         
317         const MeterSection& first_meter() const;
318         const TempoSection& first_tempo() const;
319         
320         int move_metric_section (MetricSection&, const Timecode::BBT_Time& to);
321         void do_insert (MetricSection* section);
322         
323         Timecode::BBT_Time bbt_add (const Timecode::BBT_Time&, const Timecode::BBT_Time&, const TempoMetric&) const;
324         Timecode::BBT_Time bbt_add (const Timecode::BBT_Time& a, const Timecode::BBT_Time& b) const;
325         Timecode::BBT_Time bbt_subtract (const Timecode::BBT_Time&, const Timecode::BBT_Time&) const;
326 };
327
328 }; /* namespace ARDOUR */
329
330 std::ostream& operator<< (std::ostream&, const ARDOUR::Meter&);
331 std::ostream& operator<< (std::ostream&, const ARDOUR::Tempo&);
332 std::ostream& operator<< (std::ostream&, const ARDOUR::MetricSection&);
333
334 #endif /* __ardour_tempo_h__ */