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