2dc5eeaada266ff4a8eb916b6a4602e09a14cafe
[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 <sigc++/bind.h>
28
29 #include <glibmm/thread.h>
30 #include "pbd/xml++.h"
31 #include "ardour/debug.h"
32 #include "ardour/tempo.h"
33 #include "ardour/utils.h"
34
35 #include "i18n.h"
36 #include <locale.h>
37
38 using namespace std;
39 using namespace ARDOUR;
40 using namespace PBD;
41
42 /* _default tempo is 4/4 qtr=120 */
43
44 Meter    TempoMap::_default_meter (4.0, 4.0);
45 Tempo    TempoMap::_default_tempo (120.0);
46
47 const double Meter::ticks_per_beat = 1920.0;
48
49 double Tempo::frames_per_beat (nframes_t sr, const Meter& meter) const
50 {
51         return  ((60.0 * sr) / (_beats_per_minute * meter.note_divisor()/_note_type));
52 }
53
54 /***********************************************************************/
55
56 double
57 Meter::frames_per_bar (const Tempo& tempo, nframes_t sr) const
58 {
59         return ((60.0 * sr * _beats_per_bar) / (tempo.beats_per_minute() * _note_type/tempo.note_type()));
60 }
61
62 /***********************************************************************/
63
64 const string TempoSection::xml_state_node_name = "Tempo";
65
66 TempoSection::TempoSection (const XMLNode& node)
67         : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
68 {
69         const XMLProperty *prop;
70         BBT_Time start;
71         LocaleGuard lg (X_("POSIX"));
72
73         if ((prop = node.property ("start")) == 0) {
74                 error << _("TempoSection XML node has no \"start\" property") << endmsg;
75                 throw failed_constructor();
76         }
77
78         if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
79                     &start.bars,
80                     &start.beats,
81                     &start.ticks) < 3) {
82                 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
83                 throw failed_constructor();
84         }
85
86         set_start (start);
87
88         if ((prop = node.property ("beats-per-minute")) == 0) {
89                 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
90                 throw failed_constructor();
91         }
92
93         if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
94                 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
95                 throw failed_constructor();
96         }
97
98         if ((prop = node.property ("note-type")) == 0) {
99                 /* older session, make note type be quarter by default */
100                 _note_type = 4.0;
101         } else {
102                 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
103                         error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
104                         throw failed_constructor();
105                 }
106         }
107
108         if ((prop = node.property ("movable")) == 0) {
109                 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
110                 throw failed_constructor();
111         }
112
113         set_movable (string_is_affirmative (prop->value()));
114 }
115
116 XMLNode&
117 TempoSection::get_state() const
118 {
119         XMLNode *root = new XMLNode (xml_state_node_name);
120         char buf[256];
121         LocaleGuard lg (X_("POSIX"));
122
123         snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
124                   start().bars,
125                   start().beats,
126                   start().ticks);
127         root->add_property ("start", buf);
128         snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
129         root->add_property ("beats-per-minute", buf);
130         snprintf (buf, sizeof (buf), "%f", _note_type);
131         root->add_property ("note-type", buf);
132         snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
133         root->add_property ("movable", buf);
134
135         return *root;
136 }
137
138 /***********************************************************************/
139
140 const string MeterSection::xml_state_node_name = "Meter";
141
142 MeterSection::MeterSection (const XMLNode& node)
143         : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
144 {
145         const XMLProperty *prop;
146         BBT_Time start;
147         LocaleGuard lg (X_("POSIX"));
148
149         if ((prop = node.property ("start")) == 0) {
150                 error << _("MeterSection XML node has no \"start\" property") << endmsg;
151                 throw failed_constructor();
152         }
153
154         if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
155                     &start.bars,
156                     &start.beats,
157                     &start.ticks) < 3) {
158                 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
159                 throw failed_constructor();
160         }
161
162         set_start (start);
163
164         if ((prop = node.property ("beats-per-bar")) == 0) {
165                 error << _("MeterSection XML node has no \"beats-per-bar\" property") << endmsg;
166                 throw failed_constructor();
167         }
168
169         if (sscanf (prop->value().c_str(), "%lf", &_beats_per_bar) != 1 || _beats_per_bar < 0.0) {
170                 error << _("MeterSection XML node has an illegal \"beats-per-bar\" value") << endmsg;
171                 throw failed_constructor();
172         }
173
174         if ((prop = node.property ("note-type")) == 0) {
175                 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
176                 throw failed_constructor();
177         }
178
179         if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
180                 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
181                 throw failed_constructor();
182         }
183
184         if ((prop = node.property ("movable")) == 0) {
185                 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
186                 throw failed_constructor();
187         }
188
189         set_movable (string_is_affirmative (prop->value()));
190 }
191
192 XMLNode&
193 MeterSection::get_state() const
194 {
195         XMLNode *root = new XMLNode (xml_state_node_name);
196         char buf[256];
197         LocaleGuard lg (X_("POSIX"));
198
199         snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
200                   start().bars,
201                   start().beats,
202                   start().ticks);
203         root->add_property ("start", buf);
204         snprintf (buf, sizeof (buf), "%f", _note_type);
205         root->add_property ("note-type", buf);
206         snprintf (buf, sizeof (buf), "%f", _beats_per_bar);
207         root->add_property ("beats-per-bar", buf);
208         snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
209         root->add_property ("movable", buf);
210
211         return *root;
212 }
213
214 /***********************************************************************/
215
216 struct MetricSectionSorter {
217     bool operator() (const MetricSection* a, const MetricSection* b) {
218             return a->start() < b->start();
219     }
220 };
221
222 TempoMap::TempoMap (nframes64_t fr)
223 {
224         metrics = new Metrics;
225         _frame_rate = fr;
226         last_bbt_valid = false;
227         BBT_Time start;
228
229         start.bars = 1;
230         start.beats = 1;
231         start.ticks = 0;
232
233         TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
234         MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor());
235
236         t->set_movable (false);
237         m->set_movable (false);
238
239         /* note: frame time is correct (zero) for both of these */
240
241         metrics->push_back (t);
242         metrics->push_back (m);
243 }
244
245 TempoMap::~TempoMap ()
246 {
247 }
248
249 int
250 TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
251 {
252         if (when == section.start() || !section.movable()) {
253                 return -1;
254         }
255
256         Glib::RWLock::WriterLock  lm (lock);
257         MetricSectionSorter cmp;
258
259         if (when.beats != 1) {
260
261                 /* position by audio frame, then recompute BBT timestamps from the audio ones */
262
263                 nframes64_t frame = frame_time (when);
264                 // cerr << "nominal frame time = " << frame << endl;
265
266                 nframes64_t prev_frame = round_to_type (frame, -1, Beat);
267                 nframes64_t next_frame = round_to_type (frame, 1, Beat);
268
269                 // cerr << "previous beat at " << prev_frame << " next at " << next_frame << endl;
270
271                 /* use the closest beat */
272
273                 if ((frame - prev_frame) < (next_frame - frame)) {
274                         frame = prev_frame;
275                 } else {
276                         frame = next_frame;
277                 }
278
279                 // cerr << "actual frame time = " << frame << endl;
280                 section.set_frame (frame);
281                 // cerr << "frame time = " << section.frame() << endl;
282                 timestamp_metrics (false);
283                 // cerr << "new BBT time = " << section.start() << endl;
284                 metrics->sort (cmp);
285
286         } else {
287
288                 /* positioned at bar start already, so just put it there */
289
290                 section.set_start (when);
291                 metrics->sort (cmp);
292                 timestamp_metrics (true);
293         }
294
295
296         return 0;
297 }
298
299 void
300 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
301 {
302         if (move_metric_section (tempo, when) == 0) {
303                 StateChanged (Change (0));
304         }
305 }
306
307 void
308 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
309 {
310         if (move_metric_section (meter, when) == 0) {
311                 StateChanged (Change (0));
312         }
313 }
314
315 void
316 TempoMap::remove_tempo (const TempoSection& tempo)
317 {
318         bool removed = false;
319
320         {
321                 Glib::RWLock::WriterLock lm (lock);
322                 Metrics::iterator i;
323
324                 for (i = metrics->begin(); i != metrics->end(); ++i) {
325                         if (dynamic_cast<TempoSection*> (*i) != 0) {
326                                 if (tempo.frame() == (*i)->frame()) {
327                                         if ((*i)->movable()) {
328                                                 metrics->erase (i);
329                                                 removed = true;
330                                                 break;
331                                         }
332                                 }
333                         }
334                 }
335         }
336
337         if (removed) {
338                 StateChanged (Change (0));
339         }
340 }
341
342 void
343 TempoMap::remove_meter (const MeterSection& tempo)
344 {
345         bool removed = false;
346
347         {
348                 Glib::RWLock::WriterLock lm (lock);
349                 Metrics::iterator i;
350
351                 for (i = metrics->begin(); i != metrics->end(); ++i) {
352                         if (dynamic_cast<MeterSection*> (*i) != 0) {
353                                 if (tempo.frame() == (*i)->frame()) {
354                                         if ((*i)->movable()) {
355                                                 metrics->erase (i);
356                                                 removed = true;
357                                                 break;
358                                         }
359                                 }
360                         }
361                 }
362         }
363
364         if (removed) {
365                 StateChanged (Change (0));
366         }
367 }
368
369 void
370 TempoMap::do_insert (MetricSection* section, bool with_bbt)
371 {
372         Metrics::iterator i;
373
374         for (i = metrics->begin(); i != metrics->end(); ++i) {
375
376                 if (with_bbt) {
377                         if ((*i)->start() < section->start()) {
378                                 continue;
379                         }
380                 } else {
381                         if ((*i)->frame() < section->frame()) {
382                                 continue;
383                         }
384                 }
385
386                 metrics->insert (i, section);
387                 break;
388         }
389
390         if (i == metrics->end()) {
391                 metrics->insert (metrics->end(), section);
392         }
393
394         timestamp_metrics (with_bbt);
395 }
396
397 void
398 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
399 {
400         {
401                 Glib::RWLock::WriterLock lm (lock);
402
403                 /* new tempos always start on a beat */
404
405                 where.ticks = 0;
406
407                 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), true);
408         }
409
410         StateChanged (Change (0));
411 }
412
413 void
414 TempoMap::add_tempo (const Tempo& tempo, nframes64_t where)
415 {
416         {
417                 Glib::RWLock::WriterLock lm (lock);
418                 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), false);
419         }
420
421         StateChanged (Change (0));
422 }
423
424 void
425 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
426 {
427         bool replaced = false;
428
429         {
430                 Glib::RWLock::WriterLock lm (lock);
431                 Metrics::iterator i;
432
433                 for (i = metrics->begin(); i != metrics->end(); ++i) {
434                         TempoSection *ts;
435
436                         if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
437
438                                  *((Tempo *) ts) = replacement;
439
440                                 replaced = true;
441                                 timestamp_metrics (true);
442
443                                 break;
444                         }
445                 }
446         }
447
448         if (replaced) {
449                 StateChanged (Change (0));
450         }
451 }
452
453 void
454 TempoMap::add_meter (const Meter& meter, BBT_Time where)
455 {
456         {
457                 Glib::RWLock::WriterLock lm (lock);
458
459                 /* a new meter always starts a new bar on the first beat. so
460                    round the start time appropriately. remember that
461                    `where' is based on the existing tempo map, not
462                    the result after we insert the new meter.
463
464                 */
465
466                 if (where.beats != 1) {
467                         where.beats = 1;
468                         where.bars++;
469                 }
470
471                 /* new meters *always* start on a beat. */
472
473                 where.ticks = 0;
474
475                 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), true);
476         }
477
478         StateChanged (Change (0));
479 }
480
481 void
482 TempoMap::add_meter (const Meter& meter, nframes64_t where)
483 {
484         {
485                 Glib::RWLock::WriterLock lm (lock);
486                 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), false);
487         }
488
489         StateChanged (Change (0));
490 }
491
492 void
493 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
494 {
495         bool replaced = false;
496
497         {
498                 Glib::RWLock::WriterLock lm (lock);
499                 Metrics::iterator i;
500
501                 for (i = metrics->begin(); i != metrics->end(); ++i) {
502                         MeterSection *ms;
503                         if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
504
505                                 *((Meter*) ms) = replacement;
506
507                                 replaced = true;
508                                 timestamp_metrics (true);
509                                 break;
510                         }
511                 }
512         }
513
514         if (replaced) {
515                 StateChanged (Change (0));
516         }
517 }
518
519 void
520 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
521 {
522         Tempo newtempo (beats_per_minute, note_type);
523         TempoSection* t;
524
525         for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
526                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
527                         *((Tempo*) t) = newtempo;
528                         StateChanged (Change (0));
529                         break;
530                 }
531         }
532 }
533
534 void
535 TempoMap::change_existing_tempo_at (nframes64_t where, double beats_per_minute, double note_type)
536 {
537         Tempo newtempo (beats_per_minute, note_type);
538
539         TempoSection* prev;
540         TempoSection* first;
541         Metrics::iterator i;
542
543         /* find the TempoSection immediately preceding "where"
544          */
545
546         for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
547
548                 if ((*i)->frame() > where) {
549                         break;
550                 }
551
552                 TempoSection* t;
553
554                 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
555                         if (!first) {
556                                 first = t;
557                         }
558                         prev = t;
559                 }
560         }
561
562         if (!prev) {
563                 if (!first) {
564                         error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
565                         return;
566                 }
567
568                 prev = first;
569         }
570
571         /* reset */
572
573         *((Tempo*)prev) = newtempo;
574         StateChanged (Change (0));
575 }
576
577 const MeterSection&
578 TempoMap::first_meter () const
579 {
580         const MeterSection *m = 0;
581
582         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
583                 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
584                         return *m;
585                 }
586         }
587
588         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
589         /*NOTREACHED*/
590         return *m;
591 }
592
593 const TempoSection&
594 TempoMap::first_tempo () const
595 {
596         const TempoSection *t = 0;
597
598         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
599                 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
600                         return *t;
601                 }
602         }
603
604         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
605         /*NOTREACHED*/
606         return *t;
607 }
608
609 void
610 TempoMap::timestamp_metrics (bool use_bbt)
611 {
612         Metrics::iterator i;
613         const Meter* meter;
614         const Tempo* tempo;
615         Meter *m;
616         Tempo *t;
617
618         meter = &first_meter ();
619         tempo = &first_tempo ();
620
621         if (use_bbt) {
622
623                 // cerr << "\n\n\n ######################\nTIMESTAMP via BBT ##############\n" << endl;
624
625                 nframes64_t current = 0;
626                 nframes64_t section_frames;
627                 BBT_Time start;
628                 BBT_Time end;
629
630                 for (i = metrics->begin(); i != metrics->end(); ++i) {
631
632                         end = (*i)->start();
633
634                         section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
635
636                         current += section_frames;
637
638                         start = end;
639
640                         (*i)->set_frame (current);
641
642                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
643                                 tempo = t;
644                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
645                                 meter = m;
646                         } else {
647                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
648                                 /*NOTREACHED*/
649                         }
650                 }
651
652         } else {
653
654                 // cerr << "\n\n\n ######################\nTIMESTAMP via AUDIO ##############\n" << endl;
655
656                 bool first = true;
657                 MetricSection* prev = 0;
658
659                 for (i = metrics->begin(); i != metrics->end(); ++i) {
660
661                         BBT_Time bbt;
662                         TempoMetric metric (*meter, *tempo);
663
664                         if (prev) {
665                                 metric.set_start (prev->start());
666                                 metric.set_frame (prev->frame());
667                         } else {
668                                 // metric will be at frames=0 bbt=1|1|0 by default
669                                 // which is correct for our purpose
670                         }
671
672                         bbt_time_with_metric ((*i)->frame(), bbt, metric);
673
674                         // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
675
676
677                         if (first) {
678                                 first = false;
679                         } else {
680
681                                 if (bbt.ticks > Meter::ticks_per_beat/2) {
682                                         /* round up to next beat */
683                                         bbt.beats += 1;
684                                 }
685
686                                 bbt.ticks = 0;
687
688                                 if (bbt.beats != 1) {
689                                         /* round up to next bar */
690                                         bbt.bars += 1;
691                                         bbt.beats = 1;
692                                 }
693                         }
694
695                         //s cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
696
697                         (*i)->set_start (bbt);
698
699                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
700                                 tempo = t;
701                                 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
702                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
703                                 meter = m;
704                                 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
705                         } else {
706                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
707                                 /*NOTREACHED*/
708                         }
709
710                         prev = (*i);
711                 }
712         }
713
714         // dump (cerr);
715         // cerr << "###############################################\n\n\n" << endl;
716
717 }
718
719 TempoMetric
720 TempoMap::metric_at (nframes64_t frame) const
721 {
722         TempoMetric m (first_meter(), first_tempo());
723         const Meter* meter;
724         const Tempo* tempo;
725
726         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
727            at something, because we insert the default tempo and meter during
728            TempoMap construction.
729
730            now see if we can find better candidates.
731         */
732
733         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
734
735                 if ((*i)->frame() > frame) {
736                         break;
737                 }
738
739                 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
740                         m.set_tempo (*tempo);
741                 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
742                         m.set_meter (*meter);
743                 }
744
745                 m.set_frame ((*i)->frame ());
746                 m.set_start ((*i)->start ());
747         }
748
749         return m;
750 }
751
752 TempoMetric
753 TempoMap::metric_at (BBT_Time bbt) const
754 {
755         TempoMetric m (first_meter(), first_tempo());
756         const Meter* meter;
757         const Tempo* tempo;
758
759         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
760            at something, because we insert the default tempo and meter during
761            TempoMap construction.
762
763            now see if we can find better candidates.
764         */
765
766         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
767
768                 BBT_Time section_start ((*i)->start());
769
770                 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
771                         break;
772                 }
773
774                 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
775                         m.set_tempo (*tempo);
776                 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
777                         m.set_meter (*meter);
778                 }
779
780                 m.set_frame ((*i)->frame ());
781                 m.set_start (section_start);
782         }
783
784         return m;
785 }
786
787 void
788 TempoMap::bbt_time (nframes64_t frame, BBT_Time& bbt) const
789 {
790         {
791                 Glib::RWLock::ReaderLock lm (lock);
792                 bbt_time_unlocked (frame, bbt);
793         }
794 }
795
796 void
797 TempoMap::bbt_time_unlocked (nframes64_t frame, BBT_Time& bbt) const
798 {
799         bbt_time_with_metric (frame, bbt, metric_at (frame));
800 }
801
802 void
803 TempoMap::bbt_time_with_metric (nframes64_t frame, BBT_Time& bbt, const TempoMetric& metric) const
804 {
805         nframes64_t frame_diff;
806
807         // cerr << "---- BBT time for " << frame << " using metric @ " << metric.frame() << " BBT " << metric.start() << endl;
808
809         const double beats_per_bar = metric.meter().beats_per_bar();
810         const double ticks_per_frame = metric.tempo().frames_per_beat (_frame_rate, metric.meter()) / Meter::ticks_per_beat;
811
812         /* now compute how far beyond that point we actually are. */
813
814         frame_diff = frame - metric.frame();
815
816         bbt.ticks = metric.start().ticks + (uint32_t)round((double)frame_diff / ticks_per_frame);
817         uint32_t xtra_beats = bbt.ticks / (uint32_t)Meter::ticks_per_beat;
818         bbt.ticks %= (uint32_t)Meter::ticks_per_beat;
819
820         bbt.beats = metric.start().beats + xtra_beats - 1; // correction for 1-based counting, see below for matching operation.
821         bbt.bars = metric.start().bars + (uint32_t)floor((double)bbt.beats / beats_per_bar);
822         bbt.beats = (uint32_t)fmod((double)bbt.beats, beats_per_bar);
823
824         /* if we have a fractional number of beats per bar, we see if
825            we're in the last beat (the fractional one).  if so, we
826            round ticks appropriately and bump to the next bar. */
827         double beat_fraction = beats_per_bar - floor(beats_per_bar);
828         /* XXX one problem here is that I'm not sure how to handle
829            fractional beats that don't evenly divide ticks_per_beat.
830            If they aren't handled consistently, I would guess we'll
831            continue to have strange discrepancies occuring.  Perhaps
832            this will also behave badly in the case of meters like
833            0.1/4, but I can't be bothered to test that.
834         */
835         uint32_t ticks_on_last_beat = (uint32_t)floor(Meter::ticks_per_beat * beat_fraction);
836
837         if (bbt.beats > (uint32_t)floor(beats_per_bar) && bbt.ticks >= ticks_on_last_beat) {
838                 bbt.ticks -= ticks_on_last_beat;
839                 bbt.beats = 0;
840                 bbt.bars++;
841         }
842
843         bbt.beats++; // correction for 1-based counting, see above for matching operation.
844
845         // cerr << "-----\t RETURN " << bbt << endl;
846 }
847
848 nframes64_t
849 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
850 {
851         /* for this to work with fractional measure types, start and end have to be "legal" BBT types,
852            that means that the beats and ticks should be inside a bar
853         */
854
855         nframes64_t frames = 0;
856         nframes64_t start_frame = 0;
857         nframes64_t end_frame = 0;
858
859         TempoMetric m = metric_at (start);
860
861         uint32_t bar_offset = start.bars - m.start().bars;
862
863         double  beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
864                 + start.ticks/Meter::ticks_per_beat;
865
866
867         start_frame = m.frame() + (nframes64_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
868
869         m =  metric_at(end);
870
871         bar_offset = end.bars - m.start().bars;
872
873         beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
874                 + end.ticks/Meter::ticks_per_beat;
875
876         end_frame = m.frame() + (nframes64_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
877
878         frames = end_frame - start_frame;
879
880         return frames;
881
882 }
883
884 nframes64_t
885 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
886 {
887         /* this is used in timestamping the metrics by actually counting the beats */
888
889         nframes64_t frames = 0;
890         uint32_t bar = start.bars;
891         double beat = (double) start.beats;
892         double beats_counted = 0;
893         double beats_per_bar = 0;
894         double beat_frames = 0;
895
896         beats_per_bar = meter.beats_per_bar();
897         beat_frames = tempo.frames_per_beat (_frame_rate,meter);
898
899         frames = 0;
900
901         while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
902
903                 if (beat >= beats_per_bar) {
904                         beat = 1;
905                         ++bar;
906                         ++beats_counted;
907
908                         if (beat > beats_per_bar) {
909
910                                 /* this is a fractional beat at the end of a fractional bar
911                                    so it should only count for the fraction
912                                 */
913
914                                 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
915                         }
916
917                 } else {
918                         ++beat;
919                         ++beats_counted;
920                 }
921         }
922
923         // cerr << "Counted " << beats_counted << " from " << start << " to " << end
924         // << " bpb were " << beats_per_bar
925         // << " fpb was " << beat_frames
926         // << endl;
927
928         frames = (nframes64_t) floor (beats_counted * beat_frames);
929
930         return frames;
931
932 }
933
934 nframes64_t
935 TempoMap::frame_time (const BBT_Time& bbt) const
936 {
937         BBT_Time start ; /* 1|1|0 */
938
939         return  count_frames_between ( start, bbt);
940 }
941
942 nframes64_t
943 TempoMap::bbt_duration_at (nframes64_t pos, const BBT_Time& bbt, int dir) const
944 {
945         nframes64_t frames = 0;
946
947         BBT_Time when;
948         bbt_time(pos, when);
949
950         {
951                 Glib::RWLock::ReaderLock lm (lock);
952                 frames = bbt_duration_at_unlocked (when, bbt,dir);
953         }
954
955         return frames;
956 }
957
958 nframes64_t
959 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
960 {
961
962         nframes64_t frames = 0;
963
964         double beats_per_bar;
965         BBT_Time result;
966
967         result.bars = max(1U, when.bars + dir * bbt.bars) ;
968         result.beats = 1;
969         result.ticks = 0;
970
971         TempoMetric     metric = metric_at(result);
972         beats_per_bar = metric.meter().beats_per_bar();
973
974
975
976         /*reduce things to legal bbt  values
977           we have to handle possible fractional=shorter beats at the end of measures
978           and things like 0|11|9000  as a duration in a 4.5/4 measure
979           the musical decision is that the fractional beat is also a beat , although a shorter one
980         */
981
982
983         if (dir >= 0) {
984                 result.beats = when.beats +  bbt.beats;
985                 result.ticks = when.ticks +  bbt.ticks;
986
987                 while (result.beats >= (beats_per_bar + 1)) {
988                         result.bars++;
989                         result.beats -=  (uint32_t) ceil(beats_per_bar);
990                         metric = metric_at(result); // maybe there is a meter change
991                         beats_per_bar = metric.meter().beats_per_bar();
992
993                 }
994                 /*we now counted the beats and landed in the target measure, now deal with ticks
995                   this seems complicated, but we want to deal with the corner case of a sequence of time signatures like 0.2/4-0.7/4
996                   and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
997                 */
998
999                 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
1000                  */
1001
1002                 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
1003                                         (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat
1004                                            : Meter::ticks_per_beat );
1005
1006                 while (result.ticks >= ticks_at_beat) {
1007                         result.beats++;
1008                         result.ticks -= ticks_at_beat;
1009                         if  (result.beats >= (beats_per_bar + 1)) {
1010                                 result.bars++;
1011                                 result.beats = 1;
1012                                 metric = metric_at(result); // maybe there is a meter change
1013                                 beats_per_bar = metric.meter().beats_per_bar();
1014                         }
1015                         ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
1016                                        (1 - (ceil(beats_per_bar) - beats_per_bar) ) * Meter::ticks_per_beat
1017                                        : Meter::ticks_per_beat);
1018
1019                 }
1020
1021
1022         } else {
1023                 uint32_t b = bbt.beats;
1024
1025                 /* count beats */
1026                 while( b > when.beats ) {
1027
1028                         result.bars = max(1U,result.bars-- ) ;
1029                         metric = metric_at(result); // maybe there is a meter change
1030                         beats_per_bar = metric.meter().beats_per_bar();
1031                         if (b >= ceil(beats_per_bar)) {
1032
1033                                 b -= (uint32_t) ceil(beats_per_bar);
1034                         } else {
1035                                 b = (uint32_t) ceil(beats_per_bar) - b + when.beats ;
1036                         }
1037                 }
1038                 result.beats = when.beats - b;
1039
1040                 /*count ticks */
1041
1042                 if (bbt.ticks <= when.ticks) {
1043                         result.ticks = when.ticks - bbt.ticks;
1044                 } else {
1045
1046                         uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
1047                         uint32_t t = bbt.ticks - when.ticks;
1048
1049                         do {
1050
1051                                 if (result.beats == 1) {
1052                                         result.bars = max(1U, result.bars-- ) ;
1053                                         metric = metric_at(result); // maybe there is a meter change
1054                                         beats_per_bar = metric.meter().beats_per_bar();
1055                                         result.beats = (uint32_t) ceil(beats_per_bar);
1056                                         ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar)) * Meter::ticks_per_beat) ;
1057                                 } else {
1058                                         result.beats --;
1059                                         ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
1060                                 }
1061
1062                                 if (t <= ticks_at_beat) {
1063                                         result.ticks = ticks_at_beat - t;
1064                                 } else {
1065                                         t-= ticks_at_beat;
1066                                 }
1067                         } while (t > ticks_at_beat);
1068
1069                 }
1070
1071
1072         }
1073
1074         if (dir < 0 ) {
1075                 frames = count_frames_between( result,when);
1076         } else {
1077                 frames = count_frames_between(when,result);
1078         }
1079
1080         return frames;
1081 }
1082
1083
1084
1085 nframes64_t
1086 TempoMap::round_to_bar (nframes64_t fr, int dir)
1087 {
1088         {
1089                 Glib::RWLock::ReaderLock lm (lock);
1090                 return round_to_type (fr, dir, Bar);
1091         }
1092 }
1093
1094
1095 nframes64_t
1096 TempoMap::round_to_beat (nframes64_t fr, int dir)
1097 {
1098         {
1099                 Glib::RWLock::ReaderLock lm (lock);
1100                 return round_to_type (fr, dir, Beat);
1101         }
1102 }
1103
1104 nframes64_t
1105 TempoMap::round_to_beat_subdivision (nframes64_t fr, int sub_num, int dir)
1106 {
1107
1108         BBT_Time the_beat;
1109         uint32_t ticks_one_half_subdivisions_worth;
1110         uint32_t ticks_one_subdivisions_worth;
1111         uint32_t difference;
1112
1113         bbt_time(fr, the_beat);
1114
1115         ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
1116         ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
1117
1118         if (dir > 0) {
1119
1120                 /* round to next */
1121
1122                 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1123
1124                 if (mod == 0) {
1125                         /* right on the subdivision, so the difference is just the subdivision ticks */
1126                         difference = ticks_one_subdivisions_worth;
1127
1128                 } else {
1129                         /* not on subdivision, compute distance to next subdivision */
1130
1131                         difference = ticks_one_subdivisions_worth - mod;
1132                 }
1133
1134                 if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
1135                         the_beat.beats++;
1136                         the_beat.ticks += difference;
1137                         the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
1138                 } else {
1139                         the_beat.ticks += difference;
1140                 }
1141
1142         } else if (dir < 0) {
1143
1144                 /* round to previous */
1145
1146                 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1147
1148                 if (mod == 0) {
1149                         /* right on the subdivision, so the difference is just the subdivision ticks */
1150                         difference = ticks_one_subdivisions_worth;
1151                         cerr << "On the sub, move by 1 sub = " << difference << endl;
1152                 } else {
1153                         /* not on subdivision, compute distance to previous subdivision, which
1154                            is just the modulus.
1155                         */
1156
1157                         difference = mod;
1158                         cerr << "off the sub, move by 1 sub = " << difference << endl;
1159                 }
1160
1161
1162                 cerr << "ticks = " << the_beat.ticks << endl;
1163
1164                 if (the_beat.ticks < difference) {
1165                         cerr << "backup beats, set ticks to "
1166                              << (uint32_t)Meter::ticks_per_beat - difference << endl;
1167                         the_beat.beats--;
1168                         the_beat.ticks = (uint32_t)Meter::ticks_per_beat - difference;
1169                 } else {
1170                         cerr << " reduce ticks\n";
1171                         the_beat.ticks -= difference;
1172                 }
1173
1174         } else {
1175                 /* round to nearest */
1176
1177                 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1178                         difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1179                         if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
1180                                 the_beat.beats++;
1181                                 the_beat.ticks += difference;
1182                                 the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
1183                         } else {
1184                                 the_beat.ticks += difference;
1185                         }
1186                 } else {
1187                         // difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1188                         the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1189                 }
1190         }
1191
1192         return frame_time (the_beat);
1193 }
1194
1195 nframes64_t
1196 TempoMap::round_to_type (nframes64_t frame, int dir, BBTPointType type)
1197 {
1198         TempoMetric metric = metric_at (frame);
1199         BBT_Time bbt;
1200         BBT_Time start;
1201         BBT_Time one_bar (1,0,0);
1202         BBT_Time one_beat (0,1,0);
1203
1204         bbt_time_with_metric (frame, bbt, metric);
1205
1206         switch (type) {
1207         case Bar:
1208                 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to bars in direction %2\n", frame, (dir < 0 ? "back" : "forward"), bbt));
1209
1210                 if (dir < 0) {
1211
1212                         /* find bar position preceding frame */
1213
1214                         try {
1215                                 bbt = bbt_subtract (bbt, one_bar);
1216                         }
1217
1218                         catch (...) {
1219                                 return frame;
1220                         }
1221
1222
1223                 } else if (dir > 0) {
1224
1225                         /* find bar position following frame */
1226
1227                         try {
1228                                 bbt = bbt_add (bbt, one_bar, metric);
1229                         }
1230                         catch (...) {
1231                                 return frame;
1232                         }
1233
1234                 } else {
1235
1236                         /* "true" rounding */
1237
1238                         float midbar_beats;
1239                         float midbar_ticks;
1240
1241                         midbar_beats = metric.meter().beats_per_bar() / 2;
1242                         midbar_ticks = Meter::ticks_per_beat * fmod (midbar_beats, 1.0f);
1243                         midbar_beats = floor (midbar_beats);
1244                         
1245                         BBT_Time midbar (bbt.bars, lrintf (midbar_beats), lrintf (midbar_ticks));
1246
1247                         if (bbt < midbar) {
1248                                 /* round down */
1249                                 bbt.beats = 1;
1250                                 bbt.ticks = 0;
1251                         } else {
1252                                 /* round up */
1253                                 bbt.bars++;
1254                                 bbt.beats = 1;
1255                                 bbt.ticks = 0;
1256                         }
1257                 }
1258                 /* force beats & ticks to their values at the start of a bar */
1259                 bbt.beats = 1;
1260                 bbt.ticks = 0;
1261                 break;
1262
1263         case Beat:
1264                 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to beat in direction %2\n", frame, (dir < 0 ? "back" : "forward"), bbt));
1265
1266                 if (dir < 0) {
1267
1268                         /* find beat position preceding frame */
1269
1270                         try {
1271                                 bbt = bbt_subtract (bbt, one_beat); 
1272                         }
1273
1274                         catch (...) {
1275                                 return frame;
1276                         }
1277
1278
1279                 } else if (dir > 0) {
1280
1281                         /* find beat position following frame */
1282
1283                         try {
1284                                 bbt = bbt_add (bbt, one_beat, metric);
1285                         }
1286                         catch (...) {
1287                                 return frame;
1288                         }
1289
1290                 } else {
1291
1292                         /* "true" rounding */
1293
1294                         /* round to nearest beat */
1295                         if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1296
1297                                 try {
1298                                         bbt = bbt_add (bbt, one_beat, metric);
1299                                 }
1300                                 catch (...) {
1301                                         return frame;
1302                                 }
1303                         }
1304                 }
1305                 /* force ticks to the value at the start of a beat */
1306                 bbt.ticks = 0;
1307                 break;
1308
1309         }
1310
1311         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)));
1312         return metric.frame() + count_frames_between (metric.start(), bbt);
1313 }
1314
1315 TempoMap::BBTPointList *
1316 TempoMap::get_points (nframes64_t lower, nframes64_t upper) const
1317 {
1318
1319         Metrics::const_iterator i;
1320         BBTPointList *points;
1321         double current;
1322         const MeterSection* meter;
1323         const MeterSection* m;
1324         const TempoSection* tempo;
1325         const TempoSection* t;
1326         uint32_t bar;
1327         uint32_t beat;
1328         double beats_per_bar;
1329         double beat_frame;
1330         double beat_frames;
1331         double frames_per_bar;
1332         double delta_bars;
1333         double delta_beats;
1334         double dummy;
1335         nframes64_t limit;
1336
1337         meter = &first_meter ();
1338         tempo = &first_tempo ();
1339
1340         /* find the starting point */
1341
1342         for (i = metrics->begin(); i != metrics->end(); ++i) {
1343
1344                 if ((*i)->frame() > lower) {
1345                         break;
1346                 }
1347
1348                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1349                         tempo = t;
1350                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1351                         meter = m;
1352                 }
1353         }
1354
1355         /* We now have:
1356
1357            meter -> the Meter for "lower"
1358            tempo -> the Tempo for "lower"
1359            i     -> for first new metric after "lower", possibly metrics->end()
1360
1361            Now start generating points.
1362         */
1363
1364         beats_per_bar = meter->beats_per_bar ();
1365         frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1366         beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1367
1368         if (meter->frame() > tempo->frame()) {
1369                 bar = meter->start().bars;
1370                 beat = meter->start().beats;
1371                 current = meter->frame();
1372         } else {
1373                 bar = tempo->start().bars;
1374                 beat = tempo->start().beats;
1375                 current = tempo->frame();
1376         }
1377
1378         /* initialize current to point to the bar/beat just prior to the
1379            lower frame bound passed in.  assumes that current is initialized
1380            above to be on a beat.
1381         */
1382
1383         delta_bars = (lower-current) / frames_per_bar;
1384         delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1385         current += (floor(delta_bars) * frames_per_bar) +  (floor(delta_beats) * beat_frames);
1386
1387         // adjust bars and beats too
1388         bar += (uint32_t) (floor(delta_bars));
1389         beat += (uint32_t) (floor(delta_beats));
1390
1391         points = new BBTPointList;
1392
1393         do {
1394
1395                 if (i == metrics->end()) {
1396                         limit = upper;
1397                         // cerr << "== limit set to end of request @ " << limit << endl;
1398                 } else {
1399                         // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
1400                         limit = (*i)->frame();
1401                 }
1402
1403                 limit = min (limit, upper);
1404
1405                 while (current < limit) {
1406
1407                         /* if we're at the start of a bar, add bar point */
1408
1409                         if (beat == 1) {
1410                                 if (current >= lower) {
1411                                         // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1412                                         points->push_back (BBTPoint (*meter, *tempo,(nframes64_t)rint(current), Bar, bar, 1));
1413
1414                                 }
1415                         }
1416
1417                         /* add some beats if we can */
1418
1419                         beat_frame = current;
1420
1421                         while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1422                                 if (beat_frame >= lower) {
1423                                         // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1424                                         points->push_back (BBTPoint (*meter, *tempo, (nframes64_t) rint(beat_frame), Beat, bar, beat));
1425                                 }
1426                                 beat_frame += beat_frames;
1427                                 current+= beat_frames;
1428
1429                                 beat++;
1430                         }
1431
1432                         //  cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
1433                         // << (beat > ceil(beats_per_bar))
1434                         // << endl;
1435
1436                         if (beat > ceil(beats_per_bar) || i != metrics->end()) {
1437
1438                                 /* we walked an entire bar. its
1439                                    important to move `current' forward
1440                                    by the actual frames_per_bar, not move it to
1441                                    an integral beat_frame, so that metrics with
1442                                    non-integral beats-per-bar have
1443                                    their bar positions set
1444                                    correctly. consider a metric with
1445                                    9-1/2 beats-per-bar. the bar we
1446                                    just filled had  10 beat marks,
1447                                    but the bar end is 1/2 beat before
1448                                    the last beat mark.
1449                                    And it is also possible that a tempo
1450                                    change occured in the middle of a bar,
1451                                    so we subtract the possible extra fraction from the current
1452                                 */
1453
1454                                 if (beat > ceil (beats_per_bar)) {
1455                                         /* next bar goes where the numbers suggest */
1456                                         current -=  beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1457                                         // cerr << "++ next bar from numbers\n";
1458                                 } else {
1459                                         /* next bar goes where the next metric is */
1460                                         current = limit;
1461                                         // cerr << "++ next bar at next metric\n";
1462                                 }
1463                                 bar++;
1464                                 beat = 1;
1465                         }
1466
1467                 }
1468
1469                 /* if we're done, then we're done */
1470
1471                 if (current >= upper) {
1472                         break;
1473                 }
1474
1475                 /* i is an iterator that refers to the next metric (or none).
1476                    if there is a next metric, move to it, and continue.
1477                 */
1478
1479                 if (i != metrics->end()) {
1480
1481                         if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1482                                 tempo = t;
1483                         } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1484                                 meter = m;
1485                                 /* new MeterSection, beat always returns to 1 */
1486                                 beat = 1;
1487                         }
1488
1489                         current = (*i)->frame ();
1490                         // cerr << "loop around with current @ " << current << endl;
1491
1492                         beats_per_bar = meter->beats_per_bar ();
1493                         frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1494                         beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1495
1496                         ++i;
1497                 }
1498
1499         } while (1);
1500
1501         return points;
1502 }
1503
1504 const TempoSection&
1505 TempoMap::tempo_section_at (nframes64_t frame)
1506 {
1507         Glib::RWLock::ReaderLock lm (lock);
1508         Metrics::iterator i;
1509         TempoSection* prev = 0;
1510
1511         for (i = metrics->begin(); i != metrics->end(); ++i) {
1512                 TempoSection* t;
1513
1514                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1515
1516                         if ((*i)->frame() > frame) {
1517                                 break;
1518                         }
1519
1520                         prev = t;
1521                 }
1522         }
1523
1524         if (prev == 0) {
1525                 fatal << endmsg;
1526         }
1527
1528         return *prev;
1529 }
1530
1531 const Tempo&
1532 TempoMap::tempo_at (nframes64_t frame) const
1533 {
1534         TempoMetric m (metric_at (frame));
1535         return m.tempo();
1536 }
1537
1538
1539 const Meter&
1540 TempoMap::meter_at (nframes64_t frame) const
1541 {
1542         TempoMetric m (metric_at (frame));
1543         return m.meter();
1544 }
1545
1546 XMLNode&
1547 TempoMap::get_state ()
1548 {
1549         Metrics::const_iterator i;
1550         XMLNode *root = new XMLNode ("TempoMap");
1551
1552         {
1553                 Glib::RWLock::ReaderLock lm (lock);
1554                 for (i = metrics->begin(); i != metrics->end(); ++i) {
1555                         root->add_child_nocopy ((*i)->get_state());
1556                 }
1557         }
1558
1559         return *root;
1560 }
1561
1562 int
1563 TempoMap::set_state (const XMLNode& node, int /*version*/)
1564 {
1565         {
1566                 Glib::RWLock::WriterLock lm (lock);
1567
1568                 XMLNodeList nlist;
1569                 XMLNodeConstIterator niter;
1570                 Metrics old_metrics (*metrics);
1571
1572                 metrics->clear();
1573
1574                 nlist = node.children();
1575
1576                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1577                         XMLNode* child = *niter;
1578
1579                         if (child->name() == TempoSection::xml_state_node_name) {
1580
1581                                 try {
1582                                         metrics->push_back (new TempoSection (*child));
1583                                 }
1584
1585                                 catch (failed_constructor& err){
1586                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1587                                         *metrics = old_metrics;
1588                                         break;
1589                                 }
1590
1591                         } else if (child->name() == MeterSection::xml_state_node_name) {
1592
1593                                 try {
1594                                         metrics->push_back (new MeterSection (*child));
1595                                 }
1596
1597                                 catch (failed_constructor& err) {
1598                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1599                                         *metrics = old_metrics;
1600                                         break;
1601                                 }
1602                         }
1603                 }
1604
1605                 if (niter == nlist.end()) {
1606
1607                         MetricSectionSorter cmp;
1608                         metrics->sort (cmp);
1609                         timestamp_metrics (true);
1610                 }
1611         }
1612
1613         StateChanged (Change (0));
1614
1615         return 0;
1616 }
1617
1618 void
1619 TempoMap::dump (std::ostream& o) const
1620 {
1621         const MeterSection* m;
1622         const TempoSection* t;
1623
1624         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1625
1626                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1627                         o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1628                           << t->movable() << ')' << endl;
1629                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1630                         o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1631                           << " (move? " << m->movable() << ')' << endl;
1632                 }
1633         }
1634 }
1635
1636 int
1637 TempoMap::n_tempos() const
1638 {
1639         Glib::RWLock::ReaderLock lm (lock);
1640         int cnt = 0;
1641
1642         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1643                 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1644                         cnt++;
1645                 }
1646         }
1647
1648         return cnt;
1649 }
1650
1651 int
1652 TempoMap::n_meters() const
1653 {
1654         Glib::RWLock::ReaderLock lm (lock);
1655         int cnt = 0;
1656
1657         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1658                 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1659                         cnt++;
1660                 }
1661         }
1662
1663         return cnt;
1664 }
1665
1666 void
1667 TempoMap::insert_time (nframes64_t where, nframes64_t amount)
1668 {
1669         for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1670                 if ((*i)->frame() >= where) {
1671                         (*i)->set_frame ((*i)->frame() + amount);
1672                 }
1673         }
1674
1675         timestamp_metrics (false);
1676
1677         StateChanged (Change (0));
1678 }
1679
1680 BBT_Time
1681 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& other) const
1682 {
1683         TempoMetric metric =  metric_at (start);
1684         return bbt_add (start, other, metric);
1685 }
1686
1687 /**
1688  * add the BBT interval @param increment to  @param start and return the result
1689  */
1690 BBT_Time
1691 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& increment, const TempoMetric& metric) const
1692 {
1693         BBT_Time result = start;
1694         BBT_Time op = increment; /* argument is const, but we need to modify it */
1695         uint32_t ticks = result.ticks + op.ticks;
1696
1697         if (ticks >= Meter::ticks_per_beat) {
1698                 op.beats++;
1699                 result.ticks = ticks % (uint32_t) Meter::ticks_per_beat;
1700         } 
1701
1702         /* now comes the complicated part. we have to add one beat a time,
1703            checking for a new metric on every beat.
1704         */
1705         
1706         /* grab all meter sections */
1707         
1708         list<const MeterSection*> meter_sections;
1709         
1710         for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1711                 const MeterSection* ms;
1712                 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1713                         meter_sections.push_back (ms);
1714                 }
1715         }
1716         
1717         assert (!meter_sections.empty());
1718         
1719         list<const MeterSection*>::const_iterator next_meter;
1720         const Meter* meter = 0;
1721         
1722         /* go forwards through the meter sections till we get to the one
1723            covering the current value of result. this positions i to point to 
1724            the next meter section too, or the end.
1725         */
1726         
1727         for (next_meter = meter_sections.begin(); next_meter != meter_sections.end(); ++next_meter) {
1728                 
1729                 if (result < (*next_meter)->start()) {
1730                         /* this metric is past the result time. stop looking, we have what we need */
1731                         break;
1732                 }
1733
1734                 if (result == (*next_meter)->start()) {
1735                         /* this meter section starts at result, push i beyond it so that it points
1736                            to the NEXT section, opwise we will get stuck later, and use this meter section.
1737                         */
1738                         meter = *next_meter;
1739                         ++next_meter;
1740                         break;
1741                 }
1742                 
1743                 meter = *next_meter;
1744         }
1745         
1746         assert (meter != 0);
1747                 
1748         /* OK, now have the meter for the bar start we are on, and i is an iterator 
1749            that points to the metric after the one we are currently dealing with 
1750            (or to metrics->end(), of course) 
1751         */
1752         
1753         while (op.beats) {
1754                 
1755                 /* given the current meter, have we gone past the end of the bar ? */
1756                 
1757                 if (result.beats >= meter->beats_per_bar()) {
1758                         /* move to next bar, first beat */
1759                         result.bars++;
1760                         result.beats = 1;
1761                 } else {
1762                         result.beats++;
1763                 }
1764                 
1765                 /* one down ... */
1766                 
1767                 op.beats--;
1768                 
1769                 /* check if we need to use a new meter section: has adding beats to result taken us 
1770                    to or after the start of the next meter section? in which case, use it.
1771                 */
1772
1773                 if (next_meter != meter_sections.end() && (((*next_meter)->start () < result) || (result == (*next_meter)->start()))) {
1774                         meter = *next_meter;
1775                         ++next_meter;
1776                 }
1777         }
1778
1779         /* finally, add bars */
1780
1781         result.bars += op.bars++;
1782
1783         return result;
1784 }
1785
1786 /**
1787  * subtract the BBT interval @param decrement from @param start and return the result
1788  */
1789 BBT_Time
1790 TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const
1791 {
1792         BBT_Time result = start;
1793         BBT_Time op = decrement; /* argument is const, but we need to modify it */
1794
1795         if (op.ticks > result.ticks) {
1796                 /* subtract an extra beat later; meanwhile set ticks to the right "carry" value */
1797                 op.beats++;
1798                 result.ticks = Meter::ticks_per_beat - (op.ticks - result.ticks);
1799         } else {
1800                 result.ticks -= op.ticks;
1801         }
1802
1803         /* now comes the complicated part. we have to subtract one beat a time,
1804            checking for a new metric on every beat.
1805         */
1806         
1807         /* grab all meter sections */
1808         
1809         list<const MeterSection*> meter_sections;
1810         
1811         for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1812                 const MeterSection* ms;
1813                 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1814                         meter_sections.push_back (ms);
1815                 }
1816                 }
1817         
1818         assert (!meter_sections.empty());
1819         
1820         /* go backwards through the meter sections till we get to the one
1821            covering the current value of result. this positions i to point to 
1822            the next (previous) meter section too, or the end.
1823         */
1824         
1825         const MeterSection* meter = 0;
1826         list<const MeterSection*>::reverse_iterator next_meter; // older versions of GCC don't 
1827                                                                 // support const_reverse_iterator::operator!=()
1828         
1829         for (next_meter = meter_sections.rbegin(); next_meter != meter_sections.rend(); ++next_meter) {
1830                 
1831                 /* when we find the first meter section that is before or at result, use it,
1832                    and set next_meter to the previous one 
1833                 */
1834                 
1835                 if ((*next_meter)->start() < result || (*next_meter)->start() == result) {
1836                         meter = *next_meter;
1837                         ++next_meter;
1838                         break;
1839                 }
1840         }
1841
1842         assert (meter != 0);
1843         
1844         /* OK, now have the meter for the bar start we are on, and i is an iterator 
1845            that points to the metric after the one we are currently dealing with 
1846            (or to metrics->end(), of course) 
1847         */
1848         
1849         while (op.beats) {
1850
1851                 /* have we reached the start of the bar? if so, move to the last beat of the previous
1852                    bar. opwise, just step back 1 beat.
1853                 */
1854                 
1855                 if (result.beats == 1) {
1856                         
1857                         /* move to previous bar, last beat */
1858                         
1859                         if (result.bars <= 1) {
1860                                 /* i'm sorry dave, i can't do that */
1861                                 throw std::out_of_range ("illegal BBT subtraction");
1862                         }
1863                         
1864                         result.bars--;
1865                         result.beats = meter->beats_per_bar();
1866                 } else {
1867
1868                         /* back one beat */
1869
1870                         result.beats--;
1871                 }
1872                 
1873                 /* one down ... */
1874                 op.beats--;
1875                 
1876                 /* check if we need to use a new meter section: has subtracting beats to result taken us 
1877                    to before the start of the current meter section? in which case, use the prior one.
1878                 */
1879
1880                 if (result < meter->start() && next_meter != meter_sections.rend()) {
1881                         meter = *next_meter;
1882                         ++next_meter;
1883                 }
1884         }
1885
1886         /* finally, subtract bars */
1887
1888         if (op.bars >= result.bars) {
1889                 /* i'm sorry dave, i can't do that */
1890                 throw std::out_of_range ("illegal BBT subtraction");
1891         }
1892
1893         result.bars -= op.bars;
1894         return result;
1895 }