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