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