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