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