Remove unnecessary 0 checks before delete; see http://www.parashift.com/c++-faq-lite...
[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 (prop->value() == "yes");
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 (prop->value() == "yes");
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
1103 TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
1104 {
1105
1106         BBT_Time the_beat;
1107         uint32_t ticks_one_half_subdivisions_worth;
1108         uint32_t ticks_one_subdivisions_worth;
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 (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1116           uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1117           if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
1118             the_beat.beats++;
1119             the_beat.ticks += difference;
1120             the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
1121           } else {  
1122             the_beat.ticks += difference;
1123           }
1124         } else {
1125           the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1126         }
1127
1128         return frame_time (the_beat);
1129 }
1130
1131 nframes_t
1132 TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
1133 {
1134         Metric metric = metric_at (frame);
1135         BBT_Time bbt;
1136         BBT_Time start;
1137         bbt_time_with_metric (frame, bbt, metric);
1138
1139         switch (type) {
1140         case Bar:
1141                 if (dir < 0) {
1142                         /* relax */
1143                 } else if (dir > 0) {
1144                         if (bbt.beats > 0) {
1145                                 bbt.bars++;
1146                         } else if (metric.frame() < frame) {
1147                                 bbt.bars++;
1148                         }
1149                 } else {
1150                         if (bbt.beats > metric.meter().beats_per_bar()/2) {
1151                                 bbt.bars++;
1152                         }
1153                 }
1154                 bbt.beats = 1;
1155                 bbt.ticks = 0;
1156                 break;
1157         
1158         case Beat:
1159                 if (dir < 0) {
1160                         /* relax */
1161                 } else if (dir > 0) {
1162                         if (bbt.ticks > 0) {
1163                                 bbt.beats++;
1164                         } else if (metric.frame() < frame) {
1165                                 bbt.beats++;
1166                         }
1167                 } else {
1168                         if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1169                                 bbt.beats++;
1170                         }
1171                 }
1172                 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1173                         bbt.beats = 1;
1174                         bbt.bars++;
1175                 }
1176                 bbt.ticks = 0;
1177                 break;
1178         
1179         }
1180         
1181         /* 
1182         cerr << "for " << frame << " round to " << bbt << " using "
1183              << metric.start()
1184              << endl;
1185         */
1186         return metric.frame() + count_frames_between (metric.start(), bbt);
1187 }
1188
1189 TempoMap::BBTPointList *
1190 TempoMap::get_points (nframes_t lower, nframes_t upper) const
1191 {
1192
1193         Metrics::const_iterator i;
1194         BBTPointList *points;
1195         double current;
1196         const MeterSection* meter;
1197         const MeterSection* m;
1198         const TempoSection* tempo;
1199         const TempoSection* t;
1200         uint32_t bar;
1201         uint32_t beat;
1202         double beats_per_bar;
1203         double beat_frame;
1204         double beat_frames;
1205         double frames_per_bar;
1206         double delta_bars;
1207         double delta_beats;
1208         double dummy;
1209         nframes_t limit;
1210
1211         meter = &first_meter ();
1212         tempo = &first_tempo ();
1213
1214         /* find the starting point */
1215
1216         for (i = metrics->begin(); i != metrics->end(); ++i) {
1217
1218                 if ((*i)->frame() > lower) {
1219                         break;
1220                 }
1221
1222                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1223                         tempo = t;
1224                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1225                         meter = m;
1226                 }
1227         }
1228
1229         /* We now have:
1230            
1231            meter -> the Meter for "lower"
1232            tempo -> the Tempo for "lower"
1233            i     -> for first new metric after "lower", possibly metrics->end()
1234
1235            Now start generating points.
1236         */
1237
1238         beats_per_bar = meter->beats_per_bar ();
1239         frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1240         beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1241         
1242         if (meter->frame() > tempo->frame()) {
1243                 bar = meter->start().bars;
1244                 beat = meter->start().beats;
1245                 current = meter->frame();
1246         } else {
1247                 bar = tempo->start().bars;
1248                 beat = tempo->start().beats;
1249                 current = tempo->frame();
1250         }
1251
1252         /* initialize current to point to the bar/beat just prior to the
1253            lower frame bound passed in.  assumes that current is initialized
1254            above to be on a beat.
1255         */
1256         
1257         delta_bars = (lower-current) / frames_per_bar;
1258         delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1259         current += (floor(delta_bars) * frames_per_bar) +  (floor(delta_beats) * beat_frames);
1260
1261         // adjust bars and beats too
1262         bar += (uint32_t) (floor(delta_bars));
1263         beat += (uint32_t) (floor(delta_beats));
1264
1265         points = new BBTPointList;
1266                 
1267         do {
1268
1269                 if (i == metrics->end()) {
1270                         limit = upper;
1271                         // cerr << "== limit set to end of request @ " << limit << endl;
1272                 } else {
1273                         // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
1274                         limit = (*i)->frame();
1275                 }
1276
1277                 limit = min (limit, upper);
1278
1279                 while (current < limit) {
1280                         
1281                         /* if we're at the start of a bar, add bar point */
1282
1283                         if (beat == 1) {
1284                                 if (current >= lower) {
1285                                         // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1286                                         points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1));
1287
1288                                 }
1289                         }
1290
1291                         /* add some beats if we can */
1292
1293                         beat_frame = current;
1294
1295                         while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1296                                 if (beat_frame >= lower) {
1297                                         // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1298                                         points->push_back (BBTPoint (*meter, *tempo, (nframes_t) rint(beat_frame), Beat, bar, beat));
1299                                 }
1300                                 beat_frame += beat_frames;
1301                                 current+= beat_frames;
1302                                
1303                                 beat++;
1304                         }
1305
1306                         //  cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
1307                         // << (beat > ceil(beats_per_bar))
1308                         // << endl;
1309
1310                         if (beat > ceil(beats_per_bar) || i != metrics->end()) {
1311
1312                                 /* we walked an entire bar. its
1313                                    important to move `current' forward
1314                                    by the actual frames_per_bar, not move it to
1315                                    an integral beat_frame, so that metrics with
1316                                    non-integral beats-per-bar have
1317                                    their bar positions set
1318                                    correctly. consider a metric with
1319                                    9-1/2 beats-per-bar. the bar we
1320                                    just filled had  10 beat marks,
1321                                    but the bar end is 1/2 beat before
1322                                    the last beat mark.
1323                                    And it is also possible that a tempo 
1324                                    change occured in the middle of a bar, 
1325                                    so we subtract the possible extra fraction from the current
1326                                 */
1327
1328                                 if (beat > ceil (beats_per_bar)) {
1329                                         /* next bar goes where the numbers suggest */
1330                                         current -=  beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1331                                         // cerr << "++ next bar from numbers\n";
1332                                 } else {
1333                                         /* next bar goes where the next metric is */
1334                                         current = limit;
1335                                         // cerr << "++ next bar at next metric\n";
1336                                 }
1337                                 bar++;
1338                                 beat = 1;
1339                         } 
1340                 
1341                 }
1342
1343                 /* if we're done, then we're done */
1344
1345                 if (current >= upper) {
1346                         break;
1347                 }
1348
1349                 /* i is an iterator that refers to the next metric (or none).
1350                    if there is a next metric, move to it, and continue.
1351                 */
1352
1353                 if (i != metrics->end()) {
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                                 /* new MeterSection, beat always returns to 1 */
1360                                 beat = 1;
1361                         }
1362
1363                         current = (*i)->frame ();
1364                         // cerr << "loop around with current @ " << current << endl;
1365
1366                         beats_per_bar = meter->beats_per_bar ();
1367                         frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1368                         beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1369                         
1370                         ++i;
1371                 }
1372
1373         } while (1);
1374
1375         return points;
1376 }       
1377
1378 const TempoSection&
1379 TempoMap::tempo_section_at (nframes_t frame)
1380 {
1381         Glib::RWLock::ReaderLock lm (lock);
1382         Metrics::iterator i;
1383         TempoSection* prev = 0;
1384         
1385         for (i = metrics->begin(); i != metrics->end(); ++i) {
1386                 TempoSection* t;
1387
1388                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1389
1390                         if ((*i)->frame() > frame) {
1391                                 break;
1392                         }
1393
1394                         prev = t;
1395                 }
1396         }
1397
1398         if (prev == 0) {
1399                 fatal << endmsg;
1400         }
1401
1402         return *prev;
1403 }
1404
1405 const Tempo&
1406 TempoMap::tempo_at (nframes_t frame)
1407 {
1408         Metric m (metric_at (frame));
1409         return m.tempo();
1410 }
1411
1412
1413 const Meter&
1414 TempoMap::meter_at (nframes_t frame)
1415 {
1416         Metric m (metric_at (frame));
1417         return m.meter();
1418 }
1419
1420 XMLNode&
1421 TempoMap::get_state ()
1422 {
1423         Metrics::const_iterator i;
1424         XMLNode *root = new XMLNode ("TempoMap");
1425
1426         {
1427                 Glib::RWLock::ReaderLock lm (lock);
1428                 for (i = metrics->begin(); i != metrics->end(); ++i) {
1429                         root->add_child_nocopy ((*i)->get_state());
1430                 }
1431         }
1432
1433         return *root;
1434 }
1435
1436 int
1437 TempoMap::set_state (const XMLNode& node)
1438 {
1439         {
1440                 Glib::RWLock::WriterLock lm (lock);
1441
1442                 XMLNodeList nlist;
1443                 XMLNodeConstIterator niter;
1444                 Metrics old_metrics (*metrics);
1445                 
1446                 metrics->clear();
1447
1448                 nlist = node.children();
1449                 
1450                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1451                         XMLNode* child = *niter;
1452                         
1453                         if (child->name() == TempoSection::xml_state_node_name) {
1454                                 
1455                                 try {
1456                                         metrics->push_back (new TempoSection (*child));
1457                                 }
1458                                 
1459                                 catch (failed_constructor& err){
1460                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1461                                         *metrics = old_metrics;
1462                                         break;
1463                                 }
1464                                 
1465                         } else if (child->name() == MeterSection::xml_state_node_name) {
1466                                 
1467                                 try {
1468                                         metrics->push_back (new MeterSection (*child));
1469                                 }
1470                                 
1471                                 catch (failed_constructor& err) {
1472                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1473                                         *metrics = old_metrics;
1474                                         break;
1475                                 }
1476                         }
1477                 }
1478                 
1479                 if (niter == nlist.end()) {
1480                         
1481                         MetricSectionSorter cmp;
1482                         metrics->sort (cmp);
1483                         timestamp_metrics (true);
1484                 }
1485         }
1486         
1487         StateChanged (Change (0));
1488
1489         return 0;
1490 }
1491
1492 void
1493 TempoMap::dump (std::ostream& o) const
1494 {
1495         const MeterSection* m;
1496         const TempoSection* t;
1497         
1498         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1499
1500                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1501                         o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1502                           << t->movable() << ')' << endl;
1503                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1504                         o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame() 
1505                           << " (move? " << m->movable() << ')' << endl;
1506                 }
1507         }
1508 }
1509
1510 int
1511 TempoMap::n_tempos() const
1512 {
1513         Glib::RWLock::ReaderLock lm (lock);
1514         int cnt = 0;
1515
1516         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1517                 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1518                         cnt++;
1519                 }
1520         }
1521
1522         return cnt;
1523 }
1524
1525 int
1526 TempoMap::n_meters() const
1527 {
1528         Glib::RWLock::ReaderLock lm (lock);
1529         int cnt = 0;
1530
1531         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1532                 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1533                         cnt++;
1534                 }
1535         }
1536
1537         return cnt;
1538 }