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