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