fix bug with tempo computation where passed in positions or offsets are negative
[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 = 0; /* (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
1110         if (frame < 0) {
1111                 bbt.bars = 1;
1112                 bbt.beats = 1;
1113                 bbt.ticks = 0;
1114                 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1115                 return;
1116         }
1117
1118         return bbt_time (frame, bbt, bbt_before_or_at (frame));
1119 }
1120
1121 void
1122 TempoMap::bbt_time_rt (framepos_t frame, BBT_Time& bbt)
1123 {
1124         Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1125
1126         if (!lm.locked()) {
1127                 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1128         }
1129         
1130         if (_map.empty() || _map.back().frame < frame) {
1131                 throw std::logic_error (string_compose ("map not long enough to reach %1", frame));
1132         }
1133
1134         return bbt_time (frame, bbt, bbt_before_or_at (frame));
1135 }
1136
1137 void
1138 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt, const BBTPointList::const_iterator& i)
1139 {
1140         /* CALLER MUST HOLD READ LOCK */
1141
1142         bbt.bars = (*i).bar;
1143         bbt.beats = (*i).beat;
1144
1145         if ((*i).frame == frame) {
1146                 bbt.ticks = 0;
1147         } else {
1148                 bbt.ticks = llrint (((frame - (*i).frame) / (*i).tempo->frames_per_beat(_frame_rate)) *
1149                                     BBT_Time::ticks_per_beat);
1150         }
1151 }
1152
1153 framepos_t
1154 TempoMap::frame_time (const BBT_Time& bbt)
1155 {
1156         if (bbt.bars < 1) {
1157                 warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
1158                 return 0;
1159         }
1160         
1161         if (bbt.beats < 1) {
1162                 throw std::logic_error ("beats are counted from one");
1163         }
1164
1165         require_map_to (bbt);
1166
1167         Glib::RWLock::ReaderLock lm (lock);
1168
1169         BBTPointList::const_iterator s = bbt_before_or_at (BBT_Time (1, 1, 0));
1170         BBTPointList::const_iterator e = bbt_before_or_at (BBT_Time (bbt.bars, bbt.beats, 0));
1171
1172         if (bbt.ticks != 0) {
1173                 return ((*e).frame - (*s).frame) + 
1174                         llrint ((*e).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat));
1175         } else {
1176                 return ((*e).frame - (*s).frame);
1177         }
1178 }
1179
1180 framecnt_t
1181 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1182 {
1183         Glib::RWLock::ReaderLock lm (lock);
1184         framecnt_t frames = 0;
1185         BBT_Time when;
1186
1187         bbt_time (pos, when);
1188         frames = bbt_duration_at_unlocked (when, bbt,dir);
1189
1190         return frames;
1191 }
1192
1193 framecnt_t
1194 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) 
1195 {
1196         if (bbt.bars == 0 && bbt.beats == 0 && bbt.ticks == 0) {
1197                 return 0;
1198         }
1199
1200         /* round back to the previous precise beat */
1201         BBTPointList::const_iterator wi = bbt_before_or_at (BBT_Time (when.bars, when.beats, 0));
1202         BBTPointList::const_iterator start (wi);
1203         double tick_frames = 0;
1204
1205         assert (wi != _map.end());
1206
1207         /* compute how much rounding we did because of non-zero ticks */
1208
1209         if (when.ticks != 0) {
1210                 tick_frames = (*wi).tempo->frames_per_beat (_frame_rate) * (when.ticks/BBT_Time::ticks_per_beat);
1211         }
1212         
1213         uint32_t bars = 0;
1214         uint32_t beats = 0;
1215
1216         while (wi != _map.end() && bars < bbt.bars) {
1217                 ++wi;
1218                 if ((*wi).is_bar()) {
1219                         ++bars;
1220                 }
1221         }
1222         assert (wi != _map.end());
1223
1224         while (wi != _map.end() && beats < bbt.beats) {
1225                 ++wi;
1226                 ++beats;
1227         }
1228         assert (wi != _map.end());
1229
1230         /* add any additional frames related to ticks in the added value */
1231
1232         if (bbt.ticks != 0) {
1233                 tick_frames += (*wi).tempo->frames_per_beat (_frame_rate) * (bbt.ticks/BBT_Time::ticks_per_beat);
1234         }
1235
1236         return ((*wi).frame - (*start).frame) + llrint (tick_frames);
1237 }
1238
1239 framepos_t
1240 TempoMap::round_to_bar (framepos_t fr, int dir)
1241 {
1242         return round_to_type (fr, dir, Bar);
1243 }
1244
1245 framepos_t
1246 TempoMap::round_to_beat (framepos_t fr, int dir)
1247 {
1248         return round_to_type (fr, dir, Beat);
1249 }
1250
1251 framepos_t
1252 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, int dir)
1253 {
1254         require_map_to (fr);
1255
1256         Glib::RWLock::ReaderLock lm (lock);
1257         BBTPointList::const_iterator i = bbt_before_or_at (fr);
1258         BBT_Time the_beat;
1259         uint32_t ticks_one_subdivisions_worth;
1260         uint32_t difference;
1261
1262         bbt_time (fr, the_beat, i);
1263
1264         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("round %1 to nearest 1/%2 beat, before-or-at = %3 @ %4|%5 precise = %6\n",
1265                                                      fr, sub_num, (*i).frame, (*i).bar, (*i).beat, the_beat));
1266
1267         ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1268
1269         if (dir > 0) {
1270
1271                 /* round to next (even if we're on a subdivision */
1272
1273                 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1274
1275                 if (mod == 0) {
1276                         /* right on the subdivision, so the difference is just the subdivision ticks */
1277                         the_beat.ticks += ticks_one_subdivisions_worth;
1278
1279                 } else {
1280                         /* not on subdivision, compute distance to next subdivision */
1281
1282                         the_beat.ticks += ticks_one_subdivisions_worth - mod;
1283                 }
1284
1285                 if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1286                         assert (i != _map.end());
1287                         ++i;
1288                         assert (i != _map.end());
1289                         the_beat.ticks -= BBT_Time::ticks_per_beat;
1290                 } 
1291
1292
1293         } else if (dir < 0) {
1294
1295                 /* round to previous (even if we're on a subdivision) */
1296
1297                 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1298
1299                 if (mod == 0) {
1300                         /* right on the subdivision, so the difference is just the subdivision ticks */
1301                         difference = ticks_one_subdivisions_worth;
1302                 } else {
1303                         /* not on subdivision, compute distance to previous subdivision, which
1304                            is just the modulus.
1305                         */
1306
1307                         difference = mod;
1308                 }
1309
1310                 if (the_beat.ticks < difference) {
1311                         if (i == _map.begin()) {
1312                                 /* can't go backwards from wherever pos is, so just return it */
1313                                 return fr;
1314                         }
1315                         --i;
1316                         the_beat.ticks = BBT_Time::ticks_per_beat - the_beat.ticks;
1317                 } else {
1318                         the_beat.ticks -= difference;
1319                 }
1320
1321         } else {
1322                 /* round to nearest */
1323
1324                 double rem;
1325
1326                 /* compute the distance to the previous and next subdivision */
1327                 
1328                 if ((rem = fmod ((double) the_beat.ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1329                         
1330                         /* closer to the next subdivision, so shift forward */
1331
1332                         the_beat.ticks = lrint (the_beat.ticks + (ticks_one_subdivisions_worth - rem));
1333
1334                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", the_beat.ticks));
1335
1336                         if (the_beat.ticks > BBT_Time::ticks_per_beat) {
1337                                 assert (i != _map.end());
1338                                 ++i;
1339                                 assert (i != _map.end());
1340                                 the_beat.ticks -= BBT_Time::ticks_per_beat;
1341                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", the_beat));
1342                         } 
1343
1344                 } else if (rem > 0) {
1345                         
1346                         /* closer to previous subdivision, so shift backward */
1347
1348                         if (rem > the_beat.ticks) {
1349                                 if (i == _map.begin()) {
1350                                         /* can't go backwards past zero, so ... */
1351                                         return 0;
1352                                 }
1353                                 /* step back to previous beat */
1354                                 --i;
1355                                 the_beat.ticks = lrint (BBT_Time::ticks_per_beat - rem);
1356                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", the_beat));
1357                         } else {
1358                                 the_beat.ticks = lrint (the_beat.ticks - rem);
1359                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", the_beat.ticks));
1360                         }
1361                 } else {
1362                         /* on the subdivision, do nothing */
1363                 }
1364         }
1365
1366         return (*i).frame + (the_beat.ticks/BBT_Time::ticks_per_beat) * 
1367                 (*i).tempo->frames_per_beat (_frame_rate);
1368 }
1369
1370 framepos_t
1371 TempoMap::round_to_type (framepos_t frame, int dir, BBTPointType type)
1372 {
1373         require_map_to (frame);
1374
1375         Glib::RWLock::ReaderLock lm (lock);
1376         BBTPointList::const_iterator fi;
1377
1378         if (dir > 0) {
1379                 fi = bbt_after_or_at (frame);
1380         } else {
1381                 fi = bbt_before_or_at (frame);
1382         }
1383
1384         assert (fi != _map.end());
1385
1386         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,
1387                                                      (type == Bar ? "bar" : "beat")));
1388                 
1389         switch (type) {
1390         case Bar:
1391                 if (dir < 0) {
1392                         /* find bar previous to 'frame' */
1393
1394                         if (fi == _map.begin()) {
1395                                 return 0;
1396                         }
1397
1398                         if ((*fi).is_bar() && (*fi).frame == frame) {
1399                                 --fi;
1400                         }
1401
1402                         while (!(*fi).is_bar()) {
1403                                 if (fi == _map.begin()) {
1404                                         break;
1405                                 }
1406                                 fi--;
1407                         }
1408                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n", 
1409                                                                      (*fi).bar, (*fi).beat, (*fi).frame));
1410                         return (*fi).frame;
1411
1412                 } else if (dir > 0) {
1413
1414                         /* find bar following 'frame' */
1415
1416                         if ((*fi).is_bar() && (*fi).frame == frame) {
1417                                 ++fi;
1418                         }
1419
1420                         while (!(*fi).is_bar()) {
1421                                 fi++;
1422                                 if (fi == _map.end()) {
1423                                         --fi;
1424                                         break;
1425                                 }
1426                         }
1427
1428                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to bar: map iter at %1|%2 %3, return\n", 
1429                                                                      (*fi).bar, (*fi).beat, (*fi).frame));
1430                         return (*fi).frame;
1431
1432                 } else {
1433                         
1434                         /* true rounding: find nearest bar */
1435
1436                         BBTPointList::const_iterator prev = fi;
1437                         BBTPointList::const_iterator next = fi;
1438
1439                         if ((*fi).frame == frame) {
1440                                 return frame;
1441                         }
1442
1443                         while ((*prev).beat != 1) {
1444                                 if (prev == _map.begin()) {
1445                                         break;
1446                                 }
1447                                 prev--;
1448                         }
1449
1450                         while ((next != _map.end()) && (*next).beat != 1) {
1451                                 next++;
1452                         }
1453
1454                         if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1455                                 return (*prev).frame;
1456                         } else {
1457                                 return (*next).frame;
1458                         }
1459                         
1460                 }
1461
1462                 break;
1463
1464         case Beat:
1465                 if (dir < 0) {
1466
1467                         if (fi == _map.begin()) {
1468                                 return 0;
1469                         }
1470
1471                         if ((*fi).frame >= frame) {
1472                                 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step back\n");
1473                                 --fi;
1474                         }
1475                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n", 
1476                                                                      (*fi).bar, (*fi).beat, (*fi).frame));
1477                         return (*fi).frame;
1478                 } else if (dir > 0) {
1479                         if ((*fi).frame <= frame) {
1480                                 DEBUG_TRACE (DEBUG::SnapBBT, "requested frame is on beat, step forward\n");
1481                                 ++fi;
1482                         }
1483                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("rounded to beat: map iter at %1|%2 %3, return\n", 
1484                                                                      (*fi).bar, (*fi).beat, (*fi).frame));
1485                         return (*fi).frame;
1486                 } else {
1487                         /* find beat nearest to frame */
1488                         if ((*fi).frame == frame) {
1489                                 return frame;
1490                         }
1491
1492                         BBTPointList::const_iterator prev = fi;
1493                         BBTPointList::const_iterator next = fi;
1494
1495                         /* fi is already the beat before_or_at frame, and
1496                            we've just established that its not at frame, so its
1497                            the beat before frame.
1498                         */
1499                         ++next;
1500                         
1501                         if ((next == _map.end()) || (frame - (*prev).frame) < ((*next).frame - frame)) {
1502                                 return (*prev).frame;
1503                         } else {
1504                                 return (*next).frame;
1505                         }
1506                 }
1507                 break;
1508         }
1509
1510         /* NOTREACHED */
1511         assert (false);
1512         return 0;
1513 }
1514
1515 void
1516 TempoMap::get_grid (TempoMap::BBTPointList::const_iterator& begin, 
1517                     TempoMap::BBTPointList::const_iterator& end, 
1518                     framepos_t lower, framepos_t upper) 
1519 {
1520         { 
1521                 Glib::RWLock::WriterLock lm (lock);
1522                 if (_map.empty() || (_map.back().frame < upper)) {
1523                         recompute_map (false, upper);
1524                 }
1525         }
1526
1527         begin = lower_bound (_map.begin(), _map.end(), lower);
1528         end = upper_bound (_map.begin(), _map.end(), upper);
1529 }
1530
1531 const TempoSection&
1532 TempoMap::tempo_section_at (framepos_t frame) const
1533 {
1534         Glib::RWLock::ReaderLock lm (lock);
1535         Metrics::const_iterator i;
1536         TempoSection* prev = 0;
1537
1538         for (i = metrics.begin(); i != metrics.end(); ++i) {
1539                 TempoSection* t;
1540
1541                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1542
1543                         if ((*i)->frame() > frame) {
1544                                 break;
1545                         }
1546
1547                         prev = t;
1548                 }
1549         }
1550
1551         if (prev == 0) {
1552                 fatal << endmsg;
1553         }
1554
1555         return *prev;
1556 }
1557
1558 const Tempo&
1559 TempoMap::tempo_at (framepos_t frame) const
1560 {
1561         TempoMetric m (metric_at (frame));
1562         return m.tempo();
1563 }
1564
1565
1566 const Meter&
1567 TempoMap::meter_at (framepos_t frame) const
1568 {
1569         TempoMetric m (metric_at (frame));
1570         return m.meter();
1571 }
1572
1573 XMLNode&
1574 TempoMap::get_state ()
1575 {
1576         Metrics::const_iterator i;
1577         XMLNode *root = new XMLNode ("TempoMap");
1578
1579         {
1580                 Glib::RWLock::ReaderLock lm (lock);
1581                 for (i = metrics.begin(); i != metrics.end(); ++i) {
1582                         root->add_child_nocopy ((*i)->get_state());
1583                 }
1584         }
1585
1586         return *root;
1587 }
1588
1589 int
1590 TempoMap::set_state (const XMLNode& node, int /*version*/)
1591 {
1592         {
1593                 Glib::RWLock::WriterLock lm (lock);
1594
1595                 XMLNodeList nlist;
1596                 XMLNodeConstIterator niter;
1597                 Metrics old_metrics (metrics);
1598                 MeterSection* last_meter = 0;
1599
1600                 metrics.clear();
1601
1602                 nlist = node.children();
1603                 
1604                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1605                         XMLNode* child = *niter;
1606
1607                         if (child->name() == TempoSection::xml_state_node_name) {
1608
1609                                 try {
1610                                         TempoSection* ts = new TempoSection (*child);
1611                                         metrics.push_back (ts);
1612
1613                                         if (ts->bar_offset() < 0.0) {
1614                                                 if (last_meter) {
1615                                                         ts->update_bar_offset_from_bbt (*last_meter);
1616                                                 } 
1617                                         }
1618                                 }
1619
1620                                 catch (failed_constructor& err){
1621                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1622                                         metrics = old_metrics;
1623                                         break;
1624                                 }
1625
1626                         } else if (child->name() == MeterSection::xml_state_node_name) {
1627
1628                                 try {
1629                                         MeterSection* ms = new MeterSection (*child);
1630                                         metrics.push_back (ms);
1631                                         last_meter = ms;
1632                                 }
1633
1634                                 catch (failed_constructor& err) {
1635                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1636                                         metrics = old_metrics;
1637                                         break;
1638                                 }
1639                         }
1640                 }
1641
1642                 if (niter == nlist.end()) {
1643                         MetricSectionSorter cmp;
1644                         metrics.sort (cmp);
1645                 }
1646
1647                 recompute_map (true);
1648         }
1649
1650         PropertyChanged (PropertyChange ());
1651
1652         return 0;
1653 }
1654
1655 void
1656 TempoMap::dump (std::ostream& o) const
1657 {
1658         Glib::RWLock::ReaderLock lm (lock, Glib::TRY_LOCK);
1659         const MeterSection* m;
1660         const TempoSection* t;
1661
1662         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1663
1664                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1665                         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? "
1666                           << t->movable() << ')' << endl;
1667                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1668                         o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1669                           << " (movable? " << m->movable() << ')' << endl;
1670                 }
1671         }
1672 }
1673
1674 int
1675 TempoMap::n_tempos() const
1676 {
1677         Glib::RWLock::ReaderLock lm (lock);
1678         int cnt = 0;
1679
1680         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1681                 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1682                         cnt++;
1683                 }
1684         }
1685
1686         return cnt;
1687 }
1688
1689 int
1690 TempoMap::n_meters() const
1691 {
1692         Glib::RWLock::ReaderLock lm (lock);
1693         int cnt = 0;
1694
1695         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1696                 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1697                         cnt++;
1698                 }
1699         }
1700
1701         return cnt;
1702 }
1703
1704 void
1705 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1706 {
1707         {
1708                 Glib::RWLock::WriterLock lm (lock);
1709                 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1710                         if ((*i)->frame() >= where && (*i)->movable ()) {
1711                                 (*i)->set_frame ((*i)->frame() + amount);
1712                         }
1713                 }
1714
1715                 /* now reset the BBT time of all metrics, based on their new
1716                  * audio time. This is the only place where we do this reverse
1717                  * timestamp.
1718                  */
1719
1720                 Metrics::iterator i;
1721                 const MeterSection* meter;
1722                 const TempoSection* tempo;
1723                 MeterSection *m;
1724                 TempoSection *t;
1725                 
1726                 meter = &first_meter ();
1727                 tempo = &first_tempo ();
1728                 
1729                 BBT_Time start;
1730                 BBT_Time end;
1731                 
1732                 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1733                 
1734                 bool first = true;
1735                 MetricSection* prev = 0;
1736                 
1737                 for (i = metrics.begin(); i != metrics.end(); ++i) {
1738                         
1739                         BBT_Time bbt;
1740                         TempoMetric metric (*meter, *tempo);
1741                         
1742                         if (prev) {
1743                                 metric.set_start (prev->start());
1744                                 metric.set_frame (prev->frame());
1745                         } else {
1746                                 // metric will be at frames=0 bbt=1|1|0 by default
1747                                 // which is correct for our purpose
1748                         }
1749                         
1750                         BBTPointList::const_iterator bi = bbt_before_or_at ((*i)->frame());
1751                         bbt_time ((*i)->frame(), bbt, bi);
1752                         
1753                         // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1754                         
1755                         if (first) {
1756                                 first = false;
1757                         } else {
1758                                 
1759                                 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
1760                                         /* round up to next beat */
1761                                         bbt.beats += 1;
1762                                 }
1763                                 
1764                                 bbt.ticks = 0;
1765                                 
1766                                 if (bbt.beats != 1) {
1767                                         /* round up to next bar */
1768                                         bbt.bars += 1;
1769                                         bbt.beats = 1;
1770                                 }
1771                         }
1772                         
1773                         // cerr << bbt << endl;
1774                         
1775                         (*i)->set_start (bbt);
1776                         
1777                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1778                                 tempo = t;
1779                                 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1780                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1781                                 meter = m;
1782                                 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1783                         } else {
1784                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1785                                 /*NOTREACHED*/
1786                         }
1787                         
1788                         prev = (*i);
1789                 }
1790                 
1791                 recompute_map (true);
1792         }
1793
1794
1795         PropertyChanged (PropertyChange ());
1796 }
1797
1798 /** Add some (fractional) beats to a session frame position, and return the result in frames.
1799  *  pos can be -ve, if required.
1800  */
1801 framepos_t
1802 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1803 {
1804         Glib::RWLock::ReaderLock lm (lock);
1805         Metrics::const_iterator next_tempo;
1806         const TempoSection* tempo;
1807
1808         /* Find the starting tempo metric */
1809
1810         for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
1811
1812                 const TempoSection* t;
1813
1814                 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
1815
1816                         /* This is a bit of a hack, but pos could be -ve, and if it is,
1817                            we consider the initial metric changes (at time 0) to actually
1818                            be in effect at pos.
1819                         */
1820
1821                         framepos_t f = (*next_tempo)->frame ();
1822
1823                         if (pos < 0 && f == 0) {
1824                                 f = pos;
1825                         }
1826                         
1827                         if (f > pos) {
1828                                 break;
1829                         }
1830                         
1831                         tempo = t;
1832                 }
1833         }
1834
1835         /* We now have:
1836
1837            tempo       -> the Tempo for "pos"
1838            next_tempo  -> first tempo after "pos", possibly metrics.end()
1839         */
1840
1841         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 plus %2 beats, start with tempo = %3 @ %4\n",
1842                                                        pos, beats, *((Tempo*)tempo), tempo->frame()));
1843
1844         while (beats) {
1845
1846                 /* Distance to the end of this section in frames */
1847                 framecnt_t distance_frames = (next_tempo == metrics.end() ? max_framepos : ((*next_tempo)->frame() - pos));
1848
1849                 /* Distance to the end in beats */
1850                 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1851
1852                 /* Amount to subtract this time */
1853                 double const delta = min (distance_beats, beats);
1854
1855                 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1856                                                                (next_tempo == metrics.end() ? max_framepos : (*next_tempo)->frame()),
1857                                                                distance_frames, distance_beats));
1858
1859                 /* Update */
1860                 beats -= delta;
1861                 pos += delta * tempo->frames_per_beat (_frame_rate);
1862
1863                 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left\n", pos, beats));
1864
1865                 /* step forwards to next tempo section */
1866
1867                 if (next_tempo != metrics.end()) {
1868
1869                         tempo = dynamic_cast<const TempoSection*>(*next_tempo);
1870
1871                         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1872                                                                        *((Tempo*)tempo), tempo->frame(),
1873                                                                        tempo->frames_per_beat (_frame_rate)));
1874
1875                         while (next_tempo != metrics.end ()) {
1876
1877                                 ++next_tempo;
1878                                 
1879                                 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
1880                                         break;
1881                                 }
1882                         }
1883                 }
1884         }
1885
1886         return pos;
1887 }
1888
1889 /** Subtract some (fractional) beats to a frame position, and return the result in frames */
1890 framepos_t
1891 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::MusicalTime beats) const
1892 {
1893         Glib::RWLock::ReaderLock lm (lock);
1894         Metrics::const_reverse_iterator prev_tempo;
1895         const TempoSection* tempo = 0;
1896
1897         /* Find the starting tempo metric */
1898
1899         for (prev_tempo = metrics.rbegin(); prev_tempo != metrics.rend(); ++prev_tempo) {
1900
1901                 const TempoSection* t;
1902
1903                 if ((t = dynamic_cast<const TempoSection*>(*prev_tempo)) != 0) {
1904
1905                         /* This is a bit of a hack, but pos could be -ve, and if it is,
1906                            we consider the initial metric changes (at time 0) to actually
1907                            be in effect at pos.
1908                         */
1909
1910                         framepos_t f = (*prev_tempo)->frame ();
1911
1912                         if (pos < 0 && f == 0) {
1913                                 f = pos;
1914                         }
1915
1916                         /* this is slightly more complex than the forward case
1917                            because we reach the tempo in effect at pos after
1918                            passing through pos (rather before, as in the
1919                            forward case). having done that, we then need to
1920                            keep going to get the previous tempo (or
1921                            metrics.rend())
1922                         */
1923                         
1924                         if (f <= pos) {
1925                                 if (tempo == 0) {
1926                                         /* first tempo with position at or
1927                                            before pos
1928                                         */
1929                                         tempo = t;
1930                                 } else if (f < pos) {
1931                                         /* some other tempo section that
1932                                            is even earlier than 'tempo'
1933                                         */
1934                                         break;
1935                                 }
1936                         }
1937                 }
1938         }
1939
1940         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("frame %1 minus %2 beats, start with tempo = %3 @ %4 prev at beg? %5\n",
1941                                                        pos, beats, *((Tempo*)tempo), tempo->frame(),
1942                                                        prev_tempo == metrics.rend()));
1943
1944         /* We now have:
1945
1946            tempo       -> the Tempo for "pos"
1947            prev_tempo  -> the first metric before "pos", possibly metrics.rend()
1948         */
1949
1950         while (beats) {
1951                 
1952                 /* Distance to the start of this section in frames */
1953                 framecnt_t distance_frames = (pos - tempo->frame());
1954
1955                 /* Distance to the start in beats */
1956                 Evoral::MusicalTime distance_beats = distance_frames / tempo->frames_per_beat (_frame_rate);
1957
1958                 /* Amount to subtract this time */
1959                 double const sub = min (distance_beats, beats);
1960
1961                 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tdistance to %1 = %2 (%3 beats)\n",
1962                                                                tempo->frame(), distance_frames, distance_beats));
1963                 /* Update */
1964
1965                 beats -= sub;
1966                 pos -= sub * tempo->frames_per_beat (_frame_rate);
1967
1968                 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnow at %1, %2 beats left, prev at end ? %3\n", pos, beats,
1969                                                                (prev_tempo == metrics.rend())));
1970
1971                 /* step backwards to prior TempoSection */
1972
1973                 if (prev_tempo != metrics.rend()) {
1974
1975                         tempo = dynamic_cast<const TempoSection*>(*prev_tempo);
1976
1977                         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
1978                                                                        *((Tempo*)tempo), tempo->frame(),
1979                                                                        tempo->frames_per_beat (_frame_rate)));
1980
1981                         while (prev_tempo != metrics.rend ()) {
1982
1983                                 ++prev_tempo;
1984
1985                                 if (prev_tempo != metrics.rend() && dynamic_cast<const TempoSection*>(*prev_tempo) != 0) {
1986                                         break;
1987                                 }
1988                         }
1989                 } else {
1990                         pos -= llrint (beats * tempo->frames_per_beat (_frame_rate));
1991                         beats = 0;
1992                 }
1993         }
1994
1995         return pos;
1996 }
1997
1998 /** Add the BBT interval op to pos and return the result */
1999 framepos_t
2000 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2001 {
2002         Glib::RWLock::ReaderLock lm (lock);
2003         Metrics::const_iterator i;
2004         const MeterSection* meter;
2005         const MeterSection* m;
2006         const TempoSection* tempo;
2007         const TempoSection* t;
2008         double frames_per_beat;
2009         framepos_t effective_pos = max (pos, (framepos_t) 0);
2010
2011         meter = &first_meter ();
2012         tempo = &first_tempo ();
2013
2014         assert (meter);
2015         assert (tempo);
2016
2017         /* find the starting metrics for tempo & meter */
2018
2019         for (i = metrics.begin(); i != metrics.end(); ++i) {
2020
2021                 if ((*i)->frame() > effective_pos) {
2022                         break;
2023                 }
2024
2025                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2026                         tempo = t;
2027                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2028                         meter = m;
2029                 }
2030         }
2031
2032         /* We now have:
2033
2034            meter -> the Meter for "pos"
2035            tempo -> the Tempo for "pos"
2036            i     -> for first new metric after "pos", possibly metrics.end()
2037         */
2038
2039         /* now comes the complicated part. we have to add one beat a time,
2040            checking for a new metric on every beat.
2041         */
2042
2043         frames_per_beat = tempo->frames_per_beat (_frame_rate);
2044
2045         uint64_t bars = 0;
2046
2047         while (op.bars) {
2048
2049                 bars++;
2050                 op.bars--;
2051
2052                 /* check if we need to use a new metric section: has adding frames moved us
2053                    to or after the start of the next metric section? in which case, use it.
2054                 */
2055
2056                 if (i != metrics.end()) {
2057                         if ((*i)->frame() <= pos) {
2058
2059                                 /* about to change tempo or meter, so add the
2060                                  * number of frames for the bars we've just
2061                                  * traversed before we change the
2062                                  * frames_per_beat value.
2063                                  */
2064                                 
2065                                 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2066                                 bars = 0;
2067
2068                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2069                                         tempo = t;
2070                                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2071                                         meter = m;
2072                                 }
2073                                 ++i;
2074                                 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2075
2076                         }
2077                 }
2078
2079         }
2080
2081         pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2082
2083         uint64_t beats = 0;
2084
2085         while (op.beats) {
2086
2087                 /* given the current meter, have we gone past the end of the bar ? */
2088
2089                 beats++;
2090                 op.beats--;
2091
2092                 /* check if we need to use a new metric section: has adding frames moved us
2093                    to or after the start of the next metric section? in which case, use it.
2094                 */
2095
2096                 if (i != metrics.end()) {
2097                         if ((*i)->frame() <= pos) {
2098
2099                                 /* about to change tempo or meter, so add the
2100                                  * number of frames for the beats we've just
2101                                  * traversed before we change the
2102                                  * frames_per_beat value.
2103                                  */
2104
2105                                 pos += llrint (beats * frames_per_beat);
2106                                 beats = 0;
2107
2108                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2109                                         tempo = t;
2110                                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2111                                         meter = m;
2112                                 }
2113                                 ++i;
2114                                 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2115                         }
2116                 }
2117         }
2118
2119         pos += llrint (beats * frames_per_beat);
2120
2121         if (op.ticks) {
2122                 if (op.ticks >= BBT_Time::ticks_per_beat) {
2123                         pos += llrint (frames_per_beat + /* extra beat */
2124                                        (frames_per_beat * ((op.ticks % (uint32_t) BBT_Time::ticks_per_beat) / 
2125                                                            (double) BBT_Time::ticks_per_beat)));
2126                 } else {
2127                         pos += llrint (frames_per_beat * (op.ticks / (double) BBT_Time::ticks_per_beat));
2128                 }
2129         }
2130
2131         return pos;
2132 }
2133
2134 /** Count the number of beats that are equivalent to distance when going forward,
2135     starting at pos.
2136 */
2137 Evoral::MusicalTime
2138 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2139 {
2140         Glib::RWLock::ReaderLock lm (lock);
2141         Metrics::const_iterator next_tempo;
2142         const TempoSection* tempo = 0;
2143         framepos_t effective_pos = max (pos, (framepos_t) 0);
2144
2145         /* Find the relevant initial tempo metric  */
2146
2147         for (next_tempo = metrics.begin(); next_tempo != metrics.end(); ++next_tempo) {
2148
2149                 const TempoSection* t;
2150
2151                 if ((t = dynamic_cast<const TempoSection*>(*next_tempo)) != 0) {
2152
2153                         if ((*next_tempo)->frame() > effective_pos) {
2154                                 break;
2155                         }
2156
2157                         tempo = t;
2158                 }
2159         }
2160
2161         /* We now have:
2162
2163            tempo -> the Tempo for "pos"
2164            next_tempo -> the next tempo after "pos", possibly metrics.end()
2165         */
2166
2167         assert (tempo);
2168
2169         Evoral::MusicalTime beats = 0;
2170
2171         while (distance) {
2172
2173                 /* End of this section */
2174                 framepos_t const end = ((next_tempo == metrics.end()) ? max_framepos : (*next_tempo)->frame ());
2175
2176                 /* Distance to the end in frames */
2177                 framecnt_t const distance_to_end = end - pos;
2178
2179                 /* Amount to subtract this time */
2180                 double const sub = min (distance, distance_to_end);
2181
2182                 /* Update */
2183                 pos += sub;
2184                 distance -= sub;
2185                 assert (tempo);
2186                 beats += sub / tempo->frames_per_beat (_frame_rate);
2187                 
2188                 /* Move on if there's anything to move to */
2189
2190                 if (next_tempo != metrics.end()) {
2191
2192                         tempo = dynamic_cast<const TempoSection*>(*next_tempo);
2193
2194                         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("\tnew tempo = %1 @ %2 fpb = %3\n",
2195                                                                        *((Tempo*)tempo), tempo->frame(),
2196                                                                        tempo->frames_per_beat (_frame_rate)));
2197
2198                         while (next_tempo != metrics.end ()) {
2199
2200                                 ++next_tempo;
2201                                 
2202                                 if (next_tempo != metrics.end() && dynamic_cast<const TempoSection*>(*next_tempo)) {
2203                                         break;
2204                                 }
2205                         }
2206                 }
2207                 assert (tempo);
2208         }
2209
2210         return beats;
2211 }
2212
2213 TempoMap::BBTPointList::const_iterator
2214 TempoMap::bbt_before_or_at (framepos_t pos)
2215 {
2216         /* CALLER MUST HOLD READ LOCK */
2217
2218         BBTPointList::const_iterator i;
2219
2220         if (pos < 0) {
2221                 /* not really correct, but we should catch pos < 0 at a higher
2222                    level 
2223                 */
2224                 return _map.begin();
2225         }
2226
2227         i = lower_bound (_map.begin(), _map.end(), pos);
2228         assert (i != _map.end());
2229         if ((*i).frame > pos) {
2230                 assert (i != _map.begin());
2231                 --i;
2232         }
2233         return i;
2234 }
2235
2236 struct bbtcmp {
2237     bool operator() (const BBT_Time& a, const BBT_Time& b) {
2238             return a < b;
2239     }
2240 };
2241
2242 TempoMap::BBTPointList::const_iterator
2243 TempoMap::bbt_before_or_at (const BBT_Time& bbt)
2244 {
2245         BBTPointList::const_iterator i;
2246         bbtcmp cmp;
2247
2248         i = lower_bound (_map.begin(), _map.end(), bbt, cmp);
2249         assert (i != _map.end());
2250         if ((*i).bar > bbt.bars || (*i).beat > bbt.beats) {
2251                 assert (i != _map.begin());
2252                 --i;
2253         }
2254         return i;
2255 }
2256
2257 TempoMap::BBTPointList::const_iterator
2258 TempoMap::bbt_after_or_at (framepos_t pos) 
2259 {
2260         /* CALLER MUST HOLD READ LOCK */
2261
2262         BBTPointList::const_iterator i;
2263
2264         if (_map.back().frame == pos) {
2265                 i = _map.end();
2266                 assert (i != _map.begin());
2267                 --i;
2268                 return i;
2269         }
2270
2271         i = upper_bound (_map.begin(), _map.end(), pos);
2272         assert (i != _map.end());
2273         return i;
2274 }
2275
2276 std::ostream& 
2277 operator<< (std::ostream& o, const Meter& m) {
2278         return o << m.divisions_per_bar() << '/' << m.note_divisor();
2279 }
2280
2281 std::ostream& 
2282 operator<< (std::ostream& o, const Tempo& t) {
2283         return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2284 }
2285
2286 std::ostream& 
2287 operator<< (std::ostream& o, const MetricSection& section) {
2288
2289         o << "MetricSection @ " << section.frame() << " aka " << section.start() << ' ';
2290
2291         const TempoSection* ts;
2292         const MeterSection* ms;
2293
2294         if ((ts = dynamic_cast<const TempoSection*> (&section)) != 0) {
2295                 o << *((Tempo*) ts);
2296         } else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
2297                 o << *((Meter*) ms);
2298         }
2299
2300         return o;
2301 }