remove Glib::ustring from libardour; allow any characters except '/' and '\' in paths...
[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         BBT_Time the_beat;
1135         uint32_t ticks_one_half_subdivisions_worth;
1136         uint32_t ticks_one_subdivisions_worth;
1137         uint32_t difference;
1138
1139         bbt_time(fr, the_beat);
1140
1141         ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
1142         ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
1143
1144         if (dir > 0) {
1145
1146                 /* round to next */
1147
1148                 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1149
1150                 if (mod == 0) {
1151                         /* right on the subdivision, so the difference is just the subdivision ticks */
1152                         difference = ticks_one_subdivisions_worth;
1153
1154                 } else {
1155                         /* not on subdivision, compute distance to next subdivision */
1156
1157                         difference = ticks_one_subdivisions_worth - mod;
1158                 }
1159
1160                 the_beat = bbt_add (the_beat, BBT_Time (0, 0, difference));
1161
1162         } else if (dir < 0) {
1163
1164                 /* round to previous */
1165
1166                 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1167
1168                 if (mod == 0) {
1169                         /* right on the subdivision, so the difference is just the subdivision ticks */
1170                         difference = ticks_one_subdivisions_worth;
1171                 } else {
1172                         /* not on subdivision, compute distance to previous subdivision, which
1173                            is just the modulus.
1174                         */
1175
1176                         difference = mod;
1177                 }
1178
1179                 try { 
1180                         the_beat = bbt_subtract (the_beat, BBT_Time (0, 0, difference));
1181                 } catch (...) {
1182                         /* can't go backwards from wherever pos is, so just return it */
1183                         return fr;
1184                 }
1185                         
1186
1187         } else {
1188                 /* round to nearest */
1189
1190                 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1191                         difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1192                         the_beat = bbt_add (the_beat, BBT_Time (0, 0, difference));
1193                 } else {
1194                         // difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1195                         the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1196                 }
1197         }
1198
1199         return frame_time (the_beat);
1200 }
1201
1202 nframes64_t
1203 TempoMap::round_to_type (nframes64_t frame, int dir, BBTPointType type)
1204 {
1205         TempoMetric metric = metric_at (frame);
1206         BBT_Time bbt;
1207         BBT_Time start;
1208         BBT_Time one_bar (1,0,0);
1209         BBT_Time one_beat (0,1,0);
1210
1211         bbt_time_with_metric (frame, bbt, metric);
1212
1213         switch (type) {
1214         case Bar:
1215                 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to bars in direction %2\n", frame, dir, bbt));
1216
1217                 if (dir < 0) {
1218
1219                         /* find bar position preceding frame */
1220
1221                         try {
1222                                 bbt = bbt_subtract (bbt, one_bar);
1223                         }
1224
1225                         catch (...) {
1226                                 return frame;
1227                         }
1228
1229
1230                 } else if (dir > 0) {
1231
1232                         /* find bar position following frame */
1233
1234                         try {
1235                                 bbt = bbt_add (bbt, one_bar, metric);
1236                         }
1237                         catch (...) {
1238                                 return frame;
1239                         }
1240
1241                 } else {
1242
1243                         /* "true" rounding */
1244
1245                         float midbar_beats;
1246                         float midbar_ticks;
1247
1248                         midbar_beats = metric.meter().beats_per_bar() / 2 + 1;
1249                         midbar_ticks = Meter::ticks_per_beat * fmod (midbar_beats, 1.0f);
1250                         midbar_beats = floor (midbar_beats);
1251                         
1252                         BBT_Time midbar (bbt.bars, lrintf (midbar_beats), lrintf (midbar_ticks));
1253
1254                         if (bbt < midbar) {
1255                                 /* round down */
1256                                 bbt.beats = 1;
1257                                 bbt.ticks = 0;
1258                         } else {
1259                                 /* round up */
1260                                 bbt.bars++;
1261                                 bbt.beats = 1;
1262                                 bbt.ticks = 0;
1263                         }
1264                 }
1265                 /* force beats & ticks to their values at the start of a bar */
1266                 bbt.beats = 1;
1267                 bbt.ticks = 0;
1268                 break;
1269
1270         case Beat:
1271                 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to beat in direction %2\n", frame, (dir < 0 ? "back" : "forward"), bbt));
1272
1273                 if (dir < 0) {
1274
1275                         /* find beat position preceding frame */
1276
1277                         try {
1278                                 bbt = bbt_subtract (bbt, one_beat); 
1279                         }
1280
1281                         catch (...) {
1282                                 return frame;
1283                         }
1284
1285
1286                 } else if (dir > 0) {
1287
1288                         /* find beat position following frame */
1289
1290                         try {
1291                                 bbt = bbt_add (bbt, one_beat, metric);
1292                         }
1293                         catch (...) {
1294                                 return frame;
1295                         }
1296
1297                 } else {
1298
1299                         /* "true" rounding */
1300
1301                         /* round to nearest beat */
1302                         if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1303
1304                                 try {
1305                                         bbt = bbt_add (bbt, one_beat, metric);
1306                                 }
1307                                 catch (...) {
1308                                         return frame;
1309                                 }
1310                         }
1311                 }
1312                 /* force ticks to the value at the start of a beat */
1313                 bbt.ticks = 0;
1314                 break;
1315
1316         }
1317
1318         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)));
1319         return metric.frame() + count_frames_between (metric.start(), bbt);
1320 }
1321
1322 TempoMap::BBTPointList *
1323 TempoMap::get_points (nframes64_t lower, nframes64_t upper) const
1324 {
1325
1326         Metrics::const_iterator i;
1327         BBTPointList *points;
1328         double current;
1329         const MeterSection* meter;
1330         const MeterSection* m;
1331         const TempoSection* tempo;
1332         const TempoSection* t;
1333         uint32_t bar;
1334         uint32_t beat;
1335         double beats_per_bar;
1336         double beat_frame;
1337         double beat_frames;
1338         double frames_per_bar;
1339         double delta_bars;
1340         double delta_beats;
1341         double dummy;
1342         nframes64_t limit;
1343
1344         meter = &first_meter ();
1345         tempo = &first_tempo ();
1346
1347         /* find the starting point */
1348
1349         for (i = metrics->begin(); i != metrics->end(); ++i) {
1350
1351                 if ((*i)->frame() > lower) {
1352                         break;
1353                 }
1354
1355                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1356                         tempo = t;
1357                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1358                         meter = m;
1359                 }
1360         }
1361
1362         /* We now have:
1363
1364            meter -> the Meter for "lower"
1365            tempo -> the Tempo for "lower"
1366            i     -> for first new metric after "lower", possibly metrics->end()
1367
1368            Now start generating points.
1369         */
1370
1371         beats_per_bar = meter->beats_per_bar ();
1372         frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1373         beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1374
1375         if (meter->frame() > tempo->frame()) {
1376                 bar = meter->start().bars;
1377                 beat = meter->start().beats;
1378                 current = meter->frame();
1379         } else {
1380                 bar = tempo->start().bars;
1381                 beat = tempo->start().beats;
1382                 current = tempo->frame();
1383         }
1384
1385         /* initialize current to point to the bar/beat just prior to the
1386            lower frame bound passed in.  assumes that current is initialized
1387            above to be on a beat.
1388         */
1389
1390         delta_bars = (lower-current) / frames_per_bar;
1391         delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1392         current += (floor(delta_bars) * frames_per_bar) +  (floor(delta_beats) * beat_frames);
1393
1394         // adjust bars and beats too
1395         bar += (uint32_t) (floor(delta_bars));
1396         beat += (uint32_t) (floor(delta_beats));
1397
1398         points = new BBTPointList;
1399
1400         do {
1401
1402                 if (i == metrics->end()) {
1403                         limit = upper;
1404                         // cerr << "== limit set to end of request @ " << limit << endl;
1405                 } else {
1406                         // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
1407                         limit = (*i)->frame();
1408                 }
1409
1410                 limit = min (limit, upper);
1411
1412                 while (current < limit) {
1413
1414                         /* if we're at the start of a bar, add bar point */
1415
1416                         if (beat == 1) {
1417                                 if (current >= lower) {
1418                                         // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1419                                         points->push_back (BBTPoint (*meter, *tempo,(nframes64_t)rint(current), Bar, bar, 1));
1420
1421                                 }
1422                         }
1423
1424                         /* add some beats if we can */
1425
1426                         beat_frame = current;
1427
1428                         while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1429                                 if (beat_frame >= lower) {
1430                                         // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1431                                         points->push_back (BBTPoint (*meter, *tempo, (nframes64_t) rint(beat_frame), Beat, bar, beat));
1432                                 }
1433                                 beat_frame += beat_frames;
1434                                 current+= beat_frames;
1435
1436                                 beat++;
1437                         }
1438
1439                         //  cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
1440                         // << (beat > ceil(beats_per_bar))
1441                         // << endl;
1442
1443                         if (beat > ceil(beats_per_bar) || i != metrics->end()) {
1444
1445                                 /* we walked an entire bar. its
1446                                    important to move `current' forward
1447                                    by the actual frames_per_bar, not move it to
1448                                    an integral beat_frame, so that metrics with
1449                                    non-integral beats-per-bar have
1450                                    their bar positions set
1451                                    correctly. consider a metric with
1452                                    9-1/2 beats-per-bar. the bar we
1453                                    just filled had  10 beat marks,
1454                                    but the bar end is 1/2 beat before
1455                                    the last beat mark.
1456                                    And it is also possible that a tempo
1457                                    change occured in the middle of a bar,
1458                                    so we subtract the possible extra fraction from the current
1459                                 */
1460
1461                                 if (beat > ceil (beats_per_bar)) {
1462                                         /* next bar goes where the numbers suggest */
1463                                         current -=  beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1464                                         // cerr << "++ next bar from numbers\n";
1465                                 } else {
1466                                         /* next bar goes where the next metric is */
1467                                         current = limit;
1468                                         // cerr << "++ next bar at next metric\n";
1469                                 }
1470                                 bar++;
1471                                 beat = 1;
1472                         }
1473
1474                 }
1475
1476                 /* if we're done, then we're done */
1477
1478                 if (current >= upper) {
1479                         break;
1480                 }
1481
1482                 /* i is an iterator that refers to the next metric (or none).
1483                    if there is a next metric, move to it, and continue.
1484                 */
1485
1486                 if (i != metrics->end()) {
1487
1488                         if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1489                                 tempo = t;
1490                         } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1491                                 meter = m;
1492                                 /* new MeterSection, beat always returns to 1 */
1493                                 beat = 1;
1494                         }
1495
1496                         current = (*i)->frame ();
1497                         // cerr << "loop around with current @ " << current << endl;
1498
1499                         beats_per_bar = meter->beats_per_bar ();
1500                         frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1501                         beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1502
1503                         ++i;
1504                 }
1505
1506         } while (1);
1507
1508         return points;
1509 }
1510
1511 const TempoSection&
1512 TempoMap::tempo_section_at (nframes64_t frame)
1513 {
1514         Glib::RWLock::ReaderLock lm (lock);
1515         Metrics::iterator i;
1516         TempoSection* prev = 0;
1517
1518         for (i = metrics->begin(); i != metrics->end(); ++i) {
1519                 TempoSection* t;
1520
1521                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1522
1523                         if ((*i)->frame() > frame) {
1524                                 break;
1525                         }
1526
1527                         prev = t;
1528                 }
1529         }
1530
1531         if (prev == 0) {
1532                 fatal << endmsg;
1533         }
1534
1535         return *prev;
1536 }
1537
1538 const Tempo&
1539 TempoMap::tempo_at (nframes64_t frame) const
1540 {
1541         TempoMetric m (metric_at (frame));
1542         return m.tempo();
1543 }
1544
1545
1546 const Meter&
1547 TempoMap::meter_at (nframes64_t frame) const
1548 {
1549         TempoMetric m (metric_at (frame));
1550         return m.meter();
1551 }
1552
1553 XMLNode&
1554 TempoMap::get_state ()
1555 {
1556         Metrics::const_iterator i;
1557         XMLNode *root = new XMLNode ("TempoMap");
1558
1559         {
1560                 Glib::RWLock::ReaderLock lm (lock);
1561                 for (i = metrics->begin(); i != metrics->end(); ++i) {
1562                         root->add_child_nocopy ((*i)->get_state());
1563                 }
1564         }
1565
1566         return *root;
1567 }
1568
1569 int
1570 TempoMap::set_state (const XMLNode& node, int /*version*/)
1571 {
1572         {
1573                 Glib::RWLock::WriterLock lm (lock);
1574
1575                 XMLNodeList nlist;
1576                 XMLNodeConstIterator niter;
1577                 Metrics old_metrics (*metrics);
1578
1579                 metrics->clear();
1580
1581                 nlist = node.children();
1582
1583                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1584                         XMLNode* child = *niter;
1585
1586                         if (child->name() == TempoSection::xml_state_node_name) {
1587
1588                                 try {
1589                                         metrics->push_back (new TempoSection (*child));
1590                                 }
1591
1592                                 catch (failed_constructor& err){
1593                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1594                                         *metrics = old_metrics;
1595                                         break;
1596                                 }
1597
1598                         } else if (child->name() == MeterSection::xml_state_node_name) {
1599
1600                                 try {
1601                                         metrics->push_back (new MeterSection (*child));
1602                                 }
1603
1604                                 catch (failed_constructor& err) {
1605                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1606                                         *metrics = old_metrics;
1607                                         break;
1608                                 }
1609                         }
1610                 }
1611
1612                 if (niter == nlist.end()) {
1613
1614                         MetricSectionSorter cmp;
1615                         metrics->sort (cmp);
1616                         timestamp_metrics (true);
1617                 }
1618         }
1619
1620         PropertyChanged (PropertyChange ());
1621
1622         return 0;
1623 }
1624
1625 void
1626 TempoMap::dump (std::ostream& o) const
1627 {
1628         const MeterSection* m;
1629         const TempoSection* t;
1630
1631         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1632
1633                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1634                         o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1635                           << t->movable() << ')' << endl;
1636                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1637                         o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1638                           << " (move? " << m->movable() << ')' << endl;
1639                 }
1640         }
1641 }
1642
1643 int
1644 TempoMap::n_tempos() const
1645 {
1646         Glib::RWLock::ReaderLock lm (lock);
1647         int cnt = 0;
1648
1649         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1650                 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1651                         cnt++;
1652                 }
1653         }
1654
1655         return cnt;
1656 }
1657
1658 int
1659 TempoMap::n_meters() const
1660 {
1661         Glib::RWLock::ReaderLock lm (lock);
1662         int cnt = 0;
1663
1664         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1665                 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1666                         cnt++;
1667                 }
1668         }
1669
1670         return cnt;
1671 }
1672
1673 void
1674 TempoMap::insert_time (nframes64_t where, nframes64_t amount)
1675 {
1676         for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1677                 if ((*i)->frame() >= where) {
1678                         (*i)->set_frame ((*i)->frame() + amount);
1679                 }
1680         }
1681
1682         timestamp_metrics (false);
1683
1684         PropertyChanged (PropertyChange ());
1685 }
1686
1687 BBT_Time
1688 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& other) const
1689 {
1690         TempoMetric metric =  metric_at (start);
1691         return bbt_add (start, other, metric);
1692 }
1693
1694 /**
1695  * add the BBT interval @param increment to  @param start and return the result
1696  */
1697 BBT_Time
1698 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& increment, const TempoMetric& /*metric*/) const
1699 {
1700         BBT_Time result = start;
1701         BBT_Time op = increment; /* argument is const, but we need to modify it */
1702         uint32_t ticks = result.ticks + op.ticks;
1703
1704         if (ticks >= Meter::ticks_per_beat) {
1705                 op.beats++;
1706                 result.ticks = ticks % (uint32_t) Meter::ticks_per_beat;
1707         } else {
1708                 result.ticks += op.ticks;
1709         }
1710
1711         /* now comes the complicated part. we have to add one beat a time,
1712            checking for a new metric on every beat.
1713         */
1714         
1715         /* grab all meter sections */
1716         
1717         list<const MeterSection*> meter_sections;
1718         
1719         for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1720                 const MeterSection* ms;
1721                 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1722                         meter_sections.push_back (ms);
1723                 }
1724         }
1725         
1726         assert (!meter_sections.empty());
1727         
1728         list<const MeterSection*>::const_iterator next_meter;
1729         const Meter* meter = 0;
1730         
1731         /* go forwards through the meter sections till we get to the one
1732            covering the current value of result. this positions i to point to 
1733            the next meter section too, or the end.
1734         */
1735         
1736         for (next_meter = meter_sections.begin(); next_meter != meter_sections.end(); ++next_meter) {
1737                 
1738                 if (result < (*next_meter)->start()) {
1739                         /* this metric is past the result time. stop looking, we have what we need */
1740                         break;
1741                 }
1742
1743                 if (result == (*next_meter)->start()) {
1744                         /* this meter section starts at result, push i beyond it so that it points
1745                            to the NEXT section, opwise we will get stuck later, and use this meter section.
1746                         */
1747                         meter = *next_meter;
1748                         ++next_meter;
1749                         break;
1750                 }
1751                 
1752                 meter = *next_meter;
1753         }
1754         
1755         assert (meter != 0);
1756                 
1757         /* OK, now have the meter for the bar start we are on, and i is an iterator 
1758            that points to the metric after the one we are currently dealing with 
1759            (or to metrics->end(), of course) 
1760         */
1761         
1762         while (op.beats) {
1763                 
1764                 /* given the current meter, have we gone past the end of the bar ? */
1765                 
1766                 if (result.beats >= meter->beats_per_bar()) {
1767                         /* move to next bar, first beat */
1768                         result.bars++;
1769                         result.beats = 1;
1770                 } else {
1771                         result.beats++;
1772                 }
1773                 
1774                 /* one down ... */
1775                 
1776                 op.beats--;
1777                 
1778                 /* check if we need to use a new meter section: has adding beats to result taken us 
1779                    to or after the start of the next meter section? in which case, use it.
1780                 */
1781
1782                 if (next_meter != meter_sections.end() && (((*next_meter)->start () < result) || (result == (*next_meter)->start()))) {
1783                         meter = *next_meter;
1784                         ++next_meter;
1785                 }
1786         }
1787
1788         /* finally, add bars */
1789
1790         result.bars += op.bars++;
1791
1792         return result;
1793 }
1794
1795 /**
1796  * subtract the BBT interval @param decrement from @param start and return the result
1797  */
1798 BBT_Time
1799 TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const
1800 {
1801         BBT_Time result = start;
1802         BBT_Time op = decrement; /* argument is const, but we need to modify it */
1803
1804         if (op.ticks > result.ticks) {
1805                 /* subtract an extra beat later; meanwhile set ticks to the right "carry" value */
1806                 op.beats++;
1807                 result.ticks = Meter::ticks_per_beat - (op.ticks - result.ticks);
1808         } else {
1809                 result.ticks -= op.ticks;
1810         }
1811
1812         /* now comes the complicated part. we have to subtract one beat a time,
1813            checking for a new metric on every beat.
1814         */
1815         
1816         /* grab all meter sections */
1817         
1818         list<const MeterSection*> meter_sections;
1819         
1820         for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1821                 const MeterSection* ms;
1822                 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1823                         meter_sections.push_back (ms);
1824                 }
1825                 }
1826         
1827         assert (!meter_sections.empty());
1828         
1829         /* go backwards through the meter sections till we get to the one
1830            covering the current value of result. this positions i to point to 
1831            the next (previous) meter section too, or the end.
1832         */
1833         
1834         const MeterSection* meter = 0;
1835         list<const MeterSection*>::reverse_iterator next_meter; // older versions of GCC don't 
1836                                                                 // support const_reverse_iterator::operator!=()
1837         
1838         for (next_meter = meter_sections.rbegin(); next_meter != meter_sections.rend(); ++next_meter) {
1839                 
1840                 /* when we find the first meter section that is before or at result, use it,
1841                    and set next_meter to the previous one 
1842                 */
1843                 
1844                 if ((*next_meter)->start() < result || (*next_meter)->start() == result) {
1845                         meter = *next_meter;
1846                         ++next_meter;
1847                         break;
1848                 }
1849         }
1850
1851         assert (meter != 0);
1852         
1853         /* OK, now have the meter for the bar start we are on, and i is an iterator 
1854            that points to the metric after the one we are currently dealing with 
1855            (or to metrics->end(), of course) 
1856         */
1857         
1858         while (op.beats) {
1859
1860                 /* have we reached the start of the bar? if so, move to the last beat of the previous
1861                    bar. opwise, just step back 1 beat.
1862                 */
1863                 
1864                 if (result.beats == 1) {
1865                         
1866                         /* move to previous bar, last beat */
1867                         
1868                         if (result.bars <= 1) {
1869                                 /* i'm sorry dave, i can't do that */
1870                                 throw std::out_of_range ("illegal BBT subtraction");
1871                         }
1872                         
1873                         result.bars--;
1874                         result.beats = meter->beats_per_bar();
1875                 } else {
1876
1877                         /* back one beat */
1878
1879                         result.beats--;
1880                 }
1881                 
1882                 /* one down ... */
1883                 op.beats--;
1884                 
1885                 /* check if we need to use a new meter section: has subtracting beats to result taken us 
1886                    to before the start of the current meter section? in which case, use the prior one.
1887                 */
1888
1889                 if (result < meter->start() && next_meter != meter_sections.rend()) {
1890                         meter = *next_meter;
1891                         ++next_meter;
1892                 }
1893         }
1894
1895         /* finally, subtract bars */
1896
1897         if (op.bars >= result.bars) {
1898                 /* i'm sorry dave, i can't do that */
1899                 throw std::out_of_range ("illegal BBT subtraction");
1900         }
1901
1902         result.bars -= op.bars;
1903         return result;
1904 }
1905
1906 /** Compare the time of this with that of another MetricSection.
1907  *  @param with_bbt True to compare using ::start(), false to use ::frame().
1908  *  @return -1 for less than, 0 for equal, 1 for greater than.
1909  */
1910
1911 int
1912 MetricSection::compare (MetricSection* other, bool with_bbt) const
1913 {
1914         if (with_bbt) {
1915                 if (start() == other->start()) {
1916                         return 0;
1917                 } else if (start() < other->start()) {
1918                         return -1;
1919                 } else {
1920                         return 1;
1921                 }
1922         } else {
1923                 if (frame() == other->frame()) {
1924                         return 0;
1925                 } else if (frame() < other->frame()) {
1926                         return -1;
1927                 } else {
1928                         return 1;
1929                 }
1930         }
1931
1932         /* NOTREACHED */
1933         return 0;
1934 }