fix positioning of grid line after a non-beat aligned tempo change
[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
23 #include <unistd.h>
24
25 #include <cmath>
26
27 #include <glibmm/thread.h>
28 #include "pbd/xml++.h"
29 #include "evoral/types.hpp"
30 #include "ardour/debug.h"
31 #include "ardour/tempo.h"
32 #include "ardour/utils.h"
33
34 #include "i18n.h"
35 #include <locale.h>
36
37 using namespace std;
38 using namespace ARDOUR;
39 using namespace PBD;
40
41 using Timecode::BBT_Time;
42
43 /* _default tempo is 4/4 qtr=120 */
44
45 Meter    TempoMap::_default_meter (4.0, 4.0);
46 Tempo    TempoMap::_default_tempo (120.0);
47
48 double 
49 Tempo::frames_per_beat (framecnt_t sr) const
50 {
51         return  (60.0 * sr) / _beats_per_minute;
52 }
53
54 /***********************************************************************/
55
56 double 
57 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
58 {
59         /* This is tempo- and meter-sensitive. The number it returns
60            is based on the interval between any two lines in the 
61            grid that is constructed from tempo and meter sections.
62
63            The return value IS NOT interpretable in terms of "beats".
64         */
65
66         return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
67 }
68
69 double
70 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
71 {
72         return frames_per_grid (tempo, sr) * _divisions_per_bar;
73 }
74
75 /***********************************************************************/
76
77 const string TempoSection::xml_state_node_name = "Tempo";
78
79 TempoSection::TempoSection (const XMLNode& node)
80         : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
81 {
82         const XMLProperty *prop;
83         BBT_Time start;
84         LocaleGuard lg (X_("POSIX"));
85
86         if ((prop = node.property ("start")) == 0) {
87                 error << _("TempoSection XML node has no \"start\" property") << endmsg;
88                 throw failed_constructor();
89         }
90
91         if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
92                     &start.bars,
93                     &start.beats,
94                     &start.ticks) < 3) {
95                 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
96                 throw failed_constructor();
97         }
98
99         set_start (start);
100
101         if ((prop = node.property ("beats-per-minute")) == 0) {
102                 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
103                 throw failed_constructor();
104         }
105
106         if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
107                 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
108                 throw failed_constructor();
109         }
110
111         if ((prop = node.property ("note-type")) == 0) {
112                 /* older session, make note type be quarter by default */
113                 _note_type = 4.0;
114         } else {
115                 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
116                         error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
117                         throw failed_constructor();
118                 }
119         }
120
121         if ((prop = node.property ("movable")) == 0) {
122                 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
123                 throw failed_constructor();
124         }
125
126         set_movable (string_is_affirmative (prop->value()));
127
128         if ((prop = node.property ("bar-offset")) == 0) {
129                 _bar_offset = -1.0;
130         } else {
131                 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
132                         error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
133                         throw failed_constructor();
134                 }
135         }
136 }
137
138 XMLNode&
139 TempoSection::get_state() const
140 {
141         XMLNode *root = new XMLNode (xml_state_node_name);
142         char buf[256];
143         LocaleGuard lg (X_("POSIX"));
144
145         snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
146                   start().bars,
147                   start().beats,
148                   start().ticks);
149         root->add_property ("start", buf);
150         snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
151         root->add_property ("beats-per-minute", buf);
152         snprintf (buf, sizeof (buf), "%f", _note_type);
153         root->add_property ("note-type", buf);
154         // snprintf (buf, sizeof (buf), "%f", _bar_offset);
155         // root->add_property ("bar-offset", buf);
156         snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
157         root->add_property ("movable", buf);
158
159         return *root;
160 }
161
162 void
163
164 TempoSection::update_bar_offset_from_bbt (const Meter& m)
165 {
166         _bar_offset = ((start().beats - 1) * BBT_Time::ticks_per_beat + start().ticks) / 
167                 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
168
169         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
170 }
171
172 void
173 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
174 {
175         BBT_Time new_start;
176
177         if (_bar_offset < 0.0) {
178                 /* not set yet */
179                 return;
180         }
181
182         new_start.bars = start().bars;
183         
184         double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
185         new_start.beats = (uint32_t) floor(ticks/BBT_Time::ticks_per_beat);
186         new_start.ticks = (uint32_t) fmod (ticks, BBT_Time::ticks_per_beat);
187
188         /* remember the 1-based counting properties of beats */
189         new_start.beats += 1;
190                                             
191         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n", 
192                                                        _bar_offset, meter.divisions_per_bar(), ticks, new_start.ticks, new_start.beats));
193
194         set_start (new_start);
195 }
196
197 /***********************************************************************/
198
199 const string MeterSection::xml_state_node_name = "Meter";
200
201 MeterSection::MeterSection (const XMLNode& node)
202         : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
203 {
204         const XMLProperty *prop;
205         BBT_Time start;
206         LocaleGuard lg (X_("POSIX"));
207
208         if ((prop = node.property ("start")) == 0) {
209                 error << _("MeterSection XML node has no \"start\" property") << endmsg;
210                 throw failed_constructor();
211         }
212
213         if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
214                     &start.bars,
215                     &start.beats,
216                     &start.ticks) < 3) {
217                 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
218                 throw failed_constructor();
219         }
220
221         set_start (start);
222
223         /* beats-per-bar is old; divisions-per-bar is new */
224
225         if ((prop = node.property ("divisions-per-bar")) == 0) {
226                 if ((prop = node.property ("beats-per-bar")) == 0) {
227                         error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
228                         throw failed_constructor();
229                 } 
230         }
231
232         if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
233                 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
234                 throw failed_constructor();
235         }
236
237         if ((prop = node.property ("note-type")) == 0) {
238                 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
239                 throw failed_constructor();
240         }
241
242         if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
243                 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
244                 throw failed_constructor();
245         }
246
247         if ((prop = node.property ("movable")) == 0) {
248                 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
249                 throw failed_constructor();
250         }
251
252         set_movable (string_is_affirmative (prop->value()));
253 }
254
255 XMLNode&
256 MeterSection::get_state() const
257 {
258         XMLNode *root = new XMLNode (xml_state_node_name);
259         char buf[256];
260         LocaleGuard lg (X_("POSIX"));
261
262         snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
263                   start().bars,
264                   start().beats,
265                   start().ticks);
266         root->add_property ("start", buf);
267         snprintf (buf, sizeof (buf), "%f", _note_type);
268         root->add_property ("note-type", buf);
269         snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
270         root->add_property ("divisions-per-bar", buf);
271         snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
272         root->add_property ("movable", buf);
273
274         return *root;
275 }
276
277 /***********************************************************************/
278
279 struct MetricSectionSorter {
280     bool operator() (const MetricSection* a, const MetricSection* b) {
281             return a->start() < b->start();
282     }
283 };
284
285 TempoMap::TempoMap (framecnt_t fr)
286 {
287         _frame_rate = fr;
288         BBT_Time start;
289
290         start.bars = 1;
291         start.beats = 1;
292         start.ticks = 0;
293
294         TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
295         MeterSection *m = new MeterSection (start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
296
297         t->set_movable (false);
298         m->set_movable (false);
299
300         /* note: frame time is correct (zero) for both of these */
301
302         metrics.push_back (t);
303         metrics.push_back (m);
304 }
305
306 TempoMap::~TempoMap ()
307 {
308 }
309
310 void
311 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
312 {
313         bool removed = false;
314
315         {
316                 Glib::RWLock::WriterLock lm (lock);
317                 Metrics::iterator i;
318
319                 for (i = metrics.begin(); i != metrics.end(); ++i) {
320                         if (dynamic_cast<TempoSection*> (*i) != 0) {
321                                 if (tempo.frame() == (*i)->frame()) {
322                                         if ((*i)->movable()) {
323                                                 metrics.erase (i);
324                                                 removed = true;
325                                                 break;
326                                         }
327                                 }
328                         }
329                 }
330
331                 if (removed && complete_operation) {
332                         recompute_map (false);
333                 }
334         }
335
336         if (removed && complete_operation) {
337                 PropertyChanged (PropertyChange ());
338         }
339 }
340
341 void
342 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
343 {
344         bool removed = false;
345
346         {
347                 Glib::RWLock::WriterLock lm (lock);
348                 Metrics::iterator i;
349
350                 for (i = metrics.begin(); i != metrics.end(); ++i) {
351                         if (dynamic_cast<MeterSection*> (*i) != 0) {
352                                 if (tempo.frame() == (*i)->frame()) {
353                                         if ((*i)->movable()) {
354                                                 metrics.erase (i);
355                                                 removed = true;
356                                                 break;
357                                         }
358                                 }
359                         }
360                 }
361
362                 if (removed && complete_operation) {
363                         recompute_map (true);
364                 }
365         }
366
367         if (removed && complete_operation) {
368                 PropertyChanged (PropertyChange ());
369         }
370 }
371
372 void
373 TempoMap::do_insert (MetricSection* section)
374 {
375         bool need_add = true;
376
377         assert (section->start().ticks == 0);
378
379         /* we only allow new meters to be inserted on beat 1 of an existing
380          * measure. 
381          */
382
383         if (dynamic_cast<MeterSection*>(section)) {
384
385                 /* we need to (potentially) update the BBT times of tempo
386                    sections based on this new meter.
387                 */
388                 
389                 if ((section->start().beats != 1) || (section->start().ticks != 0)) {
390                         
391                         BBT_Time corrected = section->start();
392                         corrected.beats = 1;
393                         corrected.ticks = 0;
394                         
395                         warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
396                                                    section->start(), corrected) << endmsg;
397                         
398                         section->set_start (corrected);
399                 }
400         }
401
402         Metrics::iterator i;
403
404         /* Look for any existing MetricSection that is of the same type and
405            at the same time as the new one, and remove it before adding
406            the new one.
407         */
408
409         Metrics::iterator to_remove = metrics.end ();
410
411         for (i = metrics.begin(); i != metrics.end(); ++i) {
412
413                 int const c = (*i)->compare (*section);
414
415                 if (c < 0) {
416                         /* this section is before the one to be added; go back round */
417                         continue;
418                 } else if (c > 0) {
419                         /* this section is after the one to be added; there can't be any at the same time */
420                         break;
421                 }
422
423                 /* hacky comparison of type */
424                 bool const iter_is_tempo = dynamic_cast<TempoSection*> (*i) != 0;
425                 bool const insert_is_tempo = dynamic_cast<TempoSection*> (section) != 0;
426
427                 if (iter_is_tempo == insert_is_tempo) {
428
429                         if (!(*i)->movable()) {
430
431                                 /* can't (re)move this section, so overwrite
432                                  * its data content (but not its properties as
433                                  * a section
434                                  */
435
436                                 if (!iter_is_tempo) {
437                                         *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(section));
438                                 } else {
439                                         *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(section));
440                                 }
441                                 need_add = false;
442                                 break;
443                         }
444
445                         to_remove = i;
446                         break;
447                 }
448         }
449
450         if (to_remove != metrics.end()) {
451                 /* remove the MetricSection at the same time as the one we are about to add */
452                 metrics.erase (to_remove);
453         }
454
455         /* Add the given MetricSection */
456
457         if (need_add) {
458                 for (i = metrics.begin(); i != metrics.end(); ++i) {
459                         
460                         if ((*i)->compare (*section) < 0) {
461                                 continue;
462                         }
463                         
464                         metrics.insert (i, section);
465                         break;
466                 }
467
468                 if (i == metrics.end()) {
469                         metrics.insert (metrics.end(), section);
470                 }
471         }
472 }
473
474 void
475 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const BBT_Time& where)
476 {
477         const TempoSection& first (first_tempo());
478
479         if (ts != first) {
480                 remove_tempo (ts, false);
481                 add_tempo (tempo, where);
482         } else {
483                 {
484                         Glib::RWLock::WriterLock lm (lock);
485                         /* cannot move the first tempo section */
486                         *((Tempo*)&first) = tempo;
487                         recompute_map (false);
488                 }
489         }
490
491         PropertyChanged (PropertyChange ());
492 }
493
494 void
495 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
496 {
497         {
498                 Glib::RWLock::WriterLock lm (lock);
499
500                 /* new tempos always start on a beat */
501                 where.ticks = 0;
502
503                 TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type());
504                 
505                 /* find the meter to use to set the bar offset of this
506                  * tempo section.
507                  */
508
509                 const Meter* meter = &first_meter();
510                 
511                 /* as we start, we are *guaranteed* to have m.meter and m.tempo pointing
512                    at something, because we insert the default tempo and meter during
513                    TempoMap construction.
514                    
515                    now see if we can find better candidates.
516                 */
517                 
518                 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
519                         
520                         const MeterSection* m;
521                         
522                         if (where < (*i)->start()) {
523                                 break;
524                         }
525                         
526                         if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
527                                 meter = m;
528                         }
529                 }
530
531                 ts->update_bar_offset_from_bbt (*meter);
532
533                 /* and insert it */
534                 
535                 do_insert (ts);
536
537                 recompute_map (false);
538         }
539
540
541         PropertyChanged (PropertyChange ());
542 }
543
544 void
545 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
546 {
547         const MeterSection& first (first_meter());
548
549         if (ms != first) {
550                 remove_meter (ms, false);
551                 add_meter (meter, where);
552         } else {
553                 {
554                         Glib::RWLock::WriterLock lm (lock);
555                         /* cannot move the first meter section */
556                         *((Meter*)&first) = meter;
557                         recompute_map (true);
558                 }
559         }
560
561         PropertyChanged (PropertyChange ());
562 }
563
564 void
565 TempoMap::add_meter (const Meter& meter, BBT_Time where)
566 {
567         {
568                 Glib::RWLock::WriterLock lm (lock);
569
570                 /* a new meter always starts a new bar on the first beat. so
571                    round the start time appropriately. remember that
572                    `where' is based on the existing tempo map, not
573                    the result after we insert the new meter.
574
575                 */
576
577                 if (where.beats != 1) {
578                         where.beats = 1;
579                         where.bars++;
580                 }
581
582                 /* new meters *always* start on a beat. */
583                 where.ticks = 0;
584                 
585                 do_insert (new MeterSection (where, meter.divisions_per_bar(), meter.note_divisor()));
586                 recompute_map (true);
587         }
588
589         
590 #ifndef NDEBUG
591         if (DEBUG_ENABLED(DEBUG::TempoMap)) {
592                 dump (std::cerr);
593         }
594 #endif
595
596         PropertyChanged (PropertyChange ());
597 }
598
599 void
600 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
601 {
602         Tempo newtempo (beats_per_minute, note_type);
603         TempoSection* t;
604
605         for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
606                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
607                         { 
608                                 Glib::RWLock::WriterLock lm (lock);
609                                 *((Tempo*) t) = newtempo;
610                                 recompute_map (false);
611                         }
612                         PropertyChanged (PropertyChange ());
613                         break;
614                 }
615         }
616 }
617
618 void
619 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
620 {
621         Tempo newtempo (beats_per_minute, note_type);
622
623         TempoSection* prev;
624         TempoSection* first;
625         Metrics::iterator i;
626
627         /* find the TempoSection immediately preceding "where"
628          */
629
630         for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
631
632                 if ((*i)->frame() > where) {
633                         break;
634                 }
635
636                 TempoSection* t;
637
638                 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
639                         if (!first) {
640                                 first = t;
641                         }
642                         prev = t;
643                 }
644         }
645
646         if (!prev) {
647                 if (!first) {
648                         error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
649                         return;
650                 }
651
652                 prev = first;
653         }
654
655         /* reset */
656
657         {
658                 Glib::RWLock::WriterLock lm (lock);
659                 /* cannot move the first tempo section */
660                 *((Tempo*)prev) = newtempo;
661                 recompute_map (false);
662         }
663
664         PropertyChanged (PropertyChange ());
665 }
666
667 const MeterSection&
668 TempoMap::first_meter () const
669 {
670         const MeterSection *m = 0;
671
672         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
673                 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
674                         return *m;
675                 }
676         }
677
678         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
679         /*NOTREACHED*/
680         return *m;
681 }
682
683 const TempoSection&
684 TempoMap::first_tempo () const
685 {
686         const TempoSection *t = 0;
687
688         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
689                 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
690                         return *t;
691                 }
692         }
693
694         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
695         /*NOTREACHED*/
696         return *t;
697 }
698
699 void
700 TempoMap::require_map_to (framepos_t pos)
701 {
702         Glib::RWLock::WriterLock lm (lock);
703
704         if (_map.empty() || _map.back().frame < pos) {
705                 extend_map (pos);
706         }
707 }
708
709 void
710 TempoMap::require_map_to (const BBT_Time& bbt)
711 {
712         Glib::RWLock::WriterLock lm (lock);
713
714         /* since we have no idea where BBT is if its off the map, see the last
715          * point in the map is past BBT, and if not add an arbitrary amount of
716          * time until it is.
717          */
718
719         int additional_minutes = 1;
720         
721         while (1) {
722                 if (!_map.empty() && _map.back().bar >= (bbt.bars + 1)) {
723                         break;
724                 }
725                 /* add some more distance, using bigger steps each time */
726                 extend_map (_map.back().frame + (_frame_rate * 60 * additional_minutes));
727                 additional_minutes *= 2;
728         }
729 }
730
731 void
732 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
733 {
734         /* CALLER MUST HOLD WRITE LOCK */
735
736         MeterSection* meter = 0;
737         TempoSection* tempo = 0;
738         double current_frame;
739         BBT_Time current;
740         Metrics::iterator next_metric;
741
742         if (end < 0) {
743
744                 if (_map.empty()) {
745                         /* compute 1 mins worth */
746                         end = _frame_rate * 60;
747                 } else {
748                         end = _map.back().frame;
749                 }
750         } else {
751                 if (!_map.empty ()) {
752                         /* never allow the map to be shortened */
753                         end = max (end, _map.back().frame);
754                 }
755         }
756
757         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
758
759         for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
760                 MeterSection* ms;
761
762                 if ((ms = dynamic_cast<MeterSection *> (*i)) != 0) {
763                         meter = ms;
764                         break;
765                 }
766         }
767
768         for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
769                 TempoSection* ts;
770
771                 if ((ts = dynamic_cast<TempoSection *> (*i)) != 0) {
772                         tempo = ts;
773                         break;
774                 }
775         }
776
777         /* assumes that the first meter & tempo are at frame zero */
778         current_frame = 0;
779         meter->set_frame (0);
780         tempo->set_frame (0);
781
782         /* assumes that the first meter & tempo are at 1|1|0 */
783         current.bars = 1;
784         current.beats = 1;
785         current.ticks = 0;
786
787         if (reassign_tempo_bbt) {
788
789                 MeterSection* rmeter = meter;
790
791                 DEBUG_TRACE (DEBUG::TempoMath, "\tUpdating tempo marks BBT time from bar offset\n");
792
793                 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
794
795                         TempoSection* ts;
796                         MeterSection* ms;
797         
798                         if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
799
800                                 /* reassign the BBT time of this tempo section
801                                  * based on its bar offset position.
802                                  */
803
804                                 ts->update_bbt_time_from_bar_offset (*rmeter);
805
806                         } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
807                                 rmeter = ms;
808                         } else {
809                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
810                                 /*NOTREACHED*/
811                         }
812                 }
813         }
814
815         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("start with meter = %1 tempo = %2\n", *((Meter*)meter), *((Tempo*)tempo)));
816
817         next_metric = metrics.begin();
818         ++next_metric; // skip meter (or tempo)
819         ++next_metric; // skip tempo (or meter)
820
821         _map.clear ();
822
823         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add first bar at 1|1 @ %2\n", current.bars, current_frame));
824         _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), 1, 1));
825
826         if (end == 0) {
827                 /* silly call from Session::process() during startup
828                  */
829                 return;
830         }
831
832         _extend_map (tempo, meter, next_metric, current, current_frame, end);
833 }
834
835 void
836 TempoMap::extend_map (framepos_t end)
837 {
838         /* CALLER MUST HOLD WRITE LOCK */
839
840         if (_map.empty()) {
841                 recompute_map (false, end);
842                 return;
843         }
844
845         BBTPointList::const_iterator i = _map.end();    
846         Metrics::iterator next_metric;
847
848         --i;
849
850         BBT_Time last_metric_start;
851
852         if ((*i).tempo->frame() > (*i).meter->frame()) {
853                 last_metric_start = (*i).tempo->start();
854         } else {
855                 last_metric_start = (*i).meter->start();
856         }
857
858         /* find the metric immediately after the tempo + meter sections for the
859          * last point in the map 
860          */
861
862         for (next_metric = metrics.begin(); next_metric != metrics.end(); ++next_metric) {
863                 if ((*next_metric)->start() > last_metric_start) {
864                         break;
865                 }
866         }
867
868         /* we cast away const here because this is the one place where we need
869          * to actually modify the frame time of each metric section. 
870          */
871
872         _extend_map (const_cast<TempoSection*> ((*i).tempo), 
873                      const_cast<MeterSection*> ((*i).meter),
874                      next_metric, BBT_Time ((*i).bar, (*i).beat, 0), (*i).frame, end);
875 }
876
877 void
878 TempoMap::_extend_map (TempoSection* tempo, MeterSection* meter, 
879                        Metrics::iterator next_metric,
880                        BBT_Time current, framepos_t current_frame, framepos_t end)
881 {
882         /* CALLER MUST HOLD WRITE LOCK */
883
884         TempoSection* ts;
885         MeterSection* ms;
886         double divisions_per_bar;
887         double beat_frames;
888         framepos_t bar_start_frame;
889
890         if (current.beats == 1) {
891                 bar_start_frame = current_frame;
892         } else {
893                 bar_start_frame = 0;
894         }
895
896         divisions_per_bar = meter->divisions_per_bar ();
897         beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
898
899         while (current_frame < end) {
900
901                 current.beats++;
902                 current_frame += beat_frames;
903
904                 if (current.beats > meter->divisions_per_bar()) {
905                         current.bars++;
906                         current.beats = 1;
907                 }
908
909                 if (next_metric != metrics.end()) {
910
911                         /* no operator >= so invert operator < */
912
913                         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("now at %1 next metric @ %2\n", current, (*next_metric)->start()));
914
915                         if (!(current < (*next_metric)->start())) {
916
917                           set_metrics:
918                                 if (((ts = dynamic_cast<TempoSection*> (*next_metric)) != 0)) {
919
920                                         tempo = ts;
921
922                                         /* new tempo section: if its on a beat,
923                                          * we don't have to do anything other
924                                          * than recompute various distances,
925                                          * done further below as we transition
926                                          * the next metric section.
927                                          *
928                                          * if its not on the beat, we have to
929                                          * compute the duration of the beat it
930                                          * is within, which will be different
931                                          * from the preceding following ones
932                                          * since it takes part of its duration
933                                          * from the preceding tempo and part 
934                                          * from this new tempo.
935                                          */
936
937                                         if (tempo->start().ticks != 0) {
938                                                 
939                                                 double next_beat_frames = tempo->frames_per_beat (_frame_rate);                                 
940                                                 
941                                                 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into non-beat-aligned tempo metric at %1 = %2, adjust next beat using %3\n",
942                                                                                                tempo->start(), current_frame, tempo->bar_offset()));
943                                                 
944                                                 /* back up to previous beat */
945                                                 current_frame -= beat_frames;
946
947                                                 /* set tempo section location
948                                                  * based on offset from last
949                                                  * bar start 
950                                                  */
951                                                 tempo->set_frame (bar_start_frame + 
952                                                                   llrint ((ts->bar_offset() * meter->divisions_per_bar() * beat_frames)));
953                                                 
954                                                 /* advance to the location of
955                                                  * the new (adjusted) beat. do
956                                                  * this by figuring out the
957                                                  * offset within the beat that
958                                                  * would have been there
959                                                  * without the tempo
960                                                  * change. then stretch the
961                                                  * beat accordingly.
962                                                  */
963
964                                                 double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
965
966                                                 current_frame += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
967
968                                                 /* next metric doesn't have to
969                                                  * match this precisely to
970                                                  * merit a reloop ...
971                                                  */
972                                                 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Adjusted last beat to %1\n", current_frame));
973                                                 
974                                         } else {
975                                                 
976                                                 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into beat-aligned tempo metric at %1 = %2\n",
977                                                                                                tempo->start(), current_frame));
978                                                 tempo->set_frame (current_frame);
979                                         }
980
981                                 } else if ((ms = dynamic_cast<MeterSection*>(*next_metric)) != 0) {
982                                         
983                                         meter = ms;
984
985                                         /* new meter section: always defines the
986                                          * start of a bar.
987                                          */
988                                         
989                                         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("bumped into meter section at %1 vs %2 (%3)\n",
990                                                                                        meter->start(), current, current_frame));
991                                         
992                                         assert (current.beats == 1);
993
994                                         meter->set_frame (current_frame);
995                                 }
996                                 
997                                 divisions_per_bar = meter->divisions_per_bar ();
998                                 beat_frames = meter->frames_per_grid (*tempo, _frame_rate);
999                                 
1000                                 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("New metric with beat frames = %1 dpb %2 meter %3 tempo %4\n", 
1001                                                                                beat_frames, divisions_per_bar, *((Meter*)meter), *((Tempo*)tempo)));
1002                         
1003                                 ++next_metric;
1004
1005                                 if (next_metric != metrics.end() && ((*next_metric)->start() == current)) {
1006                                         /* same position so go back and set this one up before advancing
1007                                         */
1008                                         goto set_metrics;
1009                                 }
1010                         }
1011                 }
1012
1013                 if (current.beats == 1) {
1014                         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
1015                         _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
1016                         bar_start_frame = current_frame;
1017                 } else {
1018                         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
1019                         _map.push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
1020                 }
1021         }
1022 }
1023
1024 TempoMetric
1025 TempoMap::metric_at (framepos_t frame) const
1026 {
1027         Glib::RWLock::ReaderLock lm (lock);
1028         TempoMetric m (first_meter(), first_tempo());
1029         const Meter* meter;
1030         const Tempo* tempo;
1031
1032         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1033            at something, because we insert the default tempo and meter during
1034            TempoMap construction.
1035
1036            now see if we can find better candidates.
1037         */
1038
1039         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1040
1041                 // cerr << "Looking at a metric section " << **i << endl;
1042
1043                 if ((*i)->frame() > frame) {
1044                         break;
1045                 }
1046
1047                 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1048                         m.set_tempo (*tempo);
1049                 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1050                         m.set_meter (*meter);
1051                 }
1052
1053                 m.set_frame ((*i)->frame ());
1054                 m.set_start ((*i)->start ());
1055         }
1056         
1057         // cerr << "for framepos " << frame << " returning " << m.meter() << " @ " << m.tempo() << " location " << m.frame() << " = " << m.start() << endl;
1058         return m;
1059 }
1060
1061 TempoMetric
1062 TempoMap::metric_at (BBT_Time bbt) const
1063 {
1064         Glib::RWLock::ReaderLock lm (lock);
1065         TempoMetric m (first_meter(), first_tempo());
1066         const Meter* meter;
1067         const Tempo* tempo;
1068
1069         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1070            at something, because we insert the default tempo and meter during
1071            TempoMap construction.
1072
1073            now see if we can find better candidates.
1074         */
1075
1076         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1077
1078                 BBT_Time section_start ((*i)->start());
1079
1080                 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1081                         break;
1082                 }
1083
1084                 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
1085                         m.set_tempo (*tempo);
1086                 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
1087                         m.set_meter (*meter);
1088                 }
1089
1090                 m.set_frame ((*i)->frame ());
1091                 m.set_start (section_start);
1092         }
1093
1094         return m;
1095 }
1096
1097 void
1098 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1099 {
1100         require_map_to (frame);
1101
1102         Glib::RWLock::ReaderLock lm (lock);
1103         return bbt_time (frame, bbt, bbt_before_or_at (frame));
1104 }
1105
1106 void
1107 TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
1108 {
1109         Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1110
1111         if (!lm.locked()) {
1112                 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1113         }
1114         
1115         if (_map.empty() || _map.back().frame < frame) {
1116                 throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
1117         }
1118
1119         return bbt_time (frame, bbt, bbt_before_or_at (frame));
1120 }
1121
1122 void
1123 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1124 {
1125         /* CALLER MUST HOLD READ LOCK */
1126
1127         bbt.bars = (*i).bar;
1128         bbt.beats = (*i).beat;
1129
1130         if ((*i).frame == frame) {
1131                 bbt.ticks = 0;
1132         } else {
1133                 bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
1134                                     BBT_Time::ticks_per_beat);
1135         }
1136 }
1137
1138 framepos_t
1139 TempoMap::frame_time (const BBT_Time& bbt)
1140 {
1141         require_map_to (bbt);
1142
1143         Glib::RWLock::ReaderLock lm (lock);
1144
1145         BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
1146         BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
1147
1148         if (bbt.ticks != 0) {
1149                 return ((*e).frame - (*s).frame) + 
1150                         llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
1151         } else {
1152                 return ((*e).frame - (*s).frame);
1153         }
1154 }
1155
1156 framecnt_t
1157 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1158 {
1159         Glib::RWLock::ReaderLock lm (lock);
1160         framecnt_t frames = 0;
1161         BBT_Time when;
1162
1163         bbt_time (pos, when);
1164         frames = bbt_duration_at_unlocked (when, bbt,dir);
1165
1166         return frames;
1167 }
1168
1169 framecnt_t
1170 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) 
1171 {
1172         if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1173                 return 0;
1174         }
1175
1176         /* round back to the previous precise beat */
1177         BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
1178         BBTPointList::const_iterator start (wi);
1179         double tick_frames = 0;
1180
1181         assert (wi != _map.end());
1182
1183         /* compute how much rounding we did because of non-zero ticks */
1184
1185         if (when.ticks != 0) {
1186                 tick_frames = (*wi).tempo->frames_per_beat (_frame_rate) * (when.ticks/BBT_Time::ticks_per_beat);
1187         }
1188         
1189         uint32_t bars = 0;
1190         uint32_t beats = 0;
1191
1192         while (wi != _map.end() && bars < bbt.bars) {
1193                 ++wi;
1194                 if ((*wi).is_bar()) {
1195                         ++bars;
1196                 }
1197         }
1198         assert (wi != _map.end());
1199
1200         while (wi != _map.end() && beats < bbt.beats) {
1201                 ++wi;
1202                 ++beats;
1203         }
1204         assert (wi != _map.end());
1205
1206         /* add any additional frames related to ticks in the added value */
1207
1208         if (bbt.ticks != 0) {
1209                 tick_frames += (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
1210         }
1211
1212         return ((*wi).frame - (*start).frame) + llrint (tick_frames);
1213 }
1214
1215 framepos_t
1216 TempoMap::round_to_bar (framepos_t fr, int dir)
1217 {
1218         return round_to_type (fr, dir, Bar);
1219 }
1220
1221 framepos_t
1222 TempoMap::round_to_beat (framepos_t fr, int dir)
1223 {
1224         return round_to_type (fr, dir, Beat);
1225 }
1226
1227 framepos_t
1228 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1229 {
1230         require_map_to (fr);
1231
1232         Glib::RWLock::ReaderLock lm (lock);
1233         BBTPointList::const_iterator i = bbt_before_or_at (fr);
1234         BBT_Time the_beat;
1235         uint32_t ticks_one_subdivisions_worth;
1236         uint32_t difference;
1237
1238         bbt_time (fr, the_beat, i);
1239
1240         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1241                                                      fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1242
1243         ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1244
1245         if (dir > 0) {
1246
1247                 /* round to next (even if we're on a subdivision */
1248
1249                 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1250
1251                 if (mod == 0) {
1252                         /* right on the subdivision, so the difference is just the subdivision ticks */
1253                         the_beat.ticks += ticks_one_subdivisions_worth;
1254
1255                 } else {
1256                         /* not on subdivision, compute distance to next subdivision */
1257
1258                         the_beat.ticks += ticks_one_subdivisions_worth - mod;
1259                 }
1260
1261                 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1262                         assert (i != _map.end());
1263                         ++i;
1264                         assert (i != _map.end());
1265                         the_beat.ticks -= BBT_Time::ticks_per_beat;
1266                 } 
1267
1268
1269         } else if (dir < 0) {
1270
1271                 /* round to previous (even if we're on a subdivision) */
1272
1273                 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1274
1275                 if (mod == 0) {
1276                         /* right on the subdivision, so the difference is just the subdivision ticks */
1277                         difference = ticks_one_subdivisions_worth;
1278                 } else {
1279                         /* not on subdivision, compute distance to previous subdivision, which
1280                            is just the modulus.
1281                         */
1282
1283                         difference = mod;
1284                 }
1285
1286                 if (the_beat.ticks < difference) {
1287                         if (i == _map.begin()) {
1288                                 /* can't go backwards from wherever pos is, so just return it */
1289                                 return fr;
1290                         }
1291                         --i;
1292                         the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks;
1293                 } else {
1294                         the_beat.ticks -= difference;
1295                 }
1296
1297         } else {
1298                 /* round to nearest */
1299
1300                 double rem;
1301
1302                 /* compute the distance to the previous and next subdivision */
1303                 
1304                 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1305                         
1306                         /* closer to the next subdivision, so shift forward */
1307
1308                         the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1309
1310                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1311
1312                         if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1313                                 assert (i != _map.end());
1314                                 ++i;
1315                                 assert (i != _map.end());
1316                                 the_beat.ticks -= BBT_Time::ticks_per_beat;
1317                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1318                         } 
1319
1320                 } else if (rem > 0) {
1321                         
1322                         /* closer to previous subdivision, so shift backward */
1323
1324                         if (rem > the_beat.ticks) {
1325                                 if (i == _map.begin()) {
1326                                         /* can't go backwards past zero, so ... */
1327                                         return 0;
1328                                 }
1329                                 /* step back to previous beat */
1330                                 --i;
1331                                 the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem);
1332                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1333                         } else {
1334                                 the_beat.ticks = lrint (the_beat.ticks - rem);
1335                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1336                         }
1337                 } else {
1338                         /* on the subdivision, do nothing */
1339                 }
1340         }
1341
1342         return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) * 
1343                 (*i).tempo->frames_per_beat (_frame_rate);
1344 }
1345
1346 framepos_t
1347 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1348 {
1349         require_map_to (frame);
1350
1351         Glib::RWLock::ReaderLock lm (lock);
1352         BBTPointList::const_iterator fi;
1353
1354         if (dir > 0) {
1355                 fi = bbt_after_or_at (frame);
1356         } else {
1357                 fi = bbt_before_or_at (frame);
1358         }
1359
1360         assert (fi != _map.end());
1361
1362         DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3|%4 @ %5) to bars in direction %2\n", frame, dir, (*fi).bar, (*fi).beat, (*fi).frame));
1363                 
1364         switch (type) {
1365         case Bar:
1366                 if (dir < 0) {
1367                         /* find bar previous to 'frame' */
1368
1369                         if (fi == _map.begin()) {
1370                                 return 0;
1371                         }
1372
1373                         if ((*fi).is_bar() && (*fi).frame == frame) {
1374                                 --fi;
1375                         }
1376
1377                         while (!(*fi).is_bar()) {
1378                                 if (fi == _map.begin()) {
1379                                         break;
1380                                 }
1381                                 fi--;
1382                         }
1383                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n", 
1384                                                                      (*fi).bar, (*fi).beat, (*fi).frame));
1385                         return (*fi).frame;
1386
1387                 } else if (dir > 0) {
1388
1389                         /* find bar following 'frame' */
1390
1391                         if ((*fi).is_bar() && (*fi).frame == frame) {
1392                                 ++fi;
1393                         }
1394
1395                         while (!(*fi).is_bar()) {
1396                                 fi++;
1397                                 if (fi == _map.end()) {
1398                                         --fi;
1399                                         break;
1400                                 }
1401                         }
1402
1403                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n", 
1404                                                                      (*fi).bar, (*fi).beat, (*fi).frame));
1405                         return (*fi).frame;
1406
1407                 } else {
1408                         
1409                         /* true rounding: find nearest bar */
1410
1411                         BBTPointList::const_iterator prev = fi;
1412                         BBTPointList::const_iterator next = fi;
1413
1414                         if ((*fi).frame == frame) {
1415                                 return frame;
1416                         }
1417
1418                         while ((*prev).beat != 1) {
1419                                 if (prev == _map.begin()) {
1420                                         break;
1421                                 }
1422                                 prev--;
1423                         }
1424
1425                         while ((next != _map.end()) && (*next).beat != 1) {
1426                                 next++;
1427                         }
1428
1429                         if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1430                                 return (*prev).frame;
1431                         } else {
1432                                 return (*next).frame;
1433                         }
1434                         
1435                 }
1436
1437                 break;
1438
1439         case Beat:
1440                 if (dir < 0) {
1441
1442                         if (fi == _map.begin()) {
1443                                 return 0;
1444                         }
1445
1446                         if ((*fi).frame >= frame) {
1447                                 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1448                                 --fi;
1449                         }
1450                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n", 
1451                                                                      (*fi).bar, (*fi).beat, (*fi).frame));
1452                         return (*fi).frame;
1453                 } else if (dir > 0) {
1454                         if ((*fi).frame <= frame) {
1455                                 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1456                                 ++fi;
1457                         }
1458                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n", 
1459                                                                      (*fi).bar, (*fi).beat, (*fi).frame));
1460                         return (*fi).frame;
1461                 } else {
1462                         /* find beat nearest to frame */
1463                         if ((*fi).frame == frame) {
1464                                 return frame;
1465                         }
1466
1467                         BBTPointList::const_iterator prev = fi;
1468                         BBTPointList::const_iterator next = fi;
1469                         if (prev != _map.begin()) {
1470                                 --prev;
1471                         }
1472                         ++next;
1473                         
1474                         if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1475                                 return (*prev).frame;
1476                         } else {
1477                                 return (*next).frame;
1478                         }
1479                 }
1480                 break;
1481         }
1482
1483         /* NOTREACHED */
1484         assert (false);
1485         return 0;
1486 }
1487
1488 void
1489 TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin, 
1490                     TempoMap::BBTPointList::const_iterator& end, 
1491                     framepos_t lower, framepos_t upper) 
1492 {
1493         { 
1494                 Glib::RWLock::WriterLock lm (lock);
1495                 if (_map.empty() || (_map.back().frame < upper)) {
1496                         recompute_map (false, upper);
1497                 }
1498         }
1499
1500         begin = lower_bound (_map.begin(), _map.end(), lower);
1501         end = upper_bound (_map.begin(), _map.end(), upper);
1502 }
1503
1504 const TempoSection&
1505 TempoMap::tempo_section_at (framepos_t frame) const
1506 {
1507         Glib::RWLock::ReaderLock lm (lock);
1508         Metrics::const_iterator i;
1509         TempoSection* prev = 0;
1510
1511         for (i = metrics.begin(); i != metrics.end(); ++i) {
1512                 TempoSection* t;
1513
1514                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1515
1516                         if ((*i)->frame() > frame) {
1517                                 break;
1518                         }
1519
1520                         prev = t;
1521                 }
1522         }
1523
1524         if (prev == 0) {
1525                 fatal << endmsg;
1526         }
1527
1528         return *prev;
1529 }
1530
1531 const Tempo&
1532 TempoMap::tempo_at (framepos_t frame) const
1533 {
1534         TempoMetric m (metric_at (frame));
1535         return m.tempo();
1536 }
1537
1538
1539 const Meter&
1540 TempoMap::meter_at (framepos_t frame) const
1541 {
1542         TempoMetric m (metric_at (frame));
1543         return m.meter();
1544 }
1545
1546 XMLNode&
1547 TempoMap::get_state ()
1548 {
1549         Metrics::const_iterator i;
1550         XMLNode *root = new XMLNode ("TempoMap");
1551
1552         {
1553                 Glib::RWLock::ReaderLock lm (lock);
1554                 for (i = metrics.begin(); i != metrics.end(); ++i) {
1555                         root->add_child_nocopy ((*i)->get_state());
1556                 }
1557         }
1558
1559         return *root;
1560 }
1561
1562 int
1563 TempoMap::set_state (const XMLNode& node, int /*version*/)
1564 {
1565         {
1566                 Glib::RWLock::WriterLock lm (lock);
1567
1568                 XMLNodeList nlist;
1569                 XMLNodeConstIterator niter;
1570                 Metrics old_metrics (metrics);
1571                 MeterSection* last_meter = 0;
1572
1573                 metrics.clear();
1574
1575                 nlist = node.children();
1576                 
1577                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1578                         XMLNode* child = *niter;
1579
1580                         if (child->name() == TempoSection::xml_state_node_name) {
1581
1582                                 try {
1583                                         TempoSection* ts = new TempoSection (*child);
1584                                         metrics.push_back (ts);
1585
1586                                         if (ts->bar_offset() < 0.0) {
1587                                                 if (last_meter) {
1588                                                         ts->update_bar_offset_from_bbt (*last_meter);
1589                                                 } 
1590                                         }
1591                                 }
1592
1593                                 catch (failed_constructor& err){
1594                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1595                                         metrics = old_metrics;
1596                                         break;
1597                                 }
1598
1599                         } else if (child->name() == MeterSection::xml_state_node_name) {
1600
1601                                 try {
1602                                         MeterSection* ms = new MeterSection (*child);
1603                                         metrics.push_back (ms);
1604                                         last_meter = ms;
1605                                 }
1606
1607                                 catch (failed_constructor& err) {
1608                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1609                                         metrics = old_metrics;
1610                                         break;
1611                                 }
1612                         }
1613                 }
1614
1615                 if (niter == nlist.end()) {
1616                         MetricSectionSorter cmp;
1617                         metrics.sort (cmp);
1618                 }
1619
1620                 recompute_map (true);
1621         }
1622
1623         PropertyChanged (PropertyChange ());
1624
1625         return 0;
1626 }
1627
1628 void
1629 TempoMap::dump (std::ostream& o) const
1630 {
1631         Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1632         const MeterSection* m;
1633         const TempoSection* t;
1634
1635         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1636
1637                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1638                         o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (movable? "
1639                           << t->movable() << ')' << endl;
1640                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1641                         o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1642                           << " (movable? " << m->movable() << ')' << endl;
1643                 }
1644         }
1645 }
1646
1647 int
1648 TempoMap::n_tempos() const
1649 {
1650         Glib::RWLock::ReaderLock lm (lock);
1651         int cnt = 0;
1652
1653         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1654                 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1655                         cnt++;
1656                 }
1657         }
1658
1659         return cnt;
1660 }
1661
1662 int
1663 TempoMap::n_meters() const
1664 {
1665         Glib::RWLock::ReaderLock lm (lock);
1666         int cnt = 0;
1667
1668         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1669                 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1670                         cnt++;
1671                 }
1672         }
1673
1674         return cnt;
1675 }
1676
1677 void
1678 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1679 {
1680         {
1681                 Glib::RWLock::WriterLock lm (lock);
1682                 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1683                         if ((*i)->frame() >= where && (*i)->movable ()) {
1684                                 (*i)->set_frame ((*i)->frame() + amount);
1685                         }
1686                 }
1687
1688                 /* now reset the BBT time of all metrics, based on their new
1689                  * audio time. This is the only place where we do this reverse
1690                  * timestamp.
1691                  */
1692
1693                 Metrics::iterator i;
1694                 const MeterSection* meter;
1695                 const TempoSection* tempo;
1696                 MeterSection *m;
1697                 TempoSection *t;
1698                 
1699                 meter = &first_meter ();
1700                 tempo = &first_tempo ();
1701                 
1702                 BBT_Time start;
1703                 BBT_Time end;
1704                 
1705                 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1706                 
1707                 bool first = true;
1708                 MetricSection* prev = 0;
1709                 
1710                 for (i = metrics.begin(); i != metrics.end(); ++i) {
1711                         
1712                         BBT_Time bbt;
1713                         TempoMetric metric (*meter, *tempo);
1714                         
1715                         if (prev) {
1716                                 metric.set_start (prev->start());
1717                                 metric.set_frame (prev->frame());
1718                         } else {
1719                                 // metric will be at frames=0 bbt=1|1|0 by default
1720                                 // which is correct for our purpose
1721                         }
1722                         
1723                         BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
1724                         bbt_time ((*i)->frame(), bbt, bi);
1725                         
1726                         // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1727                         
1728                         if (first) {
1729                                 first = false;
1730                         } else {
1731                                 
1732                                 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
1733                                         /* round up to next beat */
1734                                         bbt.beats += 1;
1735                                 }
1736                                 
1737                                 bbt.ticks = 0;
1738                                 
1739                                 if (bbt.beats != 1) {
1740                                         /* round up to next bar */
1741                                         bbt.bars += 1;
1742                                         bbt.beats = 1;
1743                                 }
1744                         }
1745                         
1746                         // cerr << bbt << endl;
1747                         
1748                         (*i)->set_start (bbt);
1749                         
1750                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1751                                 tempo = t;
1752                                 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1753                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1754                                 meter = m;
1755                                 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1756                         } else {
1757                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1758                                 /*NOTREACHED*/
1759                         }
1760                         
1761                         prev = (*i);
1762                 }
1763                 
1764                 recompute_map (true);
1765         }
1766
1767
1768         PropertyChanged (PropertyChange ());
1769 }
1770
1771 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1772  *  pos can be -ve, if required.
1773  */
1774 framepos_t
1775 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1776 {
1777         Glib::RWLock::ReaderLock lm (lock);
1778         Metrics::const_iterator next_tempo;
1779         const TempoSection* tempo;
1780
1781         /* Find the starting tempo metric */
1782
1783         for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
1784
1785                 const TempoSection* t;
1786
1787                 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
1788
1789                         /* This is a bit of a hack, but pos could be -ve, and if it is,
1790                            we consider the initial metric changes (at time 0) to actually
1791                            be in effect at pos.
1792                         */
1793
1794                         framepos_t f = (*next_tempo)->frame ();
1795
1796                         if (pos < 0 && f == 0) {
1797                                 f = pos;
1798                         }
1799                         
1800                         if (f > pos) {
1801                                 break;
1802                         }
1803                         
1804                         tempo = t;
1805                 }
1806         }
1807
1808         /* We now have:
1809
1810            tempo       -> the Tempo for "pos"
1811            next_tempo  -> first tempo after "pos", possibly metrics.end()
1812         */
1813
1814         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
1815                                                        pos, beats, *((Tempo*)tempo), tempo->frame()));
1816
1817         while (beats) {
1818
1819                 /* Distance to the end of this section in frames */
1820                 framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
1821
1822                 /* Distance to the end in beats */
1823                 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1824
1825                 /* Amount to subtract this time */
1826                 double const delta = min (distance_beats, beats);
1827
1828                 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1829                                                                (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
1830                                                                distance_frames, distance_beats));
1831
1832                 /* Update */
1833                 beats -= delta;
1834                 pos += delta * tempo->frames_per_beat (_frame_rate);
1835
1836                 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
1837
1838                 /* step forwards to next tempo section */
1839
1840                 if (next_tempo != metrics.end()) {
1841
1842                         tempo = dynamic_cast<const TempoSection*>(*next_tempo);
1843
1844                         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1845                                                                        *((Tempo*)tempo), tempo->frame(),
1846                                                                        tempo->frames_per_beat (_frame_rate)));
1847
1848                         while (next_tempo != metrics.end ()) {
1849
1850                                 ++next_tempo;
1851                                 
1852                                 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
1853                                         break;
1854                                 }
1855                         }
1856                 }
1857         }
1858
1859         return pos;
1860 }
1861
1862 /** Subtract some (fractional) beats to a frame position, and return the result in frames */
1863 framepos_t
1864 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1865 {
1866         Glib::RWLock::ReaderLock lm (lock);
1867         Metrics::const_reverse_iterator prev_tempo;
1868         const TempoSection* tempo = 0;
1869
1870         /* Find the starting tempo metric */
1871
1872         for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
1873
1874                 const TempoSection* t;
1875
1876                 if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
1877
1878                         /* This is a bit of a hack, but pos could be -ve, and if it is,
1879                            we consider the initial metric changes (at time 0) to actually
1880                            be in effect at pos.
1881                         */
1882
1883                         framepos_t f = (*prev_tempo)->frame ();
1884
1885                         if (pos < 0 && f == 0) {
1886                                 f = pos;
1887                         }
1888
1889                         /* this is slightly more complex than the forward case
1890                            because we reach the tempo in effect at pos after
1891                            passing through pos (rather before, as in the
1892                            forward case). having done that, we then need to
1893                            keep going to get the previous tempo (or
1894                            metrics.rend())
1895                         */
1896                         
1897                         if (f <= pos) {
1898                                 if (tempo == 0) {
1899                                         /* first tempo with position at or
1900                                            before pos
1901                                         */
1902                                         tempo = t;
1903                                 } else if (f < pos) {
1904                                         /* some other tempo section that
1905                                            is even earlier than 'tempo'
1906                                         */
1907                                         break;
1908                                 }
1909                         }
1910                 }
1911         }
1912
1913         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n",
1914                                                        pos, beats, *((Tempo*)tempo), tempo->frame(),
1915                                                        prev_tempo == metrics.rend()));
1916
1917         /* We now have:
1918
1919            tempo       -> the Tempo for "pos"
1920            prev_tempo  -> the first metric before "pos", possibly metrics.rend()
1921         */
1922
1923         while (beats) {
1924                 
1925                 /* Distance to the start of this section in frames */
1926                 framecnt_t distance_frames = (pos - tempo->frame());
1927
1928                 /* Distance to the start in beats */
1929                 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1930
1931                 /* Amount to subtract this time */
1932                 double const sub = min (distance_beats, beats);
1933
1934                 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1935                                                                tempo->frame(), distance_frames, distance_beats));
1936                 /* Update */
1937
1938                 beats -= sub;
1939                 pos -= sub * tempo->frames_per_beat (_frame_rate);
1940
1941                 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
1942                                                                (prev_tempo == metrics.rend())));
1943
1944                 /* step backwards to prior TempoSection */
1945
1946                 if (prev_tempo != metrics.rend()) {
1947
1948                         tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
1949
1950                         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1951                                                                        *((Tempo*)tempo), tempo->frame(),
1952                                                                        tempo->frames_per_beat (_frame_rate)));
1953
1954                         while (prev_tempo != metrics.rend ()) {
1955
1956                                 ++prev_tempo;
1957
1958                                 if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
1959                                         break;
1960                                 }
1961                         }
1962                 } else {
1963                         pos -= llrint (beats * tempo->frames_per_beat (_frame_rate));
1964                         beats = 0;
1965                 }
1966         }
1967
1968         return pos;
1969 }
1970
1971 /** Add the BBT interval op to pos and return the result */
1972 framepos_t
1973 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
1974 {
1975         Glib::RWLock::ReaderLock lm (lock);
1976         Metrics::const_iterator i;
1977         const MeterSection* meter;
1978         const MeterSection* m;
1979         const TempoSection* tempo;
1980         const TempoSection* t;
1981         double frames_per_beat;
1982
1983         meter = &first_meter ();
1984         tempo = &first_tempo ();
1985
1986         assert (meter);
1987         assert (tempo);
1988
1989         /* find the starting metrics for tempo & meter */
1990
1991         for (i = metrics.begin(); i != metrics.end(); ++i) {
1992
1993                 if ((*i)->frame() > pos) {
1994                         break;
1995                 }
1996
1997                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1998                         tempo = t;
1999                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2000                         meter = m;
2001                 }
2002         }
2003
2004         /* We now have:
2005
2006            meter -> the Meter for "pos"
2007            tempo -> the Tempo for "pos"
2008            i     -> for first new metric after "pos", possibly metrics.end()
2009         */
2010
2011         /* now comes the complicated part. we have to add one beat a time,
2012            checking for a new metric on every beat.
2013         */
2014
2015         frames_per_beat = tempo->frames_per_beat (_frame_rate);
2016
2017         uint64_t bars = 0;
2018
2019         while (op.bars) {
2020
2021                 bars++;
2022                 op.bars--;
2023
2024                 /* check if we need to use a new metric section: has adding frames moved us
2025                    to or after the start of the next metric section? in which case, use it.
2026                 */
2027
2028                 if (i != metrics.end()) {
2029                         if ((*i)->frame() <= pos) {
2030
2031                                 /* about to change tempo or meter, so add the
2032                                  * number of frames for the bars we've just
2033                                  * traversed before we change the
2034                                  * frames_per_beat value.
2035                                  */
2036                                 
2037                                 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2038                                 bars = 0;
2039
2040                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2041                                         tempo = t;
2042                                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2043                                         meter = m;
2044                                 }
2045                                 ++i;
2046                                 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2047
2048                         }
2049                 }
2050
2051         }
2052
2053         pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2054
2055         uint64_t beats = 0;
2056
2057         while (op.beats) {
2058
2059                 /* given the current meter, have we gone past the end of the bar ? */
2060
2061                 beats++;
2062                 op.beats--;
2063
2064                 /* check if we need to use a new metric section: has adding frames moved us
2065                    to or after the start of the next metric section? in which case, use it.
2066                 */
2067
2068                 if (i != metrics.end()) {
2069                         if ((*i)->frame() <= pos) {
2070
2071                                 /* about to change tempo or meter, so add the
2072                                  * number of frames for the beats we've just
2073                                  * traversed before we change the
2074                                  * frames_per_beat value.
2075                                  */
2076
2077                                 pos += llrint (beats * frames_per_beat);
2078                                 beats = 0;
2079
2080                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2081                                         tempo = t;
2082                                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2083                                         meter = m;
2084                                 }
2085                                 ++i;
2086                                 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2087                         }
2088                 }
2089         }
2090
2091         pos += llrint (beats * frames_per_beat);
2092
2093         if (op.ticks) {
2094                 if (op.ticks >= BBT_Time::ticks_per_beat) {
2095                         pos += llrint (frames_per_beat + /* extra beat */
2096                                        (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) / 
2097                                                            (double) BBT_Time::ticks_per_beat)));
2098                 } else {
2099                         pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2100                 }
2101         }
2102
2103         return pos;
2104 }
2105
2106 /** Count the number of beats that are equivalent to distance when going forward,
2107     starting at pos.
2108 */
2109 Evoral::MusicalTime
2110 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2111 {
2112         Glib::RWLock::ReaderLock lm (lock);
2113         Metrics::const_iterator next_tempo;
2114         const TempoSection* tempo;
2115         
2116         /* Find the relevant initial tempo metric  */
2117
2118         for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
2119
2120                 const TempoSection* t;
2121
2122                 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
2123
2124                         if ((*next_tempo)->frame() > pos) {
2125                                 break;
2126                         }
2127
2128                         tempo = t;
2129                 }
2130         }
2131
2132         /* We now have:
2133
2134            tempo -> the Tempo for "pos"
2135            next_tempo -> the next tempo after "pos", possibly metrics.end()
2136         */
2137
2138         Evoral::MusicalTime beats = 0;
2139
2140         while (distance) {
2141
2142                 /* End of this section */
2143                 framepos_t const end = ((next_tempo == metrics.end()) ? max_framepos : (*next_tempo)->frame ());
2144
2145                 /* Distance to the end in frames */
2146                 framecnt_t const distance_to_end = end - pos;
2147
2148                 /* Amount to subtract this time */
2149                 double const sub = min (distance, distance_to_end);
2150
2151                 /* Update */
2152                 pos += sub;
2153                 distance -= sub;
2154                 beats += sub / tempo->frames_per_beat (_frame_rate);
2155                 
2156                 /* Move on if there's anything to move to */
2157
2158                 if (next_tempo != metrics.end()) {
2159
2160                         tempo = dynamic_cast<const TempoSection*>(*next_tempo);
2161
2162                         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2163                                                                        *((Tempo*)tempo), tempo->frame(),
2164                                                                        tempo->frames_per_beat (_frame_rate)));
2165
2166                         while (next_tempo != metrics.end ()) {
2167
2168                                 ++next_tempo;
2169                                 
2170                                 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
2171                                         break;
2172                                 }
2173                         }
2174                 }
2175         }
2176
2177         return beats;
2178 }
2179
2180 TempoMap::BBTPointList::const_iterator
2181 TempoMap::bbt_before_or_at (framepos_t pos)
2182 {
2183         /* CALLER MUST HOLD READ LOCK */
2184
2185         BBTPointList::const_iterator i;
2186
2187         i = lower_bound (_map.begin(), _map.end(), pos);
2188         assert (i != _map.end());
2189         if ((*i).frame > pos) {
2190                 assert (i != _map.begin());
2191                 --i;
2192         }
2193         return i;
2194 }
2195
2196 struct bbtcmp {
2197     bool operator() (const BBT_Time& a, const BBT_Time& b) {
2198             return a < b;
2199     }
2200 };
2201
2202 TempoMap::BBTPointList::const_iterator
2203 TempoMap::bbt_before_or_at (const BBT_Time& bbt)
2204 {
2205         BBTPointList::const_iterator i;
2206         bbtcmp cmp;
2207
2208         i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
2209         assert (i != _map.end());
2210         if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
2211                 assert (i != _map.begin());
2212                 --i;
2213         }
2214         return i;
2215 }
2216
2217 TempoMap::BBTPointList::const_iterator
2218 TempoMap::bbt_after_or_at (framepos_t pos) 
2219 {
2220         /* CALLER MUST HOLD READ LOCK */
2221
2222         BBTPointList::const_iterator i;
2223
2224         if (_map.back().frame == pos) {
2225                 i = _map.end();
2226                 assert (i != _map.begin());
2227                 --i;
2228                 return i;
2229         }
2230
2231         i = upper_bound (_map.begin(), _map.end(), pos);
2232         assert (i != _map.end());
2233         return i;
2234 }
2235
2236 /** Compare the time of this with that of another MetricSection.
2237  *  @param with_bbt True to compare using start(), false to use frame().
2238  *  @return -1 for less than, 0 for equal, 1 for greater than.
2239  */
2240
2241 int
2242 MetricSection::compare (const MetricSection& other) const
2243 {
2244         if (start() == other.start()) {
2245                 return 0;
2246         } else if (start() < other.start()) {
2247                 return -1;
2248         } else {
2249                 return 1;
2250         }
2251
2252         /* NOTREACHED */
2253         return 0;
2254 }
2255
2256 bool
2257 MetricSection::operator== (const MetricSection& other) const
2258 {
2259         return compare (other) == 0;
2260 }
2261
2262 bool
2263 MetricSection::operator!= (const MetricSection& other) const
2264 {
2265         return compare (other) != 0;
2266 }
2267
2268 std::ostream& 
2269 operator<< (std::ostream& o, const Meter& m) {
2270         return o << m.divisions_per_bar() << '/' << m.note_divisor();
2271 }
2272
2273 std::ostream& 
2274 operator<< (std::ostream& o, const Tempo& t) {
2275         return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2276 }
2277
2278 std::ostream& 
2279 operator<< (std::ostream& o, const MetricSection& section) {
2280
2281         o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2282
2283         const TempoSection* ts;
2284         const MeterSection* ms;
2285
2286         if ((ts = dynamic_cast<const TempoSection*> (&section)) != 0) {
2287                 o << *((Tempo*) ts);
2288         } else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
2289                 o << *((Meter*) ms);
2290         }
2291
2292         return o;
2293 }