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