Tempo ramps - neaten code layout.
[ardour.git] / libs / ardour / tempo.cc
1 /*
2     Copyright (C) 2000-2002 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 #include <algorithm>
21 #include <stdexcept>
22 #include <cmath>
23
24 #include <unistd.h>
25
26 #include <glibmm/threads.h>
27
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
31
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
35
36 #include "i18n.h"
37 #include <locale.h>
38
39 using namespace std;
40 using namespace ARDOUR;
41 using namespace PBD;
42
43 using Timecode::BBT_Time;
44
45 /* _default tempo is 4/4 qtr=120 */
46
47 Meter    TempoMap::_default_meter (4.0, 4.0);
48 Tempo    TempoMap::_default_tempo (120.0);
49
50 /***********************************************************************/
51
52 double
53 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
54 {
55         /* This is tempo- and meter-sensitive. The number it returns
56            is based on the interval between any two lines in the
57            grid that is constructed from tempo and meter sections.
58
59            The return value IS NOT interpretable in terms of "beats".
60         */
61
62         return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
63 }
64
65 double
66 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
67 {
68         return frames_per_grid (tempo, sr) * _divisions_per_bar;
69 }
70
71 /***********************************************************************/
72
73 const string TempoSection::xml_state_node_name = "Tempo";
74
75 TempoSection::TempoSection (const XMLNode& node)
76         : MetricSection (0.0)
77         , Tempo (TempoMap::default_tempo())
78         , _c_func (0.0)
79         , _active (true)
80 {
81         const XMLProperty *prop;
82         LocaleGuard lg;
83         BBT_Time bbt;
84         double pulse;
85         uint32_t frame;
86
87         if ((prop = node.property ("start")) != 0) {
88                 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
89                             &bbt.bars,
90                             &bbt.beats,
91                             &bbt.ticks) == 3) {
92                         /* legacy session - start used to be in bbt*/
93                         _legacy_bbt = bbt;
94                         pulse = -1.0;
95                         set_pulse (pulse);
96                 }
97         } else {
98                 warning << _("TempoSection XML node has no \"start\" property") << endmsg;
99         }
100
101
102         if ((prop = node.property ("pulse")) != 0) {
103                 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
104                         error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
105                 } else {
106                         set_pulse (pulse);
107                 }
108         }
109         if ((prop = node.property ("frame")) != 0) {
110                 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
111                         error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
112                 } else {
113                         set_frame (frame);
114                 }
115         }
116
117         if ((prop = node.property ("beats-per-minute")) == 0) {
118                 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
119                 throw failed_constructor();
120         }
121
122         if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
123                 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
124                 throw failed_constructor();
125         }
126
127         if ((prop = node.property ("note-type")) == 0) {
128                 /* older session, make note type be quarter by default */
129                 _note_type = 4.0;
130         } else {
131                 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
132                         error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
133                         throw failed_constructor();
134                 }
135         }
136
137         if ((prop = node.property ("movable")) == 0) {
138                 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
139                 throw failed_constructor();
140         }
141
142         set_movable (string_is_affirmative (prop->value()));
143
144         if ((prop = node.property ("active")) == 0) {
145                 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
146                 set_active(true);
147         } else {
148                 set_active (string_is_affirmative (prop->value()));
149         }
150
151         if ((prop = node.property ("tempo-type")) == 0) {
152                 _type = Constant;
153         } else {
154                 _type = Type (string_2_enum (prop->value(), _type));
155         }
156
157         if ((prop = node.property ("lock-style")) == 0) {
158                 set_position_lock_style (MusicTime);
159         } else {
160                 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
161         }
162 }
163
164 XMLNode&
165 TempoSection::get_state() const
166 {
167         XMLNode *root = new XMLNode (xml_state_node_name);
168         char buf[256];
169         LocaleGuard lg;
170
171         snprintf (buf, sizeof (buf), "%f", pulse());
172         root->add_property ("pulse", buf);
173         snprintf (buf, sizeof (buf), "%li", frame());
174         root->add_property ("frame", buf);
175         snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
176         root->add_property ("beats-per-minute", buf);
177         snprintf (buf, sizeof (buf), "%f", _note_type);
178         root->add_property ("note-type", buf);
179         snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
180         root->add_property ("movable", buf);
181         snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
182         root->add_property ("active", buf);
183         root->add_property ("tempo-type", enum_2_string (_type));
184         root->add_property ("lock-style", enum_2_string (position_lock_style()));
185
186         return *root;
187 }
188
189 void
190 TempoSection::set_type (Type type)
191 {
192         _type = type;
193 }
194
195 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
196 */
197 double
198 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
199 {
200
201         if (_type == Constant) {
202                 return pulses_per_minute();
203         }
204
205         return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
206 }
207
208 /** returns the zero-based frame (relative to session)
209    where the tempo in whole pulses per minute occurs in this section.
210    beat b is only used for constant tempos.
211    note that the tempo map may have multiple such values.
212 */
213 framepos_t
214 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
215 {
216         if (_type == Constant) {
217                 return ((b - pulse())  * frames_per_pulse (frame_rate))  + frame();
218         }
219
220         return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
221 }
222 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
223 */
224 double
225 TempoSection::tempo_at_pulse (const double& p) const
226 {
227
228         if (_type == Constant) {
229                 return pulses_per_minute();
230         }
231         double const ppm = pulse_tempo_at_pulse (p - pulse());
232         return ppm;
233 }
234
235 /** returns the zero-based beat (relative to session)
236    where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
237    note that the session tempo map may have multiple beats at a given tempo.
238 */
239 double
240 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
241 {
242         if (_type == Constant) {
243                 double const beats = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
244                 return  beats;
245         }
246
247         return pulse_at_pulse_tempo (ppm) + pulse();
248 }
249
250 /** returns the zero-based pulse (relative to session origin)
251    where the zero-based frame (relative to session)
252    lies.
253 */
254 double
255 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
256 {
257         if (_type == Constant) {
258                 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
259         }
260
261         return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
262 }
263
264 /** returns the zero-based frame (relative to session start frame)
265    where the zero-based pulse (relative to session start)
266    falls.
267 */
268
269 framepos_t
270 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
271 {
272         if (_type == Constant) {
273                 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
274         }
275
276         return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
277 }
278
279 /*
280 Ramp Overview
281
282       |                     *
283 Tempo |                   *
284 Tt----|-----------------*|
285 Ta----|--------------|*  |
286       |            * |   |
287       |         *    |   |
288       |     *        |   |
289 T0----|*             |   |
290   *   |              |   |
291       _______________|___|____
292       time           a   t (next tempo)
293       [        c         ] defines c
294
295 Duration in beats at time a is the integral of some Tempo function.
296 In our case, the Tempo function (Tempo at time t) is
297 T(t) = T0(e^(ct))
298
299 with function constant
300 c = log(Ta/T0)/a
301 so
302 a = log(Ta/T0)/c
303
304 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
305 b(t) = T0(e^(ct) - 1) / c
306
307 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
308 t(b) = log((cb / T0) + 1) / c
309
310 The time t at which Tempo T occurs is a as above:
311 t(T) = log(T / T0) / c
312
313 The beat at which a Tempo T occurs is:
314 b(T) = (T - T0) / c
315
316 The Tempo at which beat b occurs is:
317 T(b) = b.c + T0
318
319 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
320 Our problem is that we usually don't know t.
321 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
322 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
323 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
324
325 By substituting our expanded t as a in the c function above, our problem is reduced to:
326 c = T0 (e^(log (Ta / T0)) - 1) / b
327
328 We can now store c for future time calculations.
329 If the following tempo section (the one that defines c in conjunction with this one)
330 is changed or moved, c is no longer valid.
331
332 The public methods are session-relative.
333
334 Most of this stuff is taken from this paper:
335
336 WHERE’S THE BEAT?
337 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
338 Jan C. Schacher
339 Martin Neukom
340 Zurich University of Arts
341 Institute for Computer Music and Sound Technology
342
343 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
344
345 */
346
347 /*
348   compute this ramp's function constant using the end tempo (in whole pulses per minute)
349   and duration (pulses into global start) of some later tempo section.
350 */
351 double
352 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
353 {
354         double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
355         return pulses_per_minute() *  (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
356 }
357
358 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
359 double
360 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
361 {
362         return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
363 }
364
365 framecnt_t
366 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
367 {
368         return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
369 }
370
371 double
372 TempoSection::frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const
373 {
374         return (frame / (double) frame_rate) / 60.0;
375 }
376
377 /* position function */
378 double
379 TempoSection::a_func (double end_bpm, double c_func) const
380 {
381         return log (end_bpm / pulses_per_minute()) /  c_func;
382 }
383
384 /*function constant*/
385 double
386 TempoSection::c_func (double end_bpm, double end_time) const
387 {
388         return log (end_bpm / pulses_per_minute()) /  end_time;
389 }
390
391 /* tempo in ppm at time in minutes */
392 double
393 TempoSection::pulse_tempo_at_time (const double& time) const
394 {
395         return exp (_c_func * time) * pulses_per_minute();
396 }
397
398 /* time in minutes at tempo in ppm */
399 double
400 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
401 {
402         return log (pulse_tempo / pulses_per_minute()) / _c_func;
403 }
404
405 /* tick at tempo in ppm */
406 double
407 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
408 {
409         return (pulse_tempo - pulses_per_minute()) / _c_func;
410 }
411
412 /* tempo in ppm at tick */
413 double
414 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
415 {
416         return (pulse * _c_func) + pulses_per_minute();
417 }
418
419 /* pulse at time in minutes */
420 double
421 TempoSection::pulse_at_time (const double& time) const
422 {
423         return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
424 }
425
426 /* time in minutes at pulse */
427 double
428 TempoSection::time_at_pulse (const double& pulse) const
429 {
430         return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
431 }
432
433 /***********************************************************************/
434
435 const string MeterSection::xml_state_node_name = "Meter";
436
437 MeterSection::MeterSection (const XMLNode& node)
438         : MetricSection (0.0), Meter (TempoMap::default_meter())
439 {
440         XMLProperty const * prop;
441         BBT_Time start;
442         LocaleGuard lg;
443         const XMLProperty *prop;
444         BBT_Time bbt;
445         double pulse = 0.0;
446         double beat = 0.0;
447         framepos_t frame = 0;
448         pair<double, BBT_Time> start;
449
450         if ((prop = node.property ("start")) != 0) {
451                 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
452                     &bbt.bars,
453                     &bbt.beats,
454                     &bbt.ticks) < 3) {
455                         error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
456                 } else {
457                         /* legacy session - start used to be in bbt*/
458                         pulse = -1.0;
459                 }
460         } else {
461                 error << _("MeterSection XML node has no \"start\" property") << endmsg;
462         }
463
464         if ((prop = node.property ("pulse")) != 0) {
465                 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
466                         error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
467                 }
468         }
469         set_pulse (pulse);
470
471         if ((prop = node.property ("beat")) != 0) {
472                 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
473                         error << _("MeterSection XML node has an illegal \"beat\" vlue") << endmsg;
474                 }
475         }
476
477         start.first = beat;
478
479         if ((prop = node.property ("bbt")) == 0) {
480                 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
481         } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
482                     &bbt.bars,
483                     &bbt.beats,
484                     &bbt.ticks) < 3) {
485                 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
486                 //throw failed_constructor();
487         }
488
489         start.second = bbt;
490         set_beat (start);
491
492         if ((prop = node.property ("frame")) != 0) {
493                 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
494                         error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
495                 } else {
496                         set_frame (frame);
497                 }
498         }
499
500         /* beats-per-bar is old; divisions-per-bar is new */
501
502         if ((prop = node.property ("divisions-per-bar")) == 0) {
503                 if ((prop = node.property ("beats-per-bar")) == 0) {
504                         error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
505                         throw failed_constructor();
506                 }
507         }
508         if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
509                 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
510                 throw failed_constructor();
511         }
512
513         if ((prop = node.property ("note-type")) == 0) {
514                 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
515                 throw failed_constructor();
516         }
517         if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
518                 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
519                 throw failed_constructor();
520         }
521
522         if ((prop = node.property ("lock-style")) == 0) {
523                 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
524                 set_position_lock_style (MusicTime);
525         } else {
526                 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
527         }
528
529         if ((prop = node.property ("movable")) == 0) {
530                 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
531                 throw failed_constructor();
532         }
533
534         set_movable (string_is_affirmative (prop->value()));
535 }
536
537 XMLNode&
538 MeterSection::get_state() const
539 {
540         XMLNode *root = new XMLNode (xml_state_node_name);
541         char buf[256];
542         LocaleGuard lg;
543
544         snprintf (buf, sizeof (buf), "%lf", pulse());
545         root->add_property ("pulse", buf);
546         snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
547                   bbt().bars,
548                   bbt().beats,
549                   bbt().ticks);
550         root->add_property ("bbt", buf);
551         snprintf (buf, sizeof (buf), "%lf", beat());
552         root->add_property ("beat", buf);
553         snprintf (buf, sizeof (buf), "%f", _note_type);
554         root->add_property ("note-type", buf);
555         snprintf (buf, sizeof (buf), "%li", frame());
556         root->add_property ("frame", buf);
557         root->add_property ("lock-style", enum_2_string (position_lock_style()));
558         snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
559         root->add_property ("divisions-per-bar", buf);
560         snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
561         root->add_property ("movable", buf);
562
563         return *root;
564 }
565
566 /***********************************************************************/
567 /*
568   Tempo Map Overview
569
570   Tempo can be thought of as a source of the musical pulse.
571   Meters divide that pulse into measures and beats.
572   Tempo pulses can be divided to be in sympathy with the meter, but this does not affect the beat
573   at any particular time.
574   Note that Tempo::beats_per_minute() has nothing to do with musical beats.
575   It should rather be thought of as tempo note divisions per minute.
576
577   TempoSections, which are nice to think of in whole pulses per minute,
578   and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
579   and beats (via note_divisor) are used to form a tempo map.
580   TempoSections and MeterSections may be locked to either audio or music (position lock style).
581   We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
582   We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
583
584   Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
585
586   The first tempo and first meter are special. they must move together, and must be locked to audio.
587   Audio locked tempos which lie before the first meter are made inactive.
588   They will be re-activated if the first meter is again placed before them.
589
590   Both tempos and meters have a pulse position and a frame position.
591   Meters also have a beat position, which is always 0.0 for the first meter.
592
593   A tempo locked to music is locked to musical pulses.
594   A meter locked to music is locked to beats.
595
596   Recomputing the tempo map is the process where the 'missing' position
597   (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
598
599   It is important to keep the _metrics in an order that makes sense.
600   Because ramped MusicTime and AudioTime tempos can interact with each other,
601   reordering is frequent. Care must be taken to keep _metrics in a solved state.
602   Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
603 */
604 struct MetricSectionSorter {
605     bool operator() (const MetricSection* a, const MetricSection* b) {
606             return a->pulse() < b->pulse();
607     }
608 };
609
610 struct MetricSectionFrameSorter {
611     bool operator() (const MetricSection* a, const MetricSection* b) {
612             return a->frame() < b->frame();
613     }
614 };
615
616 TempoMap::TempoMap (framecnt_t fr)
617 {
618         _frame_rate = fr;
619         BBT_Time start (1, 1, 0);
620
621         TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
622         MeterSection *m = new MeterSection (0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
623
624         t->set_movable (false);
625         m->set_movable (false);
626
627         /* note: frame time is correct (zero) for both of these */
628
629         _metrics.push_back (t);
630         _metrics.push_back (m);
631
632 }
633
634 TempoMap::~TempoMap ()
635 {
636 }
637
638 void
639 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
640 {
641         bool removed = false;
642
643         {
644                 Glib::Threads::RWLock::WriterLock lm (lock);
645                 if ((removed = remove_tempo_locked (tempo))) {
646                         if (complete_operation) {
647                                 recompute_map (_metrics);
648                         }
649                 }
650         }
651
652         if (removed && complete_operation) {
653                 PropertyChanged (PropertyChange ());
654         }
655 }
656
657 bool
658 TempoMap::remove_tempo_locked (const TempoSection& tempo)
659 {
660         Metrics::iterator i;
661
662         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
663                 if (dynamic_cast<TempoSection*> (*i) != 0) {
664                         if (tempo.frame() == (*i)->frame()) {
665                                 if ((*i)->movable()) {
666                                         _metrics.erase (i);
667                                         return true;
668                                 }
669                         }
670                 }
671         }
672
673         return false;
674 }
675
676 void
677 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
678 {
679         bool removed = false;
680
681         {
682                 Glib::Threads::RWLock::WriterLock lm (lock);
683                 if ((removed = remove_meter_locked (tempo))) {
684                         if (complete_operation) {
685                                 recompute_map (_metrics);
686                         }
687                 }
688         }
689
690         if (removed && complete_operation) {
691                 PropertyChanged (PropertyChange ());
692         }
693 }
694
695 bool
696 TempoMap::remove_meter_locked (const MeterSection& tempo)
697 {
698         Metrics::iterator i;
699
700         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
701                 if (dynamic_cast<MeterSection*> (*i) != 0) {
702                         if (tempo.frame() == (*i)->frame()) {
703                                 if ((*i)->movable()) {
704                                         _metrics.erase (i);
705                                         return true;
706                                 }
707                         }
708                 }
709         }
710
711         return false;
712 }
713
714 void
715 TempoMap::do_insert (MetricSection* section)
716 {
717         bool need_add = true;
718         /* we only allow new meters to be inserted on beat 1 of an existing
719          * measure.
720          */
721         MeterSection* m = 0;
722         if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
723                 //assert (m->bbt().ticks == 0);
724
725                 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
726
727                         pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
728                         corrected.second.beats = 1;
729                         corrected.second.ticks = 0;
730                         corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
731                         warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
732                                                    m->bbt(), corrected.second) << endmsg;
733                         //m->set_pulse (corrected);
734                 }
735         }
736
737         /* Look for any existing MetricSection that is of the same type and
738            in the same bar as the new one, and remove it before adding
739            the new one. Note that this means that if we find a matching,
740            existing section, we can break out of the loop since we're
741            guaranteed that there is only one such match.
742         */
743
744         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
745
746                 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
747                 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
748                 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
749                 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
750
751                 if (tempo && insert_tempo) {
752
753                         /* Tempo sections */
754                         bool const ipm = insert_tempo->position_lock_style() == MusicTime;
755                         if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
756
757                                 if (!tempo->movable()) {
758
759                                         /* can't (re)move this section, so overwrite
760                                          * its data content (but not its properties as
761                                          * a section).
762                                          */
763
764                                         *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
765                                         (*i)->set_position_lock_style (AudioTime);
766                                         need_add = false;
767                                 } else {
768                                         _metrics.erase (i);
769                                 }
770                                 break;
771                         }
772
773                 } else if (meter && insert_meter) {
774
775                         /* Meter Sections */
776
777                         bool const ipm = insert_meter->position_lock_style() == MusicTime;
778
779                         if ((ipm && meter->pulse() == insert_meter->pulse()) || (!ipm && meter->frame() == insert_meter->frame())) {
780
781                                 if (!meter->movable()) {
782
783                                         /* can't (re)move this section, so overwrite
784                                          * its data content (but not its properties as
785                                          * a section
786                                          */
787
788                                         *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
789                                         (*i)->set_position_lock_style (insert_meter->position_lock_style());
790                                         need_add = false;
791                                 } else {
792                                         _metrics.erase (i);
793
794                                 }
795
796                                 break;
797                         }
798                 } else {
799                         /* non-matching types, so we don't care */
800                 }
801         }
802
803         /* Add the given MetricSection, if we didn't just reset an existing
804          * one above
805          */
806
807         if (need_add) {
808                 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
809                 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
810                 Metrics::iterator i;
811                 if (insert_meter) {
812                         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
813                                 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
814
815                                 if (meter) {
816                                         bool const ipm = insert_meter->position_lock_style() == MusicTime;
817                                         if ((ipm && meter->pulse() > insert_meter->pulse()) || (!ipm && meter->frame() > insert_meter->frame())) {
818                                                 break;
819                                         }
820                                 }
821                         }
822                 } else if (insert_tempo) {
823                         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
824                                 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
825
826                                 if (tempo) {
827                                         bool const ipm = insert_tempo->position_lock_style() == MusicTime;
828                                         if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
829                                                 break;
830                                         }
831                                 }
832                         }
833                 }
834
835                 _metrics.insert (i, section);
836                 //dump (_metrics, std::cerr);
837         }
838 }
839
840 void
841 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
842 {
843         {
844                 Glib::Threads::RWLock::WriterLock lm (lock);
845                 TempoSection& first (first_tempo());
846                 if (ts.pulse() != first.pulse()) {
847                         remove_tempo_locked (ts);
848                         add_tempo_locked (tempo, pulse, true, type);
849                 } else {
850                         first.set_type (type);
851                         {
852                                 /* cannot move the first tempo section */
853                                 *static_cast<Tempo*>(&first) = tempo;
854                                 recompute_map (_metrics);
855                         }
856                 }
857         }
858
859         PropertyChanged (PropertyChange ());
860 }
861
862 void
863 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
864 {
865         {
866                 Glib::Threads::RWLock::WriterLock lm (lock);
867                 TempoSection& first (first_tempo());
868                 if (ts.frame() != first.frame()) {
869                         remove_tempo_locked (ts);
870                         add_tempo_locked (tempo, frame, true, type);
871                 } else {
872                         first.set_type (type);
873                         first.set_pulse (0.0);
874                         first.set_position_lock_style (AudioTime);
875                         {
876                                 /* cannot move the first tempo section */
877                                 *static_cast<Tempo*>(&first) = tempo;
878                                 recompute_map (_metrics);
879                         }
880                 }
881         }
882
883         PropertyChanged (PropertyChange ());
884 }
885
886 void
887 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
888 {
889         {
890                 Glib::Threads::RWLock::WriterLock lm (lock);
891                 add_tempo_locked (tempo, pulse, true, type);
892         }
893
894         PropertyChanged (PropertyChange ());
895 }
896
897 void
898 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
899 {
900         {
901                 Glib::Threads::RWLock::WriterLock lm (lock);
902                 add_tempo_locked (tempo, frame, true, type);
903         }
904
905
906         PropertyChanged (PropertyChange ());
907 }
908
909 void
910 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
911 {
912         TempoSection* ts = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
913
914         do_insert (ts);
915
916         if (recompute) {
917                 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->pulse());
918         }
919 }
920
921 void
922 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
923 {
924         TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
925
926         do_insert (ts);
927
928         if (recompute) {
929                 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->frame());
930         }
931 }
932
933 void
934 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
935 {
936         {
937                 Glib::Threads::RWLock::WriterLock lm (lock);
938
939                 if (ms.movable()) {
940                         remove_meter_locked (ms);
941                         add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
942                 } else {
943                         MeterSection& first (first_meter());
944                         const PositionLockStyle pl = ms.position_lock_style();
945                         /* cannot move the first meter section */
946                         *static_cast<Meter*>(&first) = meter;
947                         first.set_position_lock_style (pl);
948                         recompute_map (_metrics);
949                 }
950         }
951
952         PropertyChanged (PropertyChange ());
953 }
954
955 void
956 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
957 {
958         {
959                 Glib::Threads::RWLock::WriterLock lm (lock);
960
961                 const double beat = ms.beat();
962                 const BBT_Time bbt = ms.bbt();
963
964                 if (ms.movable()) {
965                         remove_meter_locked (ms);
966                         add_meter_locked (meter, frame, beat, bbt, true);
967                 } else {
968                         MeterSection& first (first_meter());
969                         TempoSection& first_t (first_tempo());
970                         /* cannot move the first meter section */
971                         *static_cast<Meter*>(&first) = meter;
972                         first.set_position_lock_style (AudioTime);
973                         first.set_pulse (0.0);
974                         first.set_frame (frame);
975                         pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
976                         first.set_beat (beat);
977                         first_t.set_frame (first.frame());
978                         first_t.set_pulse (0.0);
979                         first_t.set_position_lock_style (AudioTime);
980
981                         recompute_map (_metrics);
982                 }
983         }
984         PropertyChanged (PropertyChange ());
985 }
986
987
988 void
989 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
990 {
991         {
992                 Glib::Threads::RWLock::WriterLock lm (lock);
993                 add_meter_locked (meter, beat, where, true);
994         }
995
996
997 #ifndef NDEBUG
998         if (DEBUG_ENABLED(DEBUG::TempoMap)) {
999                 dump (_metrics, std::cerr);
1000         }
1001 #endif
1002
1003         PropertyChanged (PropertyChange ());
1004 }
1005
1006 void
1007 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1008 {
1009         {
1010                 Glib::Threads::RWLock::WriterLock lm (lock);
1011                 add_meter_locked (meter, frame, beat, where, true);
1012         }
1013
1014
1015 #ifndef NDEBUG
1016         if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1017                 dump (_metrics, std::cerr);
1018         }
1019 #endif
1020
1021         PropertyChanged (PropertyChange ());
1022 }
1023
1024 void
1025 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1026 {
1027         /* a new meter always starts a new bar on the first beat. so
1028            round the start time appropriately. remember that
1029            `where' is based on the existing tempo map, not
1030            the result after we insert the new meter.
1031
1032         */
1033
1034         if (where.beats != 1) {
1035                 where.beats = 1;
1036                 where.bars++;
1037         }
1038         /* new meters *always* start on a beat. */
1039         where.ticks = 0;
1040         double pulse = pulse_at_beat_locked (_metrics, beat);
1041
1042         MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1043         do_insert (new_meter);
1044
1045         if (recompute) {
1046                 solve_map (_metrics, new_meter, Meter (meter.divisions_per_bar(), meter.note_divisor()), pulse);
1047         }
1048
1049 }
1050
1051 void
1052 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1053 {
1054
1055         MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1056
1057         double pulse = pulse_at_frame_locked (_metrics, frame);
1058         new_meter->set_pulse (pulse);
1059
1060         do_insert (new_meter);
1061
1062         if (recompute) {
1063                 solve_map (_metrics, new_meter, Meter (new_meter->divisions_per_bar(), new_meter->note_divisor()), frame);
1064         }
1065
1066 }
1067
1068 void
1069 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1070 {
1071         Tempo newtempo (beats_per_minute, note_type);
1072         TempoSection* t;
1073
1074         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1075                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1076                         if (!t->active()) {
1077                                 continue;
1078                         }
1079                         {
1080                                 Glib::Threads::RWLock::WriterLock lm (lock);
1081                                 *((Tempo*) t) = newtempo;
1082                                 recompute_map (_metrics);
1083                         }
1084                         PropertyChanged (PropertyChange ());
1085                         break;
1086                 }
1087         }
1088 }
1089
1090 void
1091 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1092 {
1093         Tempo newtempo (beats_per_minute, note_type);
1094
1095         TempoSection* prev;
1096         TempoSection* first;
1097         Metrics::iterator i;
1098
1099         /* find the TempoSection immediately preceding "where"
1100          */
1101
1102         for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1103
1104                 if ((*i)->frame() > where) {
1105                         break;
1106                 }
1107
1108                 TempoSection* t;
1109
1110                 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1111                         if (!t->active()) {
1112                                 continue;
1113                         }
1114                         if (!first) {
1115                                 first = t;
1116                         }
1117                         prev = t;
1118                 }
1119         }
1120
1121         if (!prev) {
1122                 if (!first) {
1123                         error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1124                         return;
1125                 }
1126
1127                 prev = first;
1128         }
1129
1130         /* reset */
1131
1132         {
1133                 Glib::Threads::RWLock::WriterLock lm (lock);
1134                 /* cannot move the first tempo section */
1135                 *((Tempo*)prev) = newtempo;
1136                 recompute_map (_metrics);
1137         }
1138
1139         PropertyChanged (PropertyChange ());
1140 }
1141
1142 const MeterSection&
1143 TempoMap::first_meter () const
1144 {
1145         const MeterSection *m = 0;
1146
1147         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1148                 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1149                         return *m;
1150                 }
1151         }
1152
1153         fatal << _("programming error: no meter section in tempo map!") << endmsg;
1154         abort(); /*NOTREACHED*/
1155         return *m;
1156 }
1157
1158 MeterSection&
1159 TempoMap::first_meter ()
1160 {
1161         MeterSection *m = 0;
1162
1163         /* CALLER MUST HOLD LOCK */
1164
1165         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1166                 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1167                         return *m;
1168                 }
1169         }
1170
1171         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1172         abort(); /*NOTREACHED*/
1173         return *m;
1174 }
1175
1176 const TempoSection&
1177 TempoMap::first_tempo () const
1178 {
1179         const TempoSection *t = 0;
1180
1181         /* CALLER MUST HOLD LOCK */
1182
1183         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1184                 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1185                         if (!t->active()) {
1186                                 continue;
1187                         }
1188                         if (!t->movable()) {
1189                                 return *t;
1190                         }
1191                 }
1192         }
1193
1194         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1195         abort(); /*NOTREACHED*/
1196         return *t;
1197 }
1198
1199 TempoSection&
1200 TempoMap::first_tempo ()
1201 {
1202         TempoSection *t = 0;
1203
1204         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1205                 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1206                         if (!t->active()) {
1207                                 continue;
1208                         }
1209                         if (!t->movable()) {
1210                                 return *t;
1211                         }
1212                 }
1213         }
1214
1215         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1216         abort(); /*NOTREACHED*/
1217         return *t;
1218 }
1219 void
1220 TempoMap::recompute_tempos (Metrics& metrics)
1221 {
1222         TempoSection* prev_ts = 0;
1223
1224         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1225                 TempoSection* t;
1226
1227                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1228                         if (!t->active()) {
1229                                 continue;
1230                         }
1231                         if (prev_ts) {
1232                                 if (t->position_lock_style() == AudioTime) {
1233                                         prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1234                                         t->set_pulse (prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1235
1236                                 } else {
1237                                         prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1238                                         t->set_frame (prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1239
1240                                 }
1241                         }
1242                         prev_ts = t;
1243                 }
1244         }
1245         prev_ts->set_c_func (0.0);
1246 }
1247
1248 /* tempos must be positioned correctly */
1249 void
1250 TempoMap::recompute_meters (Metrics& metrics)
1251 {
1252         MeterSection* meter = 0;
1253         MeterSection* prev_m = 0;
1254         uint32_t accumulated_bars = 0;
1255
1256         for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1257                 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1258                         if (prev_m) {
1259                                 double beats_in_m;
1260                                 if (meter->position_lock_style() == MusicTime) {
1261                                         beats_in_m = meter->beat() - prev_m->beat();
1262                                 } else {
1263                                         beats_in_m = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor()) - prev_m->beat();
1264                                 }
1265                                 accumulated_bars += (beats_in_m + 1) / prev_m->divisions_per_bar();
1266                         }
1267                         if (meter->position_lock_style() == AudioTime) {
1268                                 double pulse = 0.0;
1269                                 pair<double, BBT_Time> b_bbt;
1270                                 if (meter->movable()) {
1271                                         const double beats = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor()) - prev_m->beat();
1272                                         const double ceil_beats = beats - fmod (beats, prev_m->divisions_per_bar());
1273                                         const double true_pulse = prev_m->pulse() + (ceil_beats - prev_m->beat()) / prev_m->note_divisor();
1274                                         const double pulse_off = true_pulse - ((beats - prev_m->beat()) / prev_m->note_divisor());
1275                                         b_bbt = make_pair (ceil_beats, BBT_Time (accumulated_bars + 1, 1, 0));
1276                                         pulse = true_pulse - pulse_off;
1277                                 } else {
1278                                         b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1279                                 }
1280                                 meter->set_beat (b_bbt);
1281                                 meter->set_pulse (pulse);
1282                         } else {
1283                                 double pulse = 0.0;
1284                                 if (prev_m) {
1285                                         pulse = prev_m->pulse() + (meter->beat() - prev_m->beat()) / prev_m->note_divisor();
1286                                 } else {
1287                                         pulse = pulse_at_beat_locked (metrics, meter->beat());
1288                                 }
1289                                 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1290                                 meter->set_pulse (pulse);
1291                         }
1292
1293                         prev_m = meter;
1294                 }
1295         }
1296 }
1297
1298 void
1299 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1300 {
1301         /* CALLER MUST HOLD WRITE LOCK */
1302
1303         if (end < 0) {
1304
1305                 /* we will actually stop once we hit
1306                    the last metric.
1307                 */
1308                 end = max_framepos;
1309
1310         }
1311
1312         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1313
1314         if (end == 0) {
1315                 /* silly call from Session::process() during startup
1316                  */
1317                 return;
1318         }
1319
1320         recompute_tempos (metrics);
1321         recompute_meters (metrics);
1322 }
1323
1324 TempoMetric
1325 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1326 {
1327         Glib::Threads::RWLock::ReaderLock lm (lock);
1328         TempoMetric m (first_meter(), first_tempo());
1329
1330         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1331            at something, because we insert the default tempo and meter during
1332            TempoMap construction.
1333
1334            now see if we can find better candidates.
1335         */
1336
1337         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1338
1339                 if ((*i)->frame() > frame) {
1340                         break;
1341                 }
1342
1343                 m.set_metric(*i);
1344
1345                 if (last) {
1346                         *last = i;
1347                 }
1348         }
1349
1350         return m;
1351 }
1352
1353 /* XX meters only */
1354 TempoMetric
1355 TempoMap::metric_at (BBT_Time bbt) const
1356 {
1357         Glib::Threads::RWLock::ReaderLock lm (lock);
1358         TempoMetric m (first_meter(), first_tempo());
1359
1360         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1361            at something, because we insert the default tempo and meter during
1362            TempoMap construction.
1363
1364            now see if we can find better candidates.
1365         */
1366
1367         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1368                 MeterSection* mw;
1369                 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1370                         BBT_Time section_start (mw->bbt());
1371
1372                         if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1373                                 break;
1374                         }
1375
1376                         m.set_metric (*i);
1377                 }
1378         }
1379
1380         return m;
1381 }
1382
1383 double
1384 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1385 {
1386         /* HOLD (at least) THE READER LOCK */
1387         TempoSection* prev_ts = 0;
1388         double accumulated_pulses = 0.0;
1389
1390         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1391                 TempoSection* t;
1392                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1393                         if (!t->active()) {
1394                                 continue;
1395                         }
1396                         if (prev_ts && t->frame() > frame) {
1397                                 /*the previous ts is the one containing the frame */
1398                                 const double ret = prev_ts->pulse_at_frame (frame, _frame_rate);
1399                                 return ret;
1400                         }
1401                         accumulated_pulses = t->pulse();
1402                         prev_ts = t;
1403                 }
1404         }
1405
1406         /* treated as constant for this ts */
1407         const double pulses_in_section = (frame - prev_ts->frame()) / prev_ts->frames_per_pulse (_frame_rate);
1408
1409         return pulses_in_section + accumulated_pulses;
1410 }
1411
1412 double
1413 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1414 {
1415         MeterSection* prev_ms = 0;
1416         double accumulated_beats = 0.0;
1417
1418         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1419                 MeterSection* m;
1420                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1421                         if (prev_ms && m->beat() > beat) {
1422                                 break;
1423                         }
1424                         accumulated_beats = m->beat();
1425                         prev_ms = m;
1426                 }
1427
1428         }
1429         double const ret = prev_ms->pulse() + ((beat - accumulated_beats) / prev_ms->note_divisor());
1430         return ret;
1431 }
1432
1433 double
1434 TempoMap::pulse_at_beat (const double& beat) const
1435 {
1436         Glib::Threads::RWLock::ReaderLock lm (lock);
1437         return pulse_at_beat_locked (_metrics, beat);
1438 }
1439
1440 double
1441 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1442 {
1443         MeterSection* prev_ms = 0;
1444         double accumulated_beats = 0.0;
1445
1446         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1447                 MeterSection* m;
1448                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1449                         if (prev_ms && m->pulse() > pulse) {
1450                                 break;
1451                         }
1452                         accumulated_beats = m->beat();
1453                         prev_ms = m;
1454                 }
1455         }
1456
1457         double const beats_in_section = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1458
1459         return beats_in_section + accumulated_beats;
1460 }
1461
1462 double
1463 TempoMap::beat_at_pulse (const double& pulse) const
1464 {
1465         Glib::Threads::RWLock::ReaderLock lm (lock);
1466         return beat_at_pulse_locked (_metrics, pulse);
1467 }
1468
1469 framecnt_t
1470 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1471 {
1472         /* HOLD THE READER LOCK */
1473
1474         const TempoSection* prev_ts = 0;
1475         double accumulated_pulses = 0.0;
1476
1477         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1478                 TempoSection* t;
1479
1480                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1481                         if (!t->active()) {
1482                                 continue;
1483                         }
1484                         if (prev_ts && t->pulse() > pulse) {
1485                                 return prev_ts->frame_at_pulse (pulse, _frame_rate);
1486                         }
1487
1488                         accumulated_pulses = t->pulse();
1489                         prev_ts = t;
1490                 }
1491         }
1492         /* must be treated as constant, irrespective of _type */
1493         double const pulses_in_section = pulse - accumulated_pulses;
1494         double const dtime = pulses_in_section * prev_ts->frames_per_pulse (_frame_rate);
1495
1496         framecnt_t const ret = (framecnt_t) floor (dtime) + prev_ts->frame();
1497
1498         return ret;
1499 }
1500
1501 double
1502 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1503 {
1504         const double pulse = pulse_at_frame_locked (metrics, frame);
1505         return beat_at_pulse_locked (metrics, pulse);
1506 }
1507
1508 double
1509 TempoMap::beat_at_frame (const framecnt_t& frame) const
1510 {
1511         Glib::Threads::RWLock::ReaderLock lm (lock);
1512         return beat_at_frame_locked (_metrics, frame);
1513 }
1514
1515 framecnt_t
1516 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1517 {
1518         const framecnt_t frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1519         return frame;
1520 }
1521
1522 framecnt_t
1523 TempoMap::frame_at_beat (const double& beat) const
1524 {
1525         Glib::Threads::RWLock::ReaderLock lm (lock);
1526         return frame_at_beat_locked (_metrics, beat);
1527 }
1528
1529 double
1530 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1531 {
1532         /* CALLER HOLDS READ LOCK */
1533
1534         double accumulated_beats = 0.0;
1535         double accumulated_bars = 0.0;
1536         MeterSection* prev_ms = 0;
1537
1538         /* because audio-locked meters have 'fake' integral beats,
1539            there is no pulse offset here.
1540         */
1541         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1542                 MeterSection* m;
1543                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1544                         if (prev_ms) {
1545                                 const double bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1546                                 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1547                                         break;
1548                                 }
1549                                 accumulated_beats = m->beat();
1550                                 accumulated_bars += bars_to_m;
1551                         }
1552                         prev_ms = m;
1553                 }
1554         }
1555
1556         const double remaining_bars = (bbt.bars - 1) - accumulated_bars;
1557         const double remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1558         const double ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1559
1560         return ret;
1561 }
1562
1563 double
1564 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1565 {
1566         Glib::Threads::RWLock::ReaderLock lm (lock);
1567         return bbt_to_beats_locked (_metrics, bbt);
1568 }
1569
1570 Timecode::BBT_Time
1571 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1572 {
1573         /* CALLER HOLDS READ LOCK */
1574         MeterSection* prev_ms = 0;
1575         const double beats = (b < 0.0) ? 0.0 : b;
1576         uint32_t accumulated_bars = 0;
1577         double accumulated_beats = 0.0;
1578
1579         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1580                 MeterSection* m = 0;
1581
1582                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1583                         if (prev_ms) {
1584                                 if (m->beat() > beats) {
1585                                         /* this is the meter after the one our beat is on*/
1586                                         break;
1587                                 }
1588                                 const double beats_to_m = m->beat() - prev_ms->beat();
1589                                 /* we need a whole number of bars. */
1590                                 accumulated_bars += (beats_to_m + 1) / prev_ms->divisions_per_bar();
1591                                 accumulated_beats += beats_to_m;
1592                         }
1593
1594                         prev_ms = m;
1595                 }
1596         }
1597
1598         const double beats_in_ms = beats - accumulated_beats;
1599         const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1600         const uint32_t total_bars = bars_in_ms + accumulated_bars;
1601         const double remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1602         const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1603
1604         BBT_Time ret;
1605
1606         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1607         ret.beats = (uint32_t) floor (remaining_beats);
1608         ret.bars = total_bars;
1609
1610         /* 0 0 0 to 1 1 0 - based mapping*/
1611         ++ret.bars;
1612         ++ret.beats;
1613
1614         if (ret.ticks >= BBT_Time::ticks_per_beat) {
1615                 ++ret.beats;
1616                 ret.ticks -= BBT_Time::ticks_per_beat;
1617         }
1618
1619         if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1620                 ++ret.bars;
1621                 ret.beats = 1;
1622         }
1623
1624         return ret;
1625 }
1626
1627 Timecode::BBT_Time
1628 TempoMap::beats_to_bbt (const double& beats)
1629 {
1630         Glib::Threads::RWLock::ReaderLock lm (lock);
1631         return beats_to_bbt_locked (_metrics, beats);
1632 }
1633
1634 Timecode::BBT_Time
1635 TempoMap::pulse_to_bbt (const double& pulse)
1636 {
1637         Glib::Threads::RWLock::ReaderLock lm (lock);
1638         MeterSection* prev_ms = 0;
1639         uint32_t accumulated_bars = 0;
1640         double accumulated_pulses = 0.0;
1641
1642         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1643                 MeterSection* m = 0;
1644
1645                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1646
1647                         if (prev_ms) {
1648                                 double const pulses_to_m = m->pulse() - prev_ms->pulse();
1649                                 if (accumulated_pulses + pulses_to_m > pulse) {
1650                                         /* this is the meter after the one our beat is on*/
1651                                         break;
1652                                 }
1653
1654                                 /* we need a whole number of bars. */
1655                                 accumulated_pulses += pulses_to_m;
1656                                 accumulated_bars += ((pulses_to_m * prev_ms->note_divisor()) + 1) / prev_ms->divisions_per_bar();
1657                         }
1658
1659                         prev_ms = m;
1660                 }
1661         }
1662         const double beats_in_ms = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1663         const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1664         const uint32_t total_bars = bars_in_ms + accumulated_bars;
1665         const double remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1666         const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1667
1668         BBT_Time ret;
1669
1670         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1671         ret.beats = (uint32_t) floor (remaining_beats);
1672         ret.bars = total_bars;
1673
1674         /* 0 0 0 to 1 1 0 mapping*/
1675         ++ret.bars;
1676         ++ret.beats;
1677
1678         if (ret.ticks >= BBT_Time::ticks_per_beat) {
1679                 ++ret.beats;
1680                 ret.ticks -= BBT_Time::ticks_per_beat;
1681         }
1682
1683         if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1684                 ++ret.bars;
1685                 ret.beats = 1;
1686         }
1687
1688         return ret;
1689 }
1690
1691 void
1692 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1693 {
1694
1695         if (frame < 0) {
1696                 bbt.bars = 1;
1697                 bbt.beats = 1;
1698                 bbt.ticks = 0;
1699                 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1700                 return;
1701         }
1702         Glib::Threads::RWLock::ReaderLock lm (lock);
1703         const double beat = beat_at_frame_locked (_metrics, frame);
1704
1705         bbt = beats_to_bbt_locked (_metrics, beat);
1706 }
1707
1708 framepos_t
1709 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1710 {
1711         /* HOLD THE READER LOCK */
1712
1713         const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1714         return ret;
1715 }
1716
1717 framepos_t
1718 TempoMap::frame_time (const BBT_Time& bbt)
1719 {
1720         if (bbt.bars < 1) {
1721                 warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
1722                 return 0;
1723         }
1724
1725         if (bbt.beats < 1) {
1726                 throw std::logic_error ("beats are counted from one");
1727         }
1728         Glib::Threads::RWLock::ReaderLock lm (lock);
1729         const double beat = bbt_to_beats_locked (_metrics, bbt);
1730         const framecnt_t frame = frame_at_beat_locked (_metrics, beat);
1731
1732         return frame;
1733 }
1734
1735 bool
1736 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1737 {
1738         TempoSection* prev_ts = 0;
1739
1740         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1741                 TempoSection* t;
1742                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1743                         if (!t->active()) {
1744                                 continue;
1745                         }
1746                         if (prev_ts) {
1747                                 if ((by_frame && t->frame() < prev_ts->frame()) || (!by_frame && t->pulse() < prev_ts->pulse())) {
1748                                         return false;
1749                                 }
1750
1751                                 if (t->frame() == prev_ts->frame()) {
1752                                         return false;
1753                                 }
1754
1755                                 /* precision check ensures pulses and frames align independent of lock style.*/
1756                                 if (by_frame && t->frame() != prev_ts->frame_at_pulse (t->pulse(), _frame_rate)) {
1757                                         return false;
1758                                 }
1759                         }
1760                         prev_ts = t;
1761                 }
1762         }
1763
1764         return true;
1765 }
1766
1767 bool
1768 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1769 {
1770         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1771                 TempoSection* t;
1772                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1773                         if (!t->movable()) {
1774                                 t->set_active (true);
1775                                 continue;
1776                         }
1777                         if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1778                                 t->set_active (false);
1779                                 t->set_pulse (0.0);
1780                         } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1781                                 t->set_active (true);
1782                         } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1783                                 return false;
1784                         }
1785                 }
1786         }
1787         return true;
1788 }
1789
1790 bool
1791 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const framepos_t& frame)
1792 {
1793         TempoSection* prev_ts = 0;
1794         TempoSection* section_prev = 0;
1795         framepos_t first_m_frame = 0;
1796
1797         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1798                 MeterSection* m;
1799                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1800                         if (!m->movable()) {
1801                                 first_m_frame = m->frame();
1802                                 break;
1803                         }
1804                 }
1805         }
1806         if (section->movable() && frame <= first_m_frame) {
1807                 return false;
1808         } else {
1809                 section->set_active (true);
1810         }
1811         section->set_frame (frame);
1812
1813         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1814                 TempoSection* t;
1815                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1816
1817                         if (!t->active()) {
1818                                 continue;
1819                         }
1820                         if (prev_ts) {
1821                                 if (t == section) {
1822                                         section_prev = prev_ts;
1823                                         continue;
1824                                 }
1825                                 if (t->position_lock_style() == MusicTime) {
1826                                         prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1827                                         t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
1828                                 } else {
1829                                         prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1830                                         t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
1831                                 }
1832                         }
1833                         prev_ts = t;
1834                 }
1835         }
1836
1837         if (section_prev) {
1838                 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1839                 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1840         }
1841
1842         if (section->position_lock_style() == MusicTime) {
1843                 /* we're setting the frame */
1844                 section->set_position_lock_style (AudioTime);
1845                 recompute_tempos (imaginary);
1846                 section->set_position_lock_style (MusicTime);
1847         } else {
1848                 recompute_tempos (imaginary);
1849         }
1850
1851         if (check_solved (imaginary, true)) {
1852                 recompute_meters (imaginary);
1853                 return true;
1854         }
1855
1856         MetricSectionFrameSorter fcmp;
1857         imaginary.sort (fcmp);
1858         if (section->position_lock_style() == MusicTime) {
1859                 /* we're setting the frame */
1860                 section->set_position_lock_style (AudioTime);
1861                 recompute_tempos (imaginary);
1862                 section->set_position_lock_style (MusicTime);
1863         } else {
1864                 recompute_tempos (imaginary);
1865         }
1866         if (check_solved (imaginary, true)) {
1867                 recompute_meters (imaginary);
1868                 return true;
1869         }
1870
1871         MetricSectionSorter cmp;
1872         imaginary.sort (cmp);
1873         if (section->position_lock_style() == MusicTime) {
1874                 /* we're setting the frame */
1875                 section->set_position_lock_style (AudioTime);
1876                 recompute_tempos (imaginary);
1877                 section->set_position_lock_style (MusicTime);
1878         } else {
1879                 recompute_tempos (imaginary);
1880         }
1881         if (check_solved (imaginary, true)) {
1882                 recompute_meters (imaginary);
1883                 return true;
1884         }
1885         //dump (imaginary, std::cerr);
1886
1887         return false;
1888 }
1889
1890 bool
1891 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& pulse)
1892 {
1893         TempoSection* prev_ts = 0;
1894         TempoSection* section_prev = 0;
1895
1896         section->set_pulse (pulse);
1897
1898         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1899                 TempoSection* t;
1900                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1901                         if (!t->active()) {
1902                                 continue;
1903                         }
1904                         if (prev_ts) {
1905                                 if (t == section) {
1906                                         section_prev = prev_ts;
1907                                         continue;
1908                                 }
1909                                 if (t->position_lock_style() == MusicTime) {
1910                                         prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1911                                         t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
1912                                 } else {
1913                                         prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1914                                         t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
1915                                 }
1916                         }
1917                         prev_ts = t;
1918                 }
1919         }
1920         if (section_prev) {
1921                 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1922                 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1923         }
1924
1925         if (section->position_lock_style() == AudioTime) {
1926                 /* we're setting the pulse */
1927                 section->set_position_lock_style (MusicTime);
1928                 recompute_tempos (imaginary);
1929                 section->set_position_lock_style (AudioTime);
1930         } else {
1931                 recompute_tempos (imaginary);
1932         }
1933         if (check_solved (imaginary, false)) {
1934                 recompute_meters (imaginary);
1935                 return true;
1936         }
1937
1938         MetricSectionSorter cmp;
1939         imaginary.sort (cmp);
1940         if (section->position_lock_style() == AudioTime) {
1941                 /* we're setting the pulse */
1942                 section->set_position_lock_style (MusicTime);
1943                 recompute_tempos (imaginary);
1944                 section->set_position_lock_style (AudioTime);
1945         } else {
1946                 recompute_tempos (imaginary);
1947         }
1948
1949         if (check_solved (imaginary, false)) {
1950                 recompute_meters (imaginary);
1951                 return true;
1952         }
1953
1954         MetricSectionFrameSorter fcmp;
1955         imaginary.sort (fcmp);
1956         if (section->position_lock_style() == AudioTime) {
1957                 /* we're setting the pulse */
1958                 section->set_position_lock_style (MusicTime);
1959                 recompute_tempos (imaginary);
1960                 section->set_position_lock_style (AudioTime);
1961         } else {
1962                 recompute_tempos (imaginary);
1963         }
1964
1965         if (check_solved (imaginary, false)) {
1966                 recompute_meters (imaginary);
1967                 return true;
1968         }
1969
1970         //dump (imaginary, std::cerr);
1971
1972         return false;
1973 }
1974
1975 void
1976 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
1977 {
1978         MeterSection* prev_ms = 0;
1979
1980         if (!section->movable()) {
1981                 /* lock the first tempo to our first meter */
1982                 if (!set_active_tempos (imaginary, frame)) {
1983                         return;
1984                 }
1985                 TempoSection* first_t = &first_tempo();
1986                 Metrics future_map;
1987                 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
1988
1989                 new_section->set_frame (frame);
1990                 new_section->set_pulse (0.0);
1991                 new_section->set_active (true);
1992
1993                 if (solve_map (future_map, new_section, Tempo (new_section->beats_per_minute(), new_section->note_type()), frame)) {
1994                         first_t->set_frame (frame);
1995                         first_t->set_pulse (0.0);
1996                         first_t->set_active (true);
1997                         solve_map (imaginary, first_t, Tempo (first_t->beats_per_minute(), first_t->note_type()), frame);
1998                 } else {
1999                         return;
2000                 }
2001         }
2002
2003         uint32_t accumulated_bars = 0;
2004
2005         section->set_frame (frame);
2006
2007         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2008                 MeterSection* m;
2009                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2010                         if (prev_ms) {
2011                                 double beats_in_m;
2012                                 if (m->position_lock_style() == MusicTime) {
2013                                         beats_in_m = m->beat() - prev_ms->beat();
2014                                 } else {
2015                                         beats_in_m = ((pulse_at_frame_locked (imaginary, frame) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2016                                 }
2017                                 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2018                         }
2019                         if (m == section){
2020                                 /*
2021                                   here we set the beat for this frame.
2022                                   we set it 'incorrectly' to the next bar's beat
2023                                   and use this difference to find the meter's pulse.
2024                                 */
2025                                 double pulse = 0.0;
2026                                 pair<double, BBT_Time> b_bbt;
2027                                 if (m->movable()) {
2028                                         const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2029                                         const double ceil_beats = beats - fmod (beats,  prev_ms->divisions_per_bar());
2030                                         b_bbt = make_pair (ceil_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2031                                         const double true_pulse = prev_ms->pulse() + ((ceil_beats - prev_ms->beat()) / prev_ms->note_divisor());
2032                                         const double pulse_off = true_pulse - ((beats - prev_ms->beat()) / prev_ms->note_divisor());
2033                                         pulse = true_pulse - pulse_off;
2034                                 } else {
2035                                         b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2036                                 }
2037                                 m->set_beat (b_bbt);
2038                                 m->set_pulse (pulse);
2039                                 prev_ms = m;
2040                                 continue;
2041                         }
2042                         if (prev_ms) {
2043                                 if (m->position_lock_style() == MusicTime) {
2044                                         const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2045                                         m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2046                                         m->set_pulse (pulse);
2047                                 } else {
2048                                         double pulse = 0.0;
2049                                         pair<double, BBT_Time> b_bbt;
2050                                         if (m->movable()) {
2051                                                 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2052                                                 const double ceil_beats = beats - fmod (beats , prev_ms->divisions_per_bar());
2053                                                 const double true_pulse = prev_ms->pulse() + (ceil_beats - prev_ms->beat()) / prev_ms->note_divisor();
2054                                                 const double pulse_off = true_pulse - ((beats - prev_ms->beat()) / prev_ms->note_divisor());
2055                                                 b_bbt = make_pair (ceil_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2056                                                 pulse = true_pulse - pulse_off;
2057                                         } else {
2058                                                 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2059                                                 pulse = 0.0;
2060                                         }
2061                                         m->set_beat (b_bbt);
2062                                         m->set_pulse (pulse);
2063                                 }
2064                         }
2065                         prev_ms = m;
2066                 }
2067         }
2068
2069         if (section->position_lock_style() == MusicTime) {
2070                 /* we're setting the frame */
2071                 section->set_position_lock_style (AudioTime);
2072                 recompute_meters (imaginary);
2073                 section->set_position_lock_style (MusicTime);
2074         } else {
2075                 recompute_meters (imaginary);
2076         }
2077         //dump (imaginary, std::cerr);
2078 }
2079
2080 void
2081 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& pulse)
2082 {
2083         MeterSection* prev_ms = 0;
2084         double accumulated_beats = 0.0;
2085         uint32_t accumulated_bars = 0;
2086
2087         section->set_pulse (pulse);
2088
2089         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2090                 MeterSection* m;
2091                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2092                         if (prev_ms) {
2093                                 double beats_in_m;
2094                                 if (m->position_lock_style() == MusicTime) {
2095                                         beats_in_m = m->beat() - prev_ms->beat();
2096                                 } else {
2097                                         beats_in_m = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2098                                 }
2099                                 accumulated_beats += beats_in_m;
2100                                 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2101                         }
2102                         if (m == section){
2103                                 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2104                                 const double beats = ((pulse - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2105                                 const int32_t bars = (beats + 1) / prev_ms->divisions_per_bar();
2106                                 pair<double, BBT_Time> b_bbt = make_pair (beats, BBT_Time (bars + 1, 1, 0));
2107                                 section->set_beat (b_bbt);
2108                                 prev_ms = section;
2109                                 continue;
2110                         }
2111                         if (prev_ms) {
2112                                 if (m->position_lock_style() == MusicTime) {
2113                                         const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2114                                         m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2115                                         m->set_pulse (pulse);
2116                                 } else {
2117                                         double pulse = 0.0;
2118                                         pair<double, BBT_Time> b_bbt;
2119                                         if (m->movable()) {
2120                                                 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_ms->pulse()) * prev_ms->note_divisor()) - prev_ms->beat();
2121                                                 const double ceil_beats = beats - fmod (beats, prev_ms->divisions_per_bar());
2122                                                 const double true_pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2123                                                 const double pulse_off = true_pulse - ((ceil_beats - prev_ms->beat()) / prev_ms->note_divisor());
2124                                                 b_bbt = make_pair (ceil_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2125                                                 pulse = true_pulse - pulse_off;
2126                                         } else {
2127                                                 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2128                                         }
2129                                         m->set_beat (b_bbt);
2130                                         m->set_pulse (pulse);
2131                                 }
2132                         }
2133                         prev_ms = m;
2134                 }
2135         }
2136
2137         if (section->position_lock_style() == AudioTime) {
2138                 /* we're setting the pulse */
2139                 section->set_position_lock_style (MusicTime);
2140                 recompute_meters (imaginary);
2141                 section->set_position_lock_style (AudioTime);
2142         } else {
2143                 recompute_meters (imaginary);
2144         }
2145 }
2146
2147 /** places a copy of _metrics into copy and returns a pointer
2148  *  to section's equivalent.
2149  */
2150 TempoSection*
2151 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2152 {
2153         TempoSection* t;
2154         TempoSection* ret = 0;
2155         MeterSection* m;
2156
2157         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2158                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2159                         if (t == section) {
2160                                 if (t->position_lock_style() == MusicTime) {
2161                                         ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2162                                 } else {
2163                                         ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2164                                 }
2165                                 ret->set_active (t->active());
2166                                 ret->set_movable (t->movable());
2167                                 copy.push_back (ret);
2168                                 continue;
2169                         }
2170                         TempoSection* cp = 0;
2171                         if (t->position_lock_style() == MusicTime) {
2172                                 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2173                         } else {
2174                                 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2175                         }
2176                         cp->set_active (t->active());
2177                         cp->set_movable (t->movable());
2178                         copy.push_back (cp);
2179                 }
2180                 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2181                         MeterSection* cp = 0;
2182                         if (m->position_lock_style() == MusicTime) {
2183                                 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2184                                 cp->set_frame (m->frame());
2185                         } else {
2186                                 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2187                                 cp->set_pulse (m->pulse());
2188                         }
2189                         cp->set_movable (m->movable());
2190                         copy.push_back (cp);
2191                 }
2192         }
2193         //recompute_map (copy);
2194         return ret;
2195 }
2196
2197 bool
2198 TempoMap::can_solve_bbt (TempoSection* ts,  const Tempo& bpm, const BBT_Time& bbt)
2199 {
2200         Metrics copy;
2201         TempoSection* new_section = 0;
2202
2203         {
2204                 Glib::Threads::RWLock::ReaderLock lm (lock);
2205                 new_section = copy_metrics_and_point (copy, ts);
2206         }
2207
2208         double const beat = bbt_to_beats_locked (copy, bbt);
2209         bool ret = solve_map (copy, new_section, bpm, pulse_at_beat_locked (copy, beat));
2210
2211         Metrics::const_iterator d = copy.begin();
2212         while (d != copy.end()) {
2213                 delete (*d);
2214                 ++d;
2215         }
2216
2217         return ret;
2218 }
2219
2220 /**
2221 * This is for a gui that needs to know the frame of a tempo section if it were to be moved to some bbt time,
2222 * taking any possible reordering as a consequence of this into account.
2223 * @param section - the section to be altered
2224 * @param bpm - the new Tempo
2225 * @param bbt - the bbt where the altered tempo will fall
2226 * @return returns - the position in frames where the new tempo section will lie.
2227 */
2228 framepos_t
2229 TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
2230 {
2231         Glib::Threads::RWLock::ReaderLock lm (lock);
2232         Metrics future_map;
2233         framepos_t ret = 0;
2234         TempoSection* new_section = copy_metrics_and_point (future_map, section);
2235
2236         const double beat = bbt_to_beats_locked (future_map, bbt);
2237         if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
2238                 ret = new_section->frame();
2239         } else {
2240                 ret = frame_at_beat_locked (_metrics, beat);
2241         }
2242
2243         Metrics::const_iterator d = future_map.begin();
2244         while (d != future_map.end()) {
2245                 delete (*d);
2246                 ++d;
2247         }
2248         return ret;
2249 }
2250
2251 double
2252 TempoMap::predict_tempo_pulse (TempoSection* section, const Tempo& bpm, const framepos_t& frame)
2253 {
2254         Glib::Threads::RWLock::ReaderLock lm (lock);
2255         Metrics future_map;
2256         double ret = 0.0;
2257         TempoSection* new_section = copy_metrics_and_point (future_map, section);
2258
2259         if (solve_map (future_map, new_section, bpm, frame)) {
2260                 ret = new_section->pulse();
2261         } else {
2262                 ret = pulse_at_frame_locked (_metrics, frame);
2263         }
2264
2265         Metrics::const_iterator d = future_map.begin();
2266         while (d != future_map.end()) {
2267                 delete (*d);
2268                 ++d;
2269         }
2270         return ret;
2271 }
2272
2273 void
2274 TempoMap::gui_move_tempo_frame (TempoSection* ts,  const Tempo& bpm, const framepos_t& frame)
2275 {
2276         Metrics future_map;
2277         {
2278                 Glib::Threads::RWLock::WriterLock lm (lock);
2279                 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2280                 if (solve_map (future_map, new_section, bpm, frame)) {
2281                         solve_map (_metrics, ts, bpm, frame);
2282                 }
2283         }
2284
2285         Metrics::const_iterator d = future_map.begin();
2286         while (d != future_map.end()) {
2287                 delete (*d);
2288                 ++d;
2289         }
2290
2291         MetricPositionChanged (); // Emit Signal
2292 }
2293
2294 void
2295 TempoMap::gui_move_tempo_beat (TempoSection* ts,  const Tempo& bpm, const double& beat)
2296 {
2297         Metrics future_map;
2298         {
2299                 Glib::Threads::RWLock::WriterLock lm (lock);
2300                 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2301                 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
2302                         solve_map (_metrics, ts, bpm, pulse_at_beat_locked (_metrics, beat));
2303                 }
2304         }
2305
2306         Metrics::const_iterator d = future_map.begin();
2307         while (d != future_map.end()) {
2308                 delete (*d);
2309                 ++d;
2310         }
2311
2312         MetricPositionChanged (); // Emit Signal
2313 }
2314
2315 void
2316 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t&  frame)
2317 {
2318         {
2319                 Glib::Threads::RWLock::WriterLock lm (lock);
2320                 solve_map (_metrics, ms, mt, frame);
2321         }
2322
2323         MetricPositionChanged (); // Emit Signal
2324 }
2325
2326 void
2327 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const double&  beat)
2328 {
2329         {
2330                 Glib::Threads::RWLock::WriterLock lm (lock);
2331                 solve_map (_metrics, ms, mt, pulse_at_beat_locked (_metrics, beat));
2332         }
2333
2334         MetricPositionChanged (); // Emit Signal
2335 }
2336
2337 bool
2338 TempoMap::gui_change_tempo (TempoSection* ts,  const Tempo& bpm)
2339 {
2340         Metrics future_map;
2341         bool can_solve = false;
2342         {
2343                 Glib::Threads::RWLock::WriterLock lm (lock);
2344                 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2345                 new_section->set_beats_per_minute (bpm.beats_per_minute());
2346                 recompute_tempos (future_map);
2347
2348                 if (check_solved (future_map, true)) {
2349                         ts->set_beats_per_minute (bpm.beats_per_minute());
2350                         recompute_map (_metrics);
2351                         can_solve = true;
2352                 }
2353         }
2354
2355         Metrics::const_iterator d = future_map.begin();
2356         while (d != future_map.end()) {
2357                 delete (*d);
2358                 ++d;
2359         }
2360         if (can_solve) {
2361                 MetricPositionChanged (); // Emit Signal
2362         }
2363         return can_solve;
2364 }
2365
2366 framecnt_t
2367 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2368 {
2369         Glib::Threads::RWLock::ReaderLock lm (lock);
2370
2371         const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2372         const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2373         const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2374         const framecnt_t time_at_bbt = frame_at_beat_locked (_metrics, total_beats);
2375         const framecnt_t ret = time_at_bbt;
2376
2377         return ret;
2378 }
2379
2380 framepos_t
2381 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2382 {
2383         return round_to_type (fr, dir, Bar);
2384 }
2385
2386 framepos_t
2387 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2388 {
2389         return round_to_type (fr, dir, Beat);
2390 }
2391
2392 framepos_t
2393 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2394 {
2395         Glib::Threads::RWLock::ReaderLock lm (lock);
2396         uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2397         uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2398         uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2399
2400         ticks -= beats * BBT_Time::ticks_per_beat;
2401
2402         if (dir > 0) {
2403                 /* round to next (or same iff dir == RoundUpMaybe) */
2404
2405                 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2406
2407                 if (mod == 0 && dir == RoundUpMaybe) {
2408                         /* right on the subdivision, which is fine, so do nothing */
2409
2410                 } else if (mod == 0) {
2411                         /* right on the subdivision, so the difference is just the subdivision ticks */
2412                         ticks += ticks_one_subdivisions_worth;
2413
2414                 } else {
2415                         /* not on subdivision, compute distance to next subdivision */
2416
2417                         ticks += ticks_one_subdivisions_worth - mod;
2418                 }
2419
2420                 if (ticks >= BBT_Time::ticks_per_beat) {
2421                         ticks -= BBT_Time::ticks_per_beat;
2422                 }
2423         } else if (dir < 0) {
2424
2425                 /* round to previous (or same iff dir == RoundDownMaybe) */
2426
2427                 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2428
2429                 if (difference == 0 && dir == RoundDownAlways) {
2430                         /* right on the subdivision, but force-rounding down,
2431                            so the difference is just the subdivision ticks */
2432                         difference = ticks_one_subdivisions_worth;
2433                 }
2434
2435                 if (ticks < difference) {
2436                         ticks = BBT_Time::ticks_per_beat - ticks;
2437                 } else {
2438                         ticks -= difference;
2439                 }
2440
2441         } else {
2442                 /* round to nearest */
2443                 double rem;
2444
2445                 /* compute the distance to the previous and next subdivision */
2446
2447                 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2448
2449                         /* closer to the next subdivision, so shift forward */
2450
2451                         ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2452
2453                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2454
2455                         if (ticks > BBT_Time::ticks_per_beat) {
2456                                 ++beats;
2457                                 ticks -= BBT_Time::ticks_per_beat;
2458                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2459                         }
2460
2461                 } else if (rem > 0) {
2462
2463                         /* closer to previous subdivision, so shift backward */
2464
2465                         if (rem > ticks) {
2466                                 if (beats == 0) {
2467                                         /* can't go backwards past zero, so ... */
2468                                         return 0;
2469                                 }
2470                                 /* step back to previous beat */
2471                                 --beats;
2472                                 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2473                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2474                         } else {
2475                                 ticks = lrint (ticks - rem);
2476                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2477                         }
2478                 } else {
2479                         /* on the subdivision, do nothing */
2480                 }
2481         }
2482
2483         const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2484
2485         return ret_frame;
2486 }
2487
2488 void
2489 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2490 {
2491         if (sub_num == -1) {
2492                 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2493                 if ((double) when.beats > bpb / 2.0) {
2494                         ++when.bars;
2495                 }
2496                 when.beats = 1;
2497                 when.ticks = 0;
2498                 return;
2499         } else if (sub_num == 0) {
2500                 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2501                 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2502                         ++when.beats;
2503                         while ((double) when.beats > bpb) {
2504                                 ++when.bars;
2505                                 when.beats -= (uint32_t) floor (bpb);
2506                         }
2507                 }
2508                 when.ticks = 0;
2509                 return;
2510         }
2511         const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2512         double rem;
2513         if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2514                 /* closer to the next subdivision, so shift forward */
2515
2516                 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2517
2518                 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2519                         ++when.beats;
2520                         when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2521                 }
2522
2523         } else if (rem > 0) {
2524                 /* closer to previous subdivision, so shift backward */
2525
2526                 if (rem > when.ticks) {
2527                         if (when.beats == 0) {
2528                                 /* can't go backwards past zero, so ... */
2529                         }
2530                         /* step back to previous beat */
2531                         --when.beats;
2532                         when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2533                 } else {
2534                         when.ticks = when.ticks - rem;
2535                 }
2536         }
2537 }
2538
2539 framepos_t
2540 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2541 {
2542         Glib::Threads::RWLock::ReaderLock lm (lock);
2543
2544         const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2545         BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2546
2547         switch (type) {
2548         case Bar:
2549                 if (dir < 0) {
2550                         /* find bar previous to 'frame' */
2551                         bbt.beats = 1;
2552                         bbt.ticks = 0;
2553                         return frame_time (bbt);
2554
2555                 } else if (dir > 0) {
2556                         /* find bar following 'frame' */
2557                         ++bbt.bars;
2558                         bbt.beats = 1;
2559                         bbt.ticks = 0;
2560                         return frame_time (bbt);
2561                 } else {
2562                         /* true rounding: find nearest bar */
2563                         framepos_t raw_ft = frame_time (bbt);
2564                         bbt.beats = 1;
2565                         bbt.ticks = 0;
2566                         framepos_t prev_ft = frame_time (bbt);
2567                         ++bbt.bars;
2568                         framepos_t next_ft = frame_time (bbt);
2569
2570                         if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) { 
2571                                 return next_ft;
2572                         } else {
2573                                 return prev_ft;
2574                         }
2575                 }
2576
2577                 break;
2578
2579         case Beat:
2580                 if (dir < 0) {
2581                         return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2582                 } else if (dir > 0) {
2583                         return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2584                 } else {
2585                         return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2586                 }
2587                 break;
2588         }
2589
2590         return 0;
2591 }
2592
2593 void
2594 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2595                     framepos_t lower, framepos_t upper)
2596 {
2597         Glib::Threads::RWLock::ReaderLock lm (lock);
2598         const int32_t upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2599         int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2600         /* although the map handles negative beats, bbt doesn't. */
2601         if (cnt < 0.0) {
2602                 cnt = 0.0;
2603         }
2604         while (cnt <= upper_beat) {
2605                 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2606                 const TempoSection tempo = tempo_section_at_locked (pos);
2607                 const MeterSection meter = meter_section_at_locked (pos);
2608                 const BBT_Time bbt = beats_to_bbt (cnt);
2609                 points.push_back (BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.get_c_func()));
2610                 ++cnt;
2611         }
2612 }
2613
2614 const TempoSection&
2615 TempoMap::tempo_section_at (framepos_t frame) const
2616 {
2617         Glib::Threads::RWLock::ReaderLock lm (lock);
2618         return tempo_section_at_locked (frame);
2619 }
2620
2621 const TempoSection&
2622 TempoMap::tempo_section_at_locked (framepos_t frame) const
2623 {
2624         Metrics::const_iterator i;
2625         TempoSection* prev = 0;
2626
2627         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2628                 TempoSection* t;
2629
2630                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2631                         if (!t->active()) {
2632                                 continue;
2633                         }
2634                         if (t->frame() > frame) {
2635                                 break;
2636                         }
2637
2638                         prev = t;
2639                 }
2640         }
2641
2642         if (prev == 0) {
2643                 fatal << endmsg;
2644                 abort(); /*NOTREACHED*/
2645         }
2646
2647         return *prev;
2648 }
2649
2650
2651 /* don't use this to calculate length (the tempo is only correct for this frame).
2652    do that stuff based on the beat_at_frame and frame_at_beat api
2653 */
2654 double
2655 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2656 {
2657         Glib::Threads::RWLock::ReaderLock lm (lock);
2658
2659         const TempoSection* ts_at = &tempo_section_at_locked (frame);
2660         const TempoSection* ts_after = 0;
2661         Metrics::const_iterator i;
2662
2663         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2664                 TempoSection* t;
2665
2666                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2667                         if (!t->active()) {
2668                                 continue;
2669                         }
2670                         if ((*i)->frame() > frame) {
2671                                 ts_after = t;
2672                                 break;
2673                         }
2674                 }
2675         }
2676
2677         if (ts_after) {
2678                 return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2679         }
2680         /* must be treated as constant tempo */
2681         return ts_at->frames_per_beat (_frame_rate);
2682 }
2683
2684 const Tempo
2685 TempoMap::tempo_at (const framepos_t& frame) const
2686 {
2687         Glib::Threads::RWLock::ReaderLock lm (lock);
2688         return tempo_at_locked (frame);
2689 }
2690
2691 const Tempo
2692 TempoMap::tempo_at_locked (const framepos_t& frame) const
2693 {
2694         TempoSection* prev_ts = 0;
2695
2696         Metrics::const_iterator i;
2697
2698         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2699                 TempoSection* t;
2700                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2701                         if (!t->active()) {
2702                                 continue;
2703                         }
2704                         if ((prev_ts) && t->frame() > frame) {
2705                                 /* t is the section past frame */
2706                                 const double ret = prev_ts->tempo_at_frame (frame, _frame_rate) * prev_ts->note_type();
2707                                 const Tempo ret_tempo (ret, prev_ts->note_type());
2708                                 return ret_tempo;
2709                         }
2710                         prev_ts = t;
2711                 }
2712         }
2713
2714         const double ret = prev_ts->beats_per_minute();
2715         const Tempo ret_tempo (ret, prev_ts->note_type ());
2716
2717         return ret_tempo;
2718 }
2719
2720 const MeterSection&
2721 TempoMap::meter_section_at (framepos_t frame) const
2722 {
2723         Glib::Threads::RWLock::ReaderLock lm (lock);
2724         return meter_section_at_locked (frame);
2725 }
2726
2727 const MeterSection&
2728 TempoMap::meter_section_at_locked (framepos_t frame) const
2729 {
2730         //framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
2731         Metrics::const_iterator i;
2732         MeterSection* prev = 0;
2733
2734         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2735                 MeterSection* m;
2736
2737                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2738
2739                         if (prev && (*i)->frame() > frame) {
2740                                 break;
2741                         }
2742
2743                         prev = m;
2744                 }
2745         }
2746
2747         if (prev == 0) {
2748                 fatal << endmsg;
2749                 abort(); /*NOTREACHED*/
2750         }
2751
2752         return *prev;
2753 }
2754
2755 const Meter&
2756 TempoMap::meter_at (framepos_t frame) const
2757 {
2758         TempoMetric m (metric_at (frame));
2759         return m.meter();
2760 }
2761
2762 const MeterSection&
2763 TempoMap::meter_section_at (const double& beat) const
2764 {
2765         MeterSection* prev_ms = 0;
2766         Glib::Threads::RWLock::ReaderLock lm (lock);
2767
2768         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2769                 MeterSection* m;
2770                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2771                         if (prev_ms && m->beat() > beat) {
2772                                 break;
2773                         }
2774                         prev_ms = m;
2775                 }
2776
2777         }
2778         return *prev_ms;
2779 }
2780
2781 XMLNode&
2782 TempoMap::get_state ()
2783 {
2784         Metrics::const_iterator i;
2785         XMLNode *root = new XMLNode ("TempoMap");
2786
2787         {
2788                 Glib::Threads::RWLock::ReaderLock lm (lock);
2789                 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2790                         root->add_child_nocopy ((*i)->get_state());
2791                 }
2792         }
2793
2794         return *root;
2795 }
2796
2797 int
2798 TempoMap::set_state (const XMLNode& node, int /*version*/)
2799 {
2800         {
2801                 Glib::Threads::RWLock::WriterLock lm (lock);
2802
2803                 XMLNodeList nlist;
2804                 XMLNodeConstIterator niter;
2805                 Metrics old_metrics (_metrics);
2806                 _metrics.clear();
2807
2808                 nlist = node.children();
2809
2810                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2811                         XMLNode* child = *niter;
2812
2813                         if (child->name() == TempoSection::xml_state_node_name) {
2814
2815                                 try {
2816                                         TempoSection* ts = new TempoSection (*child);
2817                                         _metrics.push_back (ts);
2818                                 }
2819
2820                                 catch (failed_constructor& err){
2821                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2822                                         _metrics = old_metrics;
2823                                         break;
2824                                 }
2825
2826                         } else if (child->name() == MeterSection::xml_state_node_name) {
2827
2828                                 try {
2829                                         MeterSection* ms = new MeterSection (*child);
2830                                         _metrics.push_back (ms);
2831                                 }
2832
2833                                 catch (failed_constructor& err) {
2834                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2835                                         _metrics = old_metrics;
2836                                         break;
2837                                 }
2838                         }
2839                 }
2840
2841                 if (niter == nlist.end()) {
2842                         MetricSectionSorter cmp;
2843                         _metrics.sort (cmp);
2844                 }
2845                 /* check for legacy sessions where bbt was the base musical unit for tempo */
2846                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2847                         MeterSection* m;
2848                         TempoSection* t;
2849                         MeterSection* prev_ms = 0;
2850                         TempoSection* prev_ts = 0;
2851                         if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2852                                 if (prev_ms && prev_ms->pulse() < 0.0) {
2853                                         /*XX we cannot possibly make this work??. */
2854                                         pair<double, BBT_Time> start = make_pair (((prev_ms->bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat), prev_ms->bbt());
2855                                         prev_ms->set_beat (start);
2856                                         const double start_pulse = ((prev_ms->bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat);
2857                                         prev_ms->set_pulse (start_pulse);
2858                                 }
2859                                 prev_ms = m;
2860                         } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2861                                 if (!t->active()) {
2862                                         continue;
2863                                 }
2864                                 if (prev_ts && prev_ts->pulse() < 0.0) {
2865                                         double const start = ((prev_ts->legacy_bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ts->legacy_bbt().beats - 1) + (prev_ts->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2866                                         prev_ts->set_pulse (start);
2867                                 }
2868                                 prev_ts = t;
2869                         }
2870                 }
2871                 /* check for multiple tempo/meters at the same location, which
2872                    ardour2 somehow allowed.
2873                 */
2874
2875                 Metrics::iterator prev = _metrics.end();
2876                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2877                         if (prev != _metrics.end()) {
2878                                 MeterSection* ms;
2879                                 MeterSection* prev_ms;
2880                                 TempoSection* ts;
2881                                 TempoSection* prev_ts;
2882                                 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2883                                         if (prev_ms->pulse() == ms->pulse()) {
2884                                                 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2885                                                 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2886                                                 return -1;
2887                                         }
2888                                 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2889                                         if (prev_ts->pulse() == ts->pulse()) {
2890                                                 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2891                                                 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2892                                                 return -1;
2893                                         }
2894                                 }
2895                         }
2896                         prev = i;
2897                 }
2898
2899                 recompute_map (_metrics);
2900         }
2901
2902         PropertyChanged (PropertyChange ());
2903
2904         return 0;
2905 }
2906
2907 void
2908 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2909 {
2910         Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2911         const MeterSection* m;
2912         const TempoSection* t;
2913         const TempoSection* prev_ts = 0;
2914
2915         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2916
2917                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2918                         o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
2919                           << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2920                         o << "current      : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2921                         if (prev_ts) {
2922                                 o << "previous     : " << prev_ts->beats_per_minute() << " | " << prev_ts->pulse() << " | " << prev_ts->frame() << std::endl;
2923                                 o << "calculated   : " << prev_ts->tempo_at_pulse (t->pulse()) *  prev_ts->note_type() << " | " << prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) <<  " | " << prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
2924                         }
2925                         prev_ts = t;
2926                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2927                         o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2928                           << " pulse: " << m->pulse() <<  " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
2929                 }
2930         }
2931         o << "------" << std::endl;
2932 }
2933
2934 int
2935 TempoMap::n_tempos() const
2936 {
2937         Glib::Threads::RWLock::ReaderLock lm (lock);
2938         int cnt = 0;
2939
2940         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2941                 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2942                         cnt++;
2943                 }
2944         }
2945
2946         return cnt;
2947 }
2948
2949 int
2950 TempoMap::n_meters() const
2951 {
2952         Glib::Threads::RWLock::ReaderLock lm (lock);
2953         int cnt = 0;
2954
2955         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2956                 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2957                         cnt++;
2958                 }
2959         }
2960
2961         return cnt;
2962 }
2963
2964 void
2965 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2966 {
2967         {
2968                 Glib::Threads::RWLock::WriterLock lm (lock);
2969                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2970                         if ((*i)->frame() >= where && (*i)->movable ()) {
2971                                 (*i)->set_frame ((*i)->frame() + amount);
2972                         }
2973                 }
2974
2975                 /* now reset the BBT time of all metrics, based on their new
2976                  * audio time. This is the only place where we do this reverse
2977                  * timestamp.
2978                  */
2979
2980                 Metrics::iterator i;
2981                 const MeterSection* meter;
2982                 const TempoSection* tempo;
2983                 MeterSection *m;
2984                 TempoSection *t;
2985
2986                 meter = &first_meter ();
2987                 tempo = &first_tempo ();
2988
2989                 BBT_Time start;
2990                 BBT_Time end;
2991
2992                 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2993
2994                 bool first = true;
2995                 MetricSection* prev = 0;
2996
2997                 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2998
2999                         BBT_Time bbt;
3000                         //TempoMetric metric (*meter, *tempo);
3001                         MeterSection* ms = const_cast<MeterSection*>(meter);
3002                         TempoSection* ts = const_cast<TempoSection*>(tempo);
3003                         if (prev) {
3004                                 if (ts){
3005                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3006                                                 if (!t->active()) {
3007                                                         continue;
3008                                                 }
3009                                                 ts->set_pulse (t->pulse());
3010                                         }
3011                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3012                                                 ts->set_pulse (m->pulse());
3013                                         }
3014                                         ts->set_frame (prev->frame());
3015
3016                                 }
3017                                 if (ms) {
3018                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3019                                                 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3020                                                 ms->set_beat (start);
3021                                                 ms->set_pulse (m->pulse());
3022                                         }
3023                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3024                                                 if (!t->active()) {
3025                                                         continue;
3026                                                 }
3027                                                 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3028                                                 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3029                                                 ms->set_beat (start);
3030                                                 ms->set_pulse (t->pulse());
3031                                         }
3032                                         ms->set_frame (prev->frame());
3033                                 }
3034
3035                         } else {
3036                                 // metric will be at frames=0 bbt=1|1|0 by default
3037                                 // which is correct for our purpose
3038                         }
3039
3040                         // cerr << bbt << endl;
3041
3042                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3043                                 if (!t->active()) {
3044                                         continue;
3045                                 }
3046                                 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3047                                 tempo = t;
3048                                 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3049                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3050                                 bbt_time (m->frame(), bbt);
3051
3052                                 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3053
3054                                 if (first) {
3055                                         first = false;
3056                                 } else {
3057
3058                                         if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3059                                                 /* round up to next beat */
3060                                                 bbt.beats += 1;
3061                                         }
3062
3063                                         bbt.ticks = 0;
3064
3065                                         if (bbt.beats != 1) {
3066                                                 /* round up to next bar */
3067                                                 bbt.bars += 1;
3068                                                 bbt.beats = 1;
3069                                         }
3070                                 }
3071                                 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3072                                 m->set_beat (start);
3073                                 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3074                                 meter = m;
3075                                 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3076                         } else {
3077                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3078                                 abort(); /*NOTREACHED*/
3079                         }
3080
3081                         prev = (*i);
3082                 }
3083
3084                 recompute_map (_metrics);
3085         }
3086
3087
3088         PropertyChanged (PropertyChange ());
3089 }
3090 bool
3091 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3092 {
3093         bool moved = false;
3094
3095         std::list<MetricSection*> metric_kill_list;
3096
3097         TempoSection* last_tempo = NULL;
3098         MeterSection* last_meter = NULL;
3099         bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3100         bool meter_after = false; // is there a meter marker likewise?
3101         {
3102                 Glib::Threads::RWLock::WriterLock lm (lock);
3103                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3104                         if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3105                                 metric_kill_list.push_back(*i);
3106                                 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3107                                 if (lt)
3108                                         last_tempo = lt;
3109                                 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3110                                 if (lm)
3111                                         last_meter = lm;
3112                         }
3113                         else if ((*i)->frame() >= where) {
3114                                 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3115                                 (*i)->set_frame ((*i)->frame() - amount);
3116                                 if ((*i)->frame() == where) {
3117                                         // marker was immediately after end of range
3118                                         tempo_after = dynamic_cast<TempoSection*> (*i);
3119                                         meter_after = dynamic_cast<MeterSection*> (*i);
3120                                 }
3121                                 moved = true;
3122                         }
3123                 }
3124
3125                 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3126                 if (last_tempo && !tempo_after) {
3127                         metric_kill_list.remove(last_tempo);
3128                         last_tempo->set_frame(where);
3129                         moved = true;
3130                 }
3131                 if (last_meter && !meter_after) {
3132                         metric_kill_list.remove(last_meter);
3133                         last_meter->set_frame(where);
3134                         moved = true;
3135                 }
3136
3137                 //remove all the remaining metrics
3138                 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3139                         _metrics.remove(*i);
3140                         moved = true;
3141                 }
3142
3143                 if (moved) {
3144                         recompute_map (_metrics);
3145                 }
3146         }
3147         PropertyChanged (PropertyChange ());
3148         return moved;
3149 }
3150
3151 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3152  *  pos can be -ve, if required.
3153  */
3154 framepos_t
3155 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3156 {
3157         return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3158 }
3159
3160 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3161 framepos_t
3162 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3163 {
3164         return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3165 }
3166
3167 /** Add the BBT interval op to pos and return the result */
3168 framepos_t
3169 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3170 {
3171         Glib::Threads::RWLock::ReaderLock lm (lock);
3172
3173         BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3174         pos_bbt.ticks += op.ticks;
3175         if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3176                 ++pos_bbt.beats;
3177                 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3178         }
3179         pos_bbt.beats += op.beats;
3180         /* the meter in effect will start on the bar */
3181         double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3182         while (pos_bbt.beats >= divisions_per_bar + 1) {
3183                 ++pos_bbt.bars;
3184                 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3185                 pos_bbt.beats -= divisions_per_bar;
3186         }
3187         pos_bbt.bars += op.bars;
3188
3189         return frame_time_locked (_metrics, pos_bbt);
3190 }
3191
3192 /** Count the number of beats that are equivalent to distance when going forward,
3193     starting at pos.
3194 */
3195 Evoral::Beats
3196 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3197 {
3198         return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3199 }
3200
3201 struct bbtcmp {
3202     bool operator() (const BBT_Time& a, const BBT_Time& b) {
3203             return a < b;
3204     }
3205 };
3206
3207 std::ostream&
3208 operator<< (std::ostream& o, const Meter& m) {
3209         return o << m.divisions_per_bar() << '/' << m.note_divisor();
3210 }
3211
3212 std::ostream&
3213 operator<< (std::ostream& o, const Tempo& t) {
3214         return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3215 }
3216
3217 std::ostream&
3218 operator<< (std::ostream& o, const MetricSection& section) {
3219
3220         o << "MetricSection @ " << section.frame() << ' ';
3221
3222         const TempoSection* ts;
3223         const MeterSection* ms;
3224
3225         if ((ts = dynamic_cast<const TempoSection*> (&section)) != 0) {
3226                 o << *((const Tempo*) ts);
3227         } else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
3228                 o << *((const Meter*) ms);
3229         }
3230
3231         return o;
3232 }