Apply MIDI looping patch from torbenh, with minor changes.
[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                         } else {
664                                 // metric will be at frames=0 bbt=1|1|0 by default
665                                 // which is correct for our purpose
666                         }
667                 
668                         bbt_time_with_metric ((*i)->frame(), bbt, metric);
669
670                         // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
671                         
672
673                         if (first) {
674                                 first = false;
675                         } else {
676                                 
677                                 if (bbt.ticks > Meter::ticks_per_beat/2) {
678                                         /* round up to next beat */
679                                         bbt.beats += 1;
680                                 } 
681
682                                 bbt.ticks = 0;
683
684                                 if (bbt.beats != 1) {
685                                         /* round up to next bar */
686                                         bbt.bars += 1;
687                                         bbt.beats = 1;
688                                 }
689                         }
690                         
691                         //s cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
692                         
693                         (*i)->set_start (bbt);
694
695                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
696                                 tempo = t;
697                                 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
698                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
699                                 meter = m;
700                                 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
701                         } else {
702                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
703                                 /*NOTREACHED*/
704                         }
705
706                         prev = (*i);
707                 }
708         }
709
710         // dump (cerr);
711         // cerr << "###############################################\n\n\n" << endl;
712
713 }
714
715 TempoMap::Metric
716 TempoMap::metric_at (nframes_t frame) const
717 {
718         Metric m (first_meter(), first_tempo());
719         const Meter* meter;
720         const Tempo* tempo;
721
722         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
723            at something, because we insert the default tempo and meter during
724            TempoMap construction.
725
726            now see if we can find better candidates.
727         */
728
729         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
730
731                 if ((*i)->frame() > frame) {
732                         break;
733                 }
734
735                 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
736                         m.set_tempo (*tempo);
737                 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
738                         m.set_meter (*meter);
739                 }
740
741                 m.set_frame ((*i)->frame ());
742                 m.set_start ((*i)->start ());
743         }
744         
745         return m;
746 }
747
748 TempoMap::Metric
749 TempoMap::metric_at (BBT_Time bbt) const
750 {
751         Metric m (first_meter(), first_tempo());
752         const Meter* meter;
753         const Tempo* tempo;
754
755         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
756            at something, because we insert the default tempo and meter during
757            TempoMap construction.
758
759            now see if we can find better candidates.
760         */
761
762         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
763
764                 BBT_Time section_start ((*i)->start());
765
766                 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
767                         break;
768                 }
769
770                 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
771                         m.set_tempo (*tempo);
772                 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
773                         m.set_meter (*meter);
774                 }
775                 
776                 m.set_frame ((*i)->frame ());
777                 m.set_start (section_start);
778         }
779
780         return m;
781 }
782
783 void
784 TempoMap::bbt_time (nframes_t frame, BBT_Time& bbt) const
785 {
786         {
787                 Glib::RWLock::ReaderLock lm (lock);
788                 bbt_time_unlocked (frame, bbt);
789         }
790 }
791
792 void
793 TempoMap::bbt_time_unlocked (nframes_t frame, BBT_Time& bbt) const
794 {
795         bbt_time_with_metric (frame, bbt, metric_at (frame));
796 }
797
798 void
799 TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& metric) const
800 {
801         nframes_t frame_diff;
802
803         uint32_t xtra_bars = 0;
804         double xtra_beats = 0;
805         double beats = 0;
806
807         // cerr << "---- BBT time for " << frame << " using metric @ " << metric.frame() << " BBT " << metric.start() << endl;
808
809         const double beats_per_bar = metric.meter().beats_per_bar();
810         const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
811         const double beat_frames = metric.tempo().frames_per_beat (_frame_rate, metric.meter());
812
813         /* now compute how far beyond that point we actually are. */
814
815         frame_diff = frame - metric.frame();
816         
817         // cerr << "----\tdelta = " << frame_diff << endl;
818
819         xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar);
820         frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar);
821         xtra_beats = (double) frame_diff / beat_frames;
822
823         // cerr << "---\tmeaning " << xtra_bars << " xtra bars and " << xtra_beats << " xtra beats\n";
824
825         /* and set the returned value */
826
827         /* and correct beat/bar shifts to match the meter.
828           remember: beat and bar counting is 1-based, 
829           not zero-based 
830           also the meter may contain a fraction
831         */
832         
833         bbt.bars = metric.start().bars + xtra_bars; 
834
835         beats = (double) metric.start().beats + xtra_beats;
836         
837         bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) );
838
839         beats = fmod(beats - 1, beats_per_bar )+ 1.0;
840         bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat));
841         bbt.beats = (uint32_t) floor(beats);
842         
843         // cerr << "-----\t RETURN " << bbt << endl;
844 }
845
846 nframes_t 
847 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
848 {
849         /* for this to work with fractional measure types, start and end have to be "legal" BBT types, 
850            that means that the beats and ticks should be inside a bar
851         */
852
853         nframes_t frames = 0;
854         nframes_t start_frame = 0;
855         nframes_t end_frame = 0;
856
857         Metric m = metric_at (start);
858
859         uint32_t bar_offset = start.bars - m.start().bars;
860
861         double  beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1) 
862                 + start.ticks/Meter::ticks_per_beat;
863
864
865         start_frame = m.frame() + (nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
866
867         m =  metric_at(end);
868
869         bar_offset = end.bars - m.start().bars;
870
871         beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1) 
872                 + end.ticks/Meter::ticks_per_beat;
873
874         end_frame = m.frame() + (nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
875
876         frames = end_frame - start_frame;
877
878         return frames;
879         
880 }       
881
882 nframes_t 
883 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
884 {
885         /* this is used in timestamping the metrics by actually counting the beats */ 
886
887         nframes_t frames = 0;
888         uint32_t bar = start.bars;
889         double beat = (double) start.beats;
890         double beats_counted = 0;
891         double beats_per_bar = 0;
892         double beat_frames = 0;
893
894         beats_per_bar = meter.beats_per_bar();
895         beat_frames = tempo.frames_per_beat (_frame_rate,meter);
896
897         frames = 0;
898
899         while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
900                 
901                 if (beat >= beats_per_bar) {
902                         beat = 1;
903                         ++bar;
904                         ++beats_counted;
905
906                         if (beat > beats_per_bar) {
907
908                                 /* this is a fractional beat at the end of a fractional bar
909                                    so it should only count for the fraction 
910                                 */
911
912                                 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
913                         }
914
915                 } else {
916                         ++beat;
917                         ++beats_counted;
918                 }
919         }
920
921         // cerr << "Counted " << beats_counted << " from " << start << " to " << end 
922         // << " bpb were " << beats_per_bar 
923         // << " fpb was " << beat_frames
924         // << endl;
925         
926         frames = (nframes_t) floor (beats_counted * beat_frames);
927
928         return frames;
929         
930 }       
931
932 nframes_t 
933 TempoMap::frame_time (const BBT_Time& bbt) const
934 {
935         BBT_Time start ; /* 1|1|0 */
936
937         return  count_frames_between ( start, bbt);
938 }
939
940 nframes_t 
941 TempoMap::bbt_duration_at (nframes_t pos, const BBT_Time& bbt, int dir) const
942 {
943         nframes_t frames = 0;
944
945         BBT_Time when;
946         bbt_time(pos,when);
947
948         {
949                 Glib::RWLock::ReaderLock lm (lock);
950                 frames = bbt_duration_at_unlocked (when, bbt,dir);
951         }
952
953         return frames;
954 }
955
956 nframes_t 
957 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
958 {
959
960         nframes_t frames = 0;
961
962         double beats_per_bar;
963         BBT_Time result;
964         
965         result.bars = max(1U,when.bars + dir * bbt.bars) ;
966         result.beats = 1;
967         result.ticks = 0;
968
969         Metric  metric = metric_at(result);
970         beats_per_bar = metric.meter().beats_per_bar();
971
972
973
974         /*reduce things to legal bbt  values 
975           we have to handle possible fractional=shorter beats at the end of measures
976           and things like 0|11|9000  as a duration in a 4.5/4 measure
977           the musical decision is that the fractional beat is also a beat , although a shorter one 
978         */
979
980     
981         if (dir >= 0) {
982                 result.beats = when.beats +  bbt.beats;
983                 result.ticks = when.ticks +  bbt.ticks;
984
985                 while (result.beats >= (beats_per_bar+1)) {
986                         result.bars++;
987                         result.beats -=  (uint32_t) ceil(beats_per_bar);
988                         metric = metric_at(result); // maybe there is a meter change
989                         beats_per_bar = metric.meter().beats_per_bar();
990                         
991                 }
992                 /*we now counted the beats and landed in the target measure, now deal with ticks 
993                   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
994                   and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
995                 */
996
997                 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
998                  */
999
1000                 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
1001                                         (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat 
1002                                            : Meter::ticks_per_beat );
1003
1004                 while (result.ticks >= ticks_at_beat) {
1005                         result.beats++;
1006                         result.ticks -= ticks_at_beat;
1007                         if  (result.beats >= (beats_per_bar+1)) {
1008                                 result.bars++;
1009                                 result.beats = 1;
1010                                 metric = metric_at(result); // maybe there is a meter change
1011                                 beats_per_bar = metric.meter().beats_per_bar();
1012                         }
1013                         ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
1014                                        (1 - (ceil(beats_per_bar) - beats_per_bar) )* Meter::ticks_per_beat 
1015                                        : Meter::ticks_per_beat);
1016
1017                 }
1018
1019           
1020         } else {
1021                 uint32_t b = bbt.beats;
1022
1023                 /* count beats */
1024                 while( b > when.beats ) {
1025                         
1026                         result.bars = max(1U,result.bars-- ) ;
1027                         metric = metric_at(result); // maybe there is a meter change
1028                         beats_per_bar = metric.meter().beats_per_bar();
1029                         if (b >= ceil(beats_per_bar)) {
1030                                 
1031                                 b -= (uint32_t) ceil(beats_per_bar);
1032                         } else {
1033                                 b = (uint32_t) ceil(beats_per_bar)- b + when.beats ;
1034                         }
1035                 }
1036                 result.beats = when.beats - b;
1037                 
1038                 /*count ticks */
1039
1040                 if (bbt.ticks <= when.ticks) {
1041                         result.ticks = when.ticks - bbt.ticks;
1042                 } else {
1043
1044                         uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
1045                         uint32_t t = bbt.ticks - when.ticks;
1046
1047                         do {
1048
1049                                 if (result.beats == 1) {
1050                                         result.bars = max(1U,result.bars-- ) ;
1051                                         metric = metric_at(result); // maybe there is a meter change
1052                                         beats_per_bar = metric.meter().beats_per_bar();
1053                                         result.beats = (uint32_t) ceil(beats_per_bar);
1054                                         ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat) ;
1055                                 } else {
1056                                         result.beats --;
1057                                         ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
1058                                 }
1059                                                 
1060                                 if (t <= ticks_at_beat) {
1061                                         result.ticks = ticks_at_beat - t; 
1062                                 } else {
1063                                         t-= ticks_at_beat;
1064                                 }
1065                         } while (t > ticks_at_beat);
1066
1067                 }
1068
1069
1070         }
1071
1072         if (dir < 0 ) {
1073                 frames = count_frames_between( result,when);
1074         } else {
1075                 frames = count_frames_between(when,result);
1076         }
1077
1078         return frames;
1079 }
1080
1081
1082
1083 nframes_t
1084 TempoMap::round_to_bar (nframes_t fr, int dir)
1085 {
1086         {
1087                 Glib::RWLock::ReaderLock lm (lock);
1088                 return round_to_type (fr, dir, Bar);
1089         }
1090 }
1091
1092
1093 nframes_t
1094 TempoMap::round_to_beat (nframes_t fr, int dir)
1095 {
1096         {
1097                 Glib::RWLock::ReaderLock lm (lock);
1098                 return round_to_type (fr, dir, Beat);
1099         }
1100 }
1101
1102 nframes_t
1103
1104 TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
1105 {
1106
1107         BBT_Time the_beat;
1108         uint32_t ticks_one_half_subdivisions_worth;
1109         uint32_t ticks_one_subdivisions_worth;
1110
1111         bbt_time(fr, the_beat);
1112
1113         ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
1114         ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
1115
1116         if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1117           uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1118           if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
1119             the_beat.beats++;
1120             the_beat.ticks += difference;
1121             the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
1122           } else {  
1123             the_beat.ticks += difference;
1124           }
1125         } else {
1126           the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1127         }
1128
1129         return frame_time (the_beat);
1130 }
1131
1132 nframes_t
1133 TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
1134 {
1135         Metric metric = metric_at (frame);
1136         BBT_Time bbt;
1137         BBT_Time start;
1138         bbt_time_with_metric (frame, bbt, metric);
1139
1140         switch (type) {
1141         case Bar:
1142                 if (dir < 0) {
1143                         /* relax */
1144                 } else if (dir > 0) {
1145                         if (bbt.beats > 0) {
1146                                 bbt.bars++;
1147                         } else if (metric.frame() < frame) {
1148                                 bbt.bars++;
1149                         }
1150                 } else {
1151                         if (bbt.beats > metric.meter().beats_per_bar()/2) {
1152                                 bbt.bars++;
1153                         }
1154                 }
1155                 bbt.beats = 1;
1156                 bbt.ticks = 0;
1157                 break;
1158         
1159         case Beat:
1160                 if (dir < 0) {
1161                         /* relax */
1162                 } else if (dir > 0) {
1163                         if (bbt.ticks > 0) {
1164                                 bbt.beats++;
1165                         } else if (metric.frame() < frame) {
1166                                 bbt.beats++;
1167                         }
1168                 } else {
1169                         if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1170                                 bbt.beats++;
1171                         }
1172                 }
1173                 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1174                         bbt.beats = 1;
1175                         bbt.bars++;
1176                 }
1177                 bbt.ticks = 0;
1178                 break;
1179         
1180         }
1181         
1182         /* 
1183         cerr << "for " << frame << " round to " << bbt << " using "
1184              << metric.start()
1185              << endl;
1186         */
1187         return metric.frame() + count_frames_between (metric.start(), bbt);
1188 }
1189
1190 TempoMap::BBTPointList *
1191 TempoMap::get_points (nframes_t lower, nframes_t upper) const
1192 {
1193
1194         Metrics::const_iterator i;
1195         BBTPointList *points;
1196         double current;
1197         const MeterSection* meter;
1198         const MeterSection* m;
1199         const TempoSection* tempo;
1200         const TempoSection* t;
1201         uint32_t bar;
1202         uint32_t beat;
1203         double beats_per_bar;
1204         double beat_frame;
1205         double beat_frames;
1206         double frames_per_bar;
1207         double delta_bars;
1208         double delta_beats;
1209         double dummy;
1210         nframes_t limit;
1211
1212         meter = &first_meter ();
1213         tempo = &first_tempo ();
1214
1215         /* find the starting point */
1216
1217         for (i = metrics->begin(); i != metrics->end(); ++i) {
1218
1219                 if ((*i)->frame() > lower) {
1220                         break;
1221                 }
1222
1223                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1224                         tempo = t;
1225                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1226                         meter = m;
1227                 }
1228         }
1229
1230         /* We now have:
1231            
1232            meter -> the Meter for "lower"
1233            tempo -> the Tempo for "lower"
1234            i     -> for first new metric after "lower", possibly metrics->end()
1235
1236            Now start generating points.
1237         */
1238
1239         beats_per_bar = meter->beats_per_bar ();
1240         frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1241         beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1242         
1243         if (meter->frame() > tempo->frame()) {
1244                 bar = meter->start().bars;
1245                 beat = meter->start().beats;
1246                 current = meter->frame();
1247         } else {
1248                 bar = tempo->start().bars;
1249                 beat = tempo->start().beats;
1250                 current = tempo->frame();
1251         }
1252
1253         /* initialize current to point to the bar/beat just prior to the
1254            lower frame bound passed in.  assumes that current is initialized
1255            above to be on a beat.
1256         */
1257         
1258         delta_bars = (lower-current) / frames_per_bar;
1259         delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1260         current += (floor(delta_bars) * frames_per_bar) +  (floor(delta_beats) * beat_frames);
1261
1262         // adjust bars and beats too
1263         bar += (uint32_t) (floor(delta_bars));
1264         beat += (uint32_t) (floor(delta_beats));
1265
1266         points = new BBTPointList;
1267                 
1268         do {
1269
1270                 if (i == metrics->end()) {
1271                         limit = upper;
1272                         // cerr << "== limit set to end of request @ " << limit << endl;
1273                 } else {
1274                         // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
1275                         limit = (*i)->frame();
1276                 }
1277
1278                 limit = min (limit, upper);
1279
1280                 while (current < limit) {
1281                         
1282                         /* if we're at the start of a bar, add bar point */
1283
1284                         if (beat == 1) {
1285                                 if (current >= lower) {
1286                                         // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1287                                         points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1));
1288
1289                                 }
1290                         }
1291
1292                         /* add some beats if we can */
1293
1294                         beat_frame = current;
1295
1296                         while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1297                                 if (beat_frame >= lower) {
1298                                         // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1299                                         points->push_back (BBTPoint (*meter, *tempo, (nframes_t) rint(beat_frame), Beat, bar, beat));
1300                                 }
1301                                 beat_frame += beat_frames;
1302                                 current+= beat_frames;
1303                                
1304                                 beat++;
1305                         }
1306
1307                         //  cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
1308                         // << (beat > ceil(beats_per_bar))
1309                         // << endl;
1310
1311                         if (beat > ceil(beats_per_bar) || i != metrics->end()) {
1312
1313                                 /* we walked an entire bar. its
1314                                    important to move `current' forward
1315                                    by the actual frames_per_bar, not move it to
1316                                    an integral beat_frame, so that metrics with
1317                                    non-integral beats-per-bar have
1318                                    their bar positions set
1319                                    correctly. consider a metric with
1320                                    9-1/2 beats-per-bar. the bar we
1321                                    just filled had  10 beat marks,
1322                                    but the bar end is 1/2 beat before
1323                                    the last beat mark.
1324                                    And it is also possible that a tempo 
1325                                    change occured in the middle of a bar, 
1326                                    so we subtract the possible extra fraction from the current
1327                                 */
1328
1329                                 if (beat > ceil (beats_per_bar)) {
1330                                         /* next bar goes where the numbers suggest */
1331                                         current -=  beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1332                                         // cerr << "++ next bar from numbers\n";
1333                                 } else {
1334                                         /* next bar goes where the next metric is */
1335                                         current = limit;
1336                                         // cerr << "++ next bar at next metric\n";
1337                                 }
1338                                 bar++;
1339                                 beat = 1;
1340                         } 
1341                 
1342                 }
1343
1344                 /* if we're done, then we're done */
1345
1346                 if (current >= upper) {
1347                         break;
1348                 }
1349
1350                 /* i is an iterator that refers to the next metric (or none).
1351                    if there is a next metric, move to it, and continue.
1352                 */
1353
1354                 if (i != metrics->end()) {
1355
1356                         if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1357                                 tempo = t;
1358                         } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1359                                 meter = m;
1360                                 /* new MeterSection, beat always returns to 1 */
1361                                 beat = 1;
1362                         }
1363
1364                         current = (*i)->frame ();
1365                         // cerr << "loop around with current @ " << current << endl;
1366
1367                         beats_per_bar = meter->beats_per_bar ();
1368                         frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1369                         beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1370                         
1371                         ++i;
1372                 }
1373
1374         } while (1);
1375
1376         return points;
1377 }       
1378
1379 const TempoSection&
1380 TempoMap::tempo_section_at (nframes_t frame)
1381 {
1382         Glib::RWLock::ReaderLock lm (lock);
1383         Metrics::iterator i;
1384         TempoSection* prev = 0;
1385         
1386         for (i = metrics->begin(); i != metrics->end(); ++i) {
1387                 TempoSection* t;
1388
1389                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1390
1391                         if ((*i)->frame() > frame) {
1392                                 break;
1393                         }
1394
1395                         prev = t;
1396                 }
1397         }
1398
1399         if (prev == 0) {
1400                 fatal << endmsg;
1401         }
1402
1403         return *prev;
1404 }
1405
1406 const Tempo&
1407 TempoMap::tempo_at (nframes_t frame)
1408 {
1409         Metric m (metric_at (frame));
1410         return m.tempo();
1411 }
1412
1413
1414 const Meter&
1415 TempoMap::meter_at (nframes_t frame)
1416 {
1417         Metric m (metric_at (frame));
1418         return m.meter();
1419 }
1420
1421 XMLNode&
1422 TempoMap::get_state ()
1423 {
1424         Metrics::const_iterator i;
1425         XMLNode *root = new XMLNode ("TempoMap");
1426
1427         {
1428                 Glib::RWLock::ReaderLock lm (lock);
1429                 for (i = metrics->begin(); i != metrics->end(); ++i) {
1430                         root->add_child_nocopy ((*i)->get_state());
1431                 }
1432         }
1433
1434         return *root;
1435 }
1436
1437 int
1438 TempoMap::set_state (const XMLNode& node)
1439 {
1440         {
1441                 Glib::RWLock::WriterLock lm (lock);
1442
1443                 XMLNodeList nlist;
1444                 XMLNodeConstIterator niter;
1445                 Metrics old_metrics (*metrics);
1446                 
1447                 metrics->clear();
1448
1449                 nlist = node.children();
1450                 
1451                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1452                         XMLNode* child = *niter;
1453                         
1454                         if (child->name() == TempoSection::xml_state_node_name) {
1455                                 
1456                                 try {
1457                                         metrics->push_back (new TempoSection (*child));
1458                                 }
1459                                 
1460                                 catch (failed_constructor& err){
1461                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1462                                         *metrics = old_metrics;
1463                                         break;
1464                                 }
1465                                 
1466                         } else if (child->name() == MeterSection::xml_state_node_name) {
1467                                 
1468                                 try {
1469                                         metrics->push_back (new MeterSection (*child));
1470                                 }
1471                                 
1472                                 catch (failed_constructor& err) {
1473                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1474                                         *metrics = old_metrics;
1475                                         break;
1476                                 }
1477                         }
1478                 }
1479                 
1480                 if (niter == nlist.end()) {
1481                         
1482                         MetricSectionSorter cmp;
1483                         metrics->sort (cmp);
1484                         timestamp_metrics (true);
1485                 }
1486         }
1487         
1488         StateChanged (Change (0));
1489
1490         return 0;
1491 }
1492
1493 void
1494 TempoMap::dump (std::ostream& o) const
1495 {
1496         const MeterSection* m;
1497         const TempoSection* t;
1498         
1499         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1500
1501                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1502                         o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1503                           << t->movable() << ')' << endl;
1504                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1505                         o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame() 
1506                           << " (move? " << m->movable() << ')' << endl;
1507                 }
1508         }
1509 }
1510
1511 int
1512 TempoMap::n_tempos() const
1513 {
1514         Glib::RWLock::ReaderLock lm (lock);
1515         int cnt = 0;
1516
1517         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1518                 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1519                         cnt++;
1520                 }
1521         }
1522
1523         return cnt;
1524 }
1525
1526 int
1527 TempoMap::n_meters() const
1528 {
1529         Glib::RWLock::ReaderLock lm (lock);
1530         int cnt = 0;
1531
1532         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1533                 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1534                         cnt++;
1535                 }
1536         }
1537
1538         return cnt;
1539 }