newly created files for use in recording appear in a .stubs folder, and are moved...
[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                 PropertyChanged (PropertyChange ());
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                 PropertyChanged (PropertyChange ());
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                 PropertyChanged (PropertyChange ());
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                 PropertyChanged (PropertyChange ());
365         }
366 }
367
368 void
369 TempoMap::do_insert (MetricSection* section, bool with_bbt)
370 {
371         Metrics::iterator i;
372
373         /* Look for any existing MetricSection that is of the same type and
374            at the same time as the new one, and remove it before adding
375            the new one.
376         */
377
378         Metrics::iterator to_remove = metrics->end ();
379
380         for (i = metrics->begin(); i != metrics->end(); ++i) {
381
382                 int const c = (*i)->compare (section, with_bbt);
383
384                 if (c < 0) {
385                         /* this section is before the one to be added; go back round */
386                         continue;
387                 } else if (c > 0) {
388                         /* this section is after the one to be added; there can't be any at the same time */
389                         break;
390                 }
391
392                 /* hacky comparison of type */
393                 bool const a = dynamic_cast<TempoSection*> (*i) != 0;
394                 bool const b = dynamic_cast<TempoSection*> (section) != 0;
395
396                 if (a == b) {
397                         to_remove = i;
398                         break;
399                 }
400         }
401
402         if (to_remove != metrics->end()) {
403                 /* remove the MetricSection at the same time as the one we are about to add */
404                 metrics->erase (to_remove);
405         }
406
407         /* Add the given MetricSection */
408
409         for (i = metrics->begin(); i != metrics->end(); ++i) {
410
411                 if ((*i)->compare (section, with_bbt) < 0) {
412                         continue;
413                 }
414
415                 metrics->insert (i, section);
416                 break;
417         }
418
419         if (i == metrics->end()) {
420                 metrics->insert (metrics->end(), section);
421         }
422
423         timestamp_metrics (with_bbt);
424 }
425
426 void
427 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
428 {
429         {
430                 Glib::RWLock::WriterLock lm (lock);
431
432                 /* new tempos always start on a beat */
433                 where.ticks = 0;
434
435                 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), true);
436         }
437
438         PropertyChanged (PropertyChange ());
439 }
440
441 void
442 TempoMap::add_tempo (const Tempo& tempo, nframes64_t where)
443 {
444         {
445                 Glib::RWLock::WriterLock lm (lock);
446                 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), false);
447         }
448
449         PropertyChanged (PropertyChange ());
450 }
451
452 void
453 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
454 {
455         bool replaced = false;
456
457         {
458                 Glib::RWLock::WriterLock lm (lock);
459                 Metrics::iterator i;
460
461                 for (i = metrics->begin(); i != metrics->end(); ++i) {
462                         TempoSection *ts;
463
464                         if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
465
466                                  *((Tempo *) ts) = replacement;
467
468                                 replaced = true;
469                                 timestamp_metrics (true);
470
471                                 break;
472                         }
473                 }
474         }
475
476         if (replaced) {
477                 PropertyChanged (PropertyChange ());
478         }
479 }
480
481 void
482 TempoMap::add_meter (const Meter& meter, BBT_Time where)
483 {
484         {
485                 Glib::RWLock::WriterLock lm (lock);
486
487                 /* a new meter always starts a new bar on the first beat. so
488                    round the start time appropriately. remember that
489                    `where' is based on the existing tempo map, not
490                    the result after we insert the new meter.
491
492                 */
493
494                 if (where.beats != 1) {
495                         where.beats = 1;
496                         where.bars++;
497                 }
498
499                 /* new meters *always* start on a beat. */
500                 where.ticks = 0;
501
502                 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), true);
503         }
504
505         PropertyChanged (PropertyChange ());
506 }
507
508 void
509 TempoMap::add_meter (const Meter& meter, nframes64_t where)
510 {
511         {
512                 Glib::RWLock::WriterLock lm (lock);
513                 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), false);
514         }
515
516         PropertyChanged (PropertyChange ());
517 }
518
519 void
520 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
521 {
522         bool replaced = false;
523
524         {
525                 Glib::RWLock::WriterLock lm (lock);
526                 Metrics::iterator i;
527
528                 for (i = metrics->begin(); i != metrics->end(); ++i) {
529                         MeterSection *ms;
530                         if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
531
532                                 *((Meter*) ms) = replacement;
533
534                                 replaced = true;
535                                 timestamp_metrics (true);
536                                 break;
537                         }
538                 }
539         }
540
541         if (replaced) {
542                 PropertyChanged (PropertyChange ());
543         }
544 }
545
546 void
547 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
548 {
549         Tempo newtempo (beats_per_minute, note_type);
550         TempoSection* t;
551
552         for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
553                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
554                         *((Tempo*) t) = newtempo;
555                         PropertyChanged (PropertyChange ());
556                         break;
557                 }
558         }
559 }
560
561 void
562 TempoMap::change_existing_tempo_at (nframes64_t where, double beats_per_minute, double note_type)
563 {
564         Tempo newtempo (beats_per_minute, note_type);
565
566         TempoSection* prev;
567         TempoSection* first;
568         Metrics::iterator i;
569
570         /* find the TempoSection immediately preceding "where"
571          */
572
573         for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
574
575                 if ((*i)->frame() > where) {
576                         break;
577                 }
578
579                 TempoSection* t;
580
581                 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
582                         if (!first) {
583                                 first = t;
584                         }
585                         prev = t;
586                 }
587         }
588
589         if (!prev) {
590                 if (!first) {
591                         error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
592                         return;
593                 }
594
595                 prev = first;
596         }
597
598         /* reset */
599
600         *((Tempo*)prev) = newtempo;
601         PropertyChanged (PropertyChange ());
602 }
603
604 const MeterSection&
605 TempoMap::first_meter () const
606 {
607         const MeterSection *m = 0;
608
609         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
610                 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
611                         return *m;
612                 }
613         }
614
615         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
616         /*NOTREACHED*/
617         return *m;
618 }
619
620 const TempoSection&
621 TempoMap::first_tempo () const
622 {
623         const TempoSection *t = 0;
624
625         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
626                 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
627                         return *t;
628                 }
629         }
630
631         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
632         /*NOTREACHED*/
633         return *t;
634 }
635
636 void
637 TempoMap::timestamp_metrics (bool use_bbt)
638 {
639         Metrics::iterator i;
640         const Meter* meter;
641         const Tempo* tempo;
642         Meter *m;
643         Tempo *t;
644
645         meter = &first_meter ();
646         tempo = &first_tempo ();
647
648         if (use_bbt) {
649
650                 // cerr << "\n\n\n ######################\nTIMESTAMP via BBT ##############\n" << endl;
651
652                 nframes64_t current = 0;
653                 nframes64_t section_frames;
654                 BBT_Time start;
655                 BBT_Time end;
656
657                 for (i = metrics->begin(); i != metrics->end(); ++i) {
658
659                         end = (*i)->start();
660
661                         section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
662
663                         current += section_frames;
664
665                         start = end;
666
667                         (*i)->set_frame (current);
668
669                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
670                                 tempo = t;
671                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
672                                 meter = m;
673                         } else {
674                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
675                                 /*NOTREACHED*/
676                         }
677                 }
678
679         } else {
680
681                 // cerr << "\n\n\n ######################\nTIMESTAMP via AUDIO ##############\n" << endl;
682
683                 bool first = true;
684                 MetricSection* prev = 0;
685
686                 for (i = metrics->begin(); i != metrics->end(); ++i) {
687
688                         BBT_Time bbt;
689                         TempoMetric metric (*meter, *tempo);
690
691                         if (prev) {
692                                 metric.set_start (prev->start());
693                                 metric.set_frame (prev->frame());
694                         } else {
695                                 // metric will be at frames=0 bbt=1|1|0 by default
696                                 // which is correct for our purpose
697                         }
698
699                         bbt_time_with_metric ((*i)->frame(), bbt, metric);
700
701                         // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
702
703
704                         if (first) {
705                                 first = false;
706                         } else {
707
708                                 if (bbt.ticks > Meter::ticks_per_beat/2) {
709                                         /* round up to next beat */
710                                         bbt.beats += 1;
711                                 }
712
713                                 bbt.ticks = 0;
714
715                                 if (bbt.beats != 1) {
716                                         /* round up to next bar */
717                                         bbt.bars += 1;
718                                         bbt.beats = 1;
719                                 }
720                         }
721
722                         //s cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
723
724                         (*i)->set_start (bbt);
725
726                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
727                                 tempo = t;
728                                 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
729                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
730                                 meter = m;
731                                 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
732                         } else {
733                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
734                                 /*NOTREACHED*/
735                         }
736
737                         prev = (*i);
738                 }
739         }
740
741         // dump (cerr);
742         // cerr << "###############################################\n\n\n" << endl;
743
744 }
745
746 TempoMetric
747 TempoMap::metric_at (nframes64_t frame) const
748 {
749         TempoMetric m (first_meter(), first_tempo());
750         const Meter* meter;
751         const Tempo* tempo;
752
753         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
754            at something, because we insert the default tempo and meter during
755            TempoMap construction.
756
757            now see if we can find better candidates.
758         */
759
760         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
761
762                 if ((*i)->frame() > frame) {
763                         break;
764                 }
765
766                 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
767                         m.set_tempo (*tempo);
768                 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
769                         m.set_meter (*meter);
770                 }
771
772                 m.set_frame ((*i)->frame ());
773                 m.set_start ((*i)->start ());
774         }
775
776         return m;
777 }
778
779 TempoMetric
780 TempoMap::metric_at (BBT_Time bbt) const
781 {
782         TempoMetric m (first_meter(), first_tempo());
783         const Meter* meter;
784         const Tempo* tempo;
785
786         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
787            at something, because we insert the default tempo and meter during
788            TempoMap construction.
789
790            now see if we can find better candidates.
791         */
792
793         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
794
795                 BBT_Time section_start ((*i)->start());
796
797                 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
798                         break;
799                 }
800
801                 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
802                         m.set_tempo (*tempo);
803                 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
804                         m.set_meter (*meter);
805                 }
806
807                 m.set_frame ((*i)->frame ());
808                 m.set_start (section_start);
809         }
810
811         return m;
812 }
813
814 void
815 TempoMap::bbt_time (nframes64_t frame, BBT_Time& bbt) const
816 {
817         {
818                 Glib::RWLock::ReaderLock lm (lock);
819                 bbt_time_unlocked (frame, bbt);
820         }
821 }
822
823 void
824 TempoMap::bbt_time_unlocked (nframes64_t frame, BBT_Time& bbt) const
825 {
826         bbt_time_with_metric (frame, bbt, metric_at (frame));
827 }
828
829 void
830 TempoMap::bbt_time_with_metric (nframes64_t frame, BBT_Time& bbt, const TempoMetric& metric) const
831 {
832         nframes64_t frame_diff;
833
834         // cerr << "---- BBT time for " << frame << " using metric @ " << metric.frame() << " BBT " << metric.start() << endl;
835
836         const double beats_per_bar = metric.meter().beats_per_bar();
837         const double ticks_per_frame = metric.tempo().frames_per_beat (_frame_rate, metric.meter()) / Meter::ticks_per_beat;
838
839         /* now compute how far beyond that point we actually are. */
840
841         frame_diff = frame - metric.frame();
842
843         bbt.ticks = metric.start().ticks + (uint32_t)round((double)frame_diff / ticks_per_frame);
844         uint32_t xtra_beats = bbt.ticks / (uint32_t)Meter::ticks_per_beat;
845         bbt.ticks %= (uint32_t)Meter::ticks_per_beat;
846
847         bbt.beats = metric.start().beats + xtra_beats - 1; // correction for 1-based counting, see below for matching operation.
848         bbt.bars = metric.start().bars + (uint32_t)floor((double)bbt.beats / beats_per_bar);
849         bbt.beats = (uint32_t)fmod((double)bbt.beats, beats_per_bar);
850
851         /* if we have a fractional number of beats per bar, we see if
852            we're in the last beat (the fractional one).  if so, we
853            round ticks appropriately and bump to the next bar. */
854         double beat_fraction = beats_per_bar - floor(beats_per_bar);
855         /* XXX one problem here is that I'm not sure how to handle
856            fractional beats that don't evenly divide ticks_per_beat.
857            If they aren't handled consistently, I would guess we'll
858            continue to have strange discrepancies occuring.  Perhaps
859            this will also behave badly in the case of meters like
860            0.1/4, but I can't be bothered to test that.
861         */
862         uint32_t ticks_on_last_beat = (uint32_t)floor(Meter::ticks_per_beat * beat_fraction);
863
864         if (bbt.beats > (uint32_t)floor(beats_per_bar) && bbt.ticks >= ticks_on_last_beat) {
865                 bbt.ticks -= ticks_on_last_beat;
866                 bbt.beats = 0;
867                 bbt.bars++;
868         }
869
870         bbt.beats++; // correction for 1-based counting, see above for matching operation.
871
872         // cerr << "-----\t RETURN " << bbt << endl;
873 }
874
875 nframes64_t
876 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
877 {
878         /* for this to work with fractional measure types, start and end have to be "legal" BBT types,
879            that means that the beats and ticks should be inside a bar
880         */
881
882         nframes64_t frames = 0;
883         nframes64_t start_frame = 0;
884         nframes64_t end_frame = 0;
885
886         TempoMetric m = metric_at (start);
887
888         uint32_t bar_offset = start.bars - m.start().bars;
889
890         double  beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
891                 + start.ticks/Meter::ticks_per_beat;
892
893
894         start_frame = m.frame() + (nframes64_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
895
896         m =  metric_at(end);
897
898         bar_offset = end.bars - m.start().bars;
899
900         beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
901                 + end.ticks/Meter::ticks_per_beat;
902
903         end_frame = m.frame() + (nframes64_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
904
905         frames = end_frame - start_frame;
906
907         return frames;
908
909 }
910
911 nframes64_t
912 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
913 {
914         /* this is used in timestamping the metrics by actually counting the beats */
915
916         nframes64_t frames = 0;
917         uint32_t bar = start.bars;
918         double beat = (double) start.beats;
919         double beats_counted = 0;
920         double beats_per_bar = 0;
921         double beat_frames = 0;
922
923         beats_per_bar = meter.beats_per_bar();
924         beat_frames = tempo.frames_per_beat (_frame_rate,meter);
925
926         frames = 0;
927
928         while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
929
930                 if (beat >= beats_per_bar) {
931                         beat = 1;
932                         ++bar;
933                         ++beats_counted;
934
935                         if (beat > beats_per_bar) {
936
937                                 /* this is a fractional beat at the end of a fractional bar
938                                    so it should only count for the fraction
939                                 */
940
941                                 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
942                         }
943
944                 } else {
945                         ++beat;
946                         ++beats_counted;
947                 }
948         }
949
950         // cerr << "Counted " << beats_counted << " from " << start << " to " << end
951         // << " bpb were " << beats_per_bar
952         // << " fpb was " << beat_frames
953         // << endl;
954
955         frames = (nframes64_t) floor (beats_counted * beat_frames);
956
957         return frames;
958
959 }
960
961 nframes64_t
962 TempoMap::frame_time (const BBT_Time& bbt) const
963 {
964         BBT_Time start ; /* 1|1|0 */
965
966         return  count_frames_between ( start, bbt);
967 }
968
969 nframes64_t
970 TempoMap::bbt_duration_at (nframes64_t pos, const BBT_Time& bbt, int dir) const
971 {
972         nframes64_t frames = 0;
973
974         BBT_Time when;
975         bbt_time(pos, when);
976
977         {
978                 Glib::RWLock::ReaderLock lm (lock);
979                 frames = bbt_duration_at_unlocked (when, bbt,dir);
980         }
981
982         return frames;
983 }
984
985 nframes64_t
986 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
987 {
988
989         nframes64_t frames = 0;
990
991         double beats_per_bar;
992         BBT_Time result;
993
994         result.bars = max(1U, when.bars + dir * bbt.bars) ;
995         result.beats = 1;
996         result.ticks = 0;
997
998         TempoMetric     metric = metric_at(result);
999         beats_per_bar = metric.meter().beats_per_bar();
1000
1001
1002
1003         /*reduce things to legal bbt  values
1004           we have to handle possible fractional=shorter beats at the end of measures
1005           and things like 0|11|9000  as a duration in a 4.5/4 measure
1006           the musical decision is that the fractional beat is also a beat , although a shorter one
1007         */
1008
1009
1010         if (dir >= 0) {
1011                 result.beats = when.beats +  bbt.beats;
1012                 result.ticks = when.ticks +  bbt.ticks;
1013
1014                 while (result.beats >= (beats_per_bar + 1)) {
1015                         result.bars++;
1016                         result.beats -=  (uint32_t) ceil(beats_per_bar);
1017                         metric = metric_at(result); // maybe there is a meter change
1018                         beats_per_bar = metric.meter().beats_per_bar();
1019
1020                 }
1021                 /*we now counted the beats and landed in the target measure, now deal with ticks
1022                   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
1023                   and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
1024                 */
1025
1026                 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
1027                  */
1028
1029                 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
1030                                         (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat
1031                                            : Meter::ticks_per_beat );
1032
1033                 while (result.ticks >= ticks_at_beat) {
1034                         result.beats++;
1035                         result.ticks -= ticks_at_beat;
1036                         if  (result.beats >= (beats_per_bar + 1)) {
1037                                 result.bars++;
1038                                 result.beats = 1;
1039                                 metric = metric_at(result); // maybe there is a meter change
1040                                 beats_per_bar = metric.meter().beats_per_bar();
1041                         }
1042                         ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
1043                                        (1 - (ceil(beats_per_bar) - beats_per_bar) ) * Meter::ticks_per_beat
1044                                        : Meter::ticks_per_beat);
1045
1046                 }
1047
1048
1049         } else {
1050                 uint32_t b = bbt.beats;
1051
1052                 /* count beats */
1053                 while( b > when.beats ) {
1054
1055                         result.bars = max(1U,result.bars-- ) ;
1056                         metric = metric_at(result); // maybe there is a meter change
1057                         beats_per_bar = metric.meter().beats_per_bar();
1058                         if (b >= ceil(beats_per_bar)) {
1059
1060                                 b -= (uint32_t) ceil(beats_per_bar);
1061                         } else {
1062                                 b = (uint32_t) ceil(beats_per_bar) - b + when.beats ;
1063                         }
1064                 }
1065                 result.beats = when.beats - b;
1066
1067                 /*count ticks */
1068
1069                 if (bbt.ticks <= when.ticks) {
1070                         result.ticks = when.ticks - bbt.ticks;
1071                 } else {
1072
1073                         uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
1074                         uint32_t t = bbt.ticks - when.ticks;
1075
1076                         do {
1077
1078                                 if (result.beats == 1) {
1079                                         result.bars = max(1U, result.bars-- ) ;
1080                                         metric = metric_at(result); // maybe there is a meter change
1081                                         beats_per_bar = metric.meter().beats_per_bar();
1082                                         result.beats = (uint32_t) ceil(beats_per_bar);
1083                                         ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar)) * Meter::ticks_per_beat) ;
1084                                 } else {
1085                                         result.beats --;
1086                                         ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
1087                                 }
1088
1089                                 if (t <= ticks_at_beat) {
1090                                         result.ticks = ticks_at_beat - t;
1091                                 } else {
1092                                         t-= ticks_at_beat;
1093                                 }
1094                         } while (t > ticks_at_beat);
1095
1096                 }
1097
1098
1099         }
1100
1101         if (dir < 0 ) {
1102                 frames = count_frames_between( result,when);
1103         } else {
1104                 frames = count_frames_between(when,result);
1105         }
1106
1107         return frames;
1108 }
1109
1110
1111
1112 nframes64_t
1113 TempoMap::round_to_bar (nframes64_t fr, int dir)
1114 {
1115         {
1116                 Glib::RWLock::ReaderLock lm (lock);
1117                 return round_to_type (fr, dir, Bar);
1118         }
1119 }
1120
1121
1122 nframes64_t
1123 TempoMap::round_to_beat (nframes64_t fr, int dir)
1124 {
1125         {
1126                 Glib::RWLock::ReaderLock lm (lock);
1127                 return round_to_type (fr, dir, Beat);
1128         }
1129 }
1130
1131 nframes64_t
1132 TempoMap::round_to_beat_subdivision (nframes64_t fr, int sub_num, int dir)
1133 {
1134         BBT_Time the_beat;
1135         uint32_t ticks_one_half_subdivisions_worth;
1136         uint32_t ticks_one_subdivisions_worth;
1137         uint32_t difference;
1138
1139         bbt_time(fr, the_beat);
1140
1141         ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
1142         ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
1143
1144         if (dir > 0) {
1145
1146                 /* round to next */
1147
1148                 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1149
1150                 if (mod == 0) {
1151                         /* right on the subdivision, so the difference is just the subdivision ticks */
1152                         difference = ticks_one_subdivisions_worth;
1153
1154                 } else {
1155                         /* not on subdivision, compute distance to next subdivision */
1156
1157                         difference = ticks_one_subdivisions_worth - mod;
1158                 }
1159
1160                 the_beat = bbt_add (the_beat, BBT_Time (0, 0, difference));
1161
1162         } else if (dir < 0) {
1163
1164                 /* round to previous */
1165
1166                 uint32_t mod = the_beat.ticks % ticks_one_subdivisions_worth;
1167
1168                 if (mod == 0) {
1169                         /* right on the subdivision, so the difference is just the subdivision ticks */
1170                         difference = ticks_one_subdivisions_worth;
1171                 } else {
1172                         /* not on subdivision, compute distance to previous subdivision, which
1173                            is just the modulus.
1174                         */
1175
1176                         difference = mod;
1177                 }
1178
1179                 the_beat = bbt_subtract (the_beat, BBT_Time (0, 0, difference));
1180
1181         } else {
1182                 /* round to nearest */
1183
1184                 if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1185                         difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1186                         the_beat = bbt_add (the_beat, BBT_Time (0, 0, difference));
1187                 } else {
1188                         // difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1189                         the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1190                 }
1191         }
1192
1193         return frame_time (the_beat);
1194 }
1195
1196 nframes64_t
1197 TempoMap::round_to_type (nframes64_t frame, int dir, BBTPointType type)
1198 {
1199         TempoMetric metric = metric_at (frame);
1200         BBT_Time bbt;
1201         BBT_Time start;
1202         BBT_Time one_bar (1,0,0);
1203         BBT_Time one_beat (0,1,0);
1204
1205         bbt_time_with_metric (frame, bbt, metric);
1206
1207         switch (type) {
1208         case Bar:
1209                 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to bars in direction %2\n", frame, dir, bbt));
1210
1211                 if (dir < 0) {
1212
1213                         /* find bar position preceding frame */
1214
1215                         try {
1216                                 bbt = bbt_subtract (bbt, one_bar);
1217                         }
1218
1219                         catch (...) {
1220                                 return frame;
1221                         }
1222
1223
1224                 } else if (dir > 0) {
1225
1226                         /* find bar position following frame */
1227
1228                         try {
1229                                 bbt = bbt_add (bbt, one_bar, metric);
1230                         }
1231                         catch (...) {
1232                                 return frame;
1233                         }
1234
1235                 } else {
1236
1237                         /* "true" rounding */
1238
1239                         float midbar_beats;
1240                         float midbar_ticks;
1241
1242                         midbar_beats = metric.meter().beats_per_bar() / 2 + 1;
1243                         midbar_ticks = Meter::ticks_per_beat * fmod (midbar_beats, 1.0f);
1244                         midbar_beats = floor (midbar_beats);
1245                         
1246                         BBT_Time midbar (bbt.bars, lrintf (midbar_beats), lrintf (midbar_ticks));
1247
1248                         if (bbt < midbar) {
1249                                 /* round down */
1250                                 bbt.beats = 1;
1251                                 bbt.ticks = 0;
1252                         } else {
1253                                 /* round up */
1254                                 bbt.bars++;
1255                                 bbt.beats = 1;
1256                                 bbt.ticks = 0;
1257                         }
1258                 }
1259                 /* force beats & ticks to their values at the start of a bar */
1260                 bbt.beats = 1;
1261                 bbt.ticks = 0;
1262                 break;
1263
1264         case Beat:
1265                 DEBUG_TRACE(DEBUG::SnapBBT, string_compose ("round from %1 (%3) to beat in direction %2\n", frame, (dir < 0 ? "back" : "forward"), bbt));
1266
1267                 if (dir < 0) {
1268
1269                         /* find beat position preceding frame */
1270
1271                         try {
1272                                 bbt = bbt_subtract (bbt, one_beat); 
1273                         }
1274
1275                         catch (...) {
1276                                 return frame;
1277                         }
1278
1279
1280                 } else if (dir > 0) {
1281
1282                         /* find beat position following frame */
1283
1284                         try {
1285                                 bbt = bbt_add (bbt, one_beat, metric);
1286                         }
1287                         catch (...) {
1288                                 return frame;
1289                         }
1290
1291                 } else {
1292
1293                         /* "true" rounding */
1294
1295                         /* round to nearest beat */
1296                         if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1297
1298                                 try {
1299                                         bbt = bbt_add (bbt, one_beat, metric);
1300                                 }
1301                                 catch (...) {
1302                                         return frame;
1303                                 }
1304                         }
1305                 }
1306                 /* force ticks to the value at the start of a beat */
1307                 bbt.ticks = 0;
1308                 break;
1309
1310         }
1311
1312         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)));
1313         return metric.frame() + count_frames_between (metric.start(), bbt);
1314 }
1315
1316 TempoMap::BBTPointList *
1317 TempoMap::get_points (nframes64_t lower, nframes64_t upper) const
1318 {
1319
1320         Metrics::const_iterator i;
1321         BBTPointList *points;
1322         double current;
1323         const MeterSection* meter;
1324         const MeterSection* m;
1325         const TempoSection* tempo;
1326         const TempoSection* t;
1327         uint32_t bar;
1328         uint32_t beat;
1329         double beats_per_bar;
1330         double beat_frame;
1331         double beat_frames;
1332         double frames_per_bar;
1333         double delta_bars;
1334         double delta_beats;
1335         double dummy;
1336         nframes64_t limit;
1337
1338         meter = &first_meter ();
1339         tempo = &first_tempo ();
1340
1341         /* find the starting point */
1342
1343         for (i = metrics->begin(); i != metrics->end(); ++i) {
1344
1345                 if ((*i)->frame() > lower) {
1346                         break;
1347                 }
1348
1349                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1350                         tempo = t;
1351                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1352                         meter = m;
1353                 }
1354         }
1355
1356         /* We now have:
1357
1358            meter -> the Meter for "lower"
1359            tempo -> the Tempo for "lower"
1360            i     -> for first new metric after "lower", possibly metrics->end()
1361
1362            Now start generating points.
1363         */
1364
1365         beats_per_bar = meter->beats_per_bar ();
1366         frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1367         beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1368
1369         if (meter->frame() > tempo->frame()) {
1370                 bar = meter->start().bars;
1371                 beat = meter->start().beats;
1372                 current = meter->frame();
1373         } else {
1374                 bar = tempo->start().bars;
1375                 beat = tempo->start().beats;
1376                 current = tempo->frame();
1377         }
1378
1379         /* initialize current to point to the bar/beat just prior to the
1380            lower frame bound passed in.  assumes that current is initialized
1381            above to be on a beat.
1382         */
1383
1384         delta_bars = (lower-current) / frames_per_bar;
1385         delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1386         current += (floor(delta_bars) * frames_per_bar) +  (floor(delta_beats) * beat_frames);
1387
1388         // adjust bars and beats too
1389         bar += (uint32_t) (floor(delta_bars));
1390         beat += (uint32_t) (floor(delta_beats));
1391
1392         points = new BBTPointList;
1393
1394         do {
1395
1396                 if (i == metrics->end()) {
1397                         limit = upper;
1398                         // cerr << "== limit set to end of request @ " << limit << endl;
1399                 } else {
1400                         // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
1401                         limit = (*i)->frame();
1402                 }
1403
1404                 limit = min (limit, upper);
1405
1406                 while (current < limit) {
1407
1408                         /* if we're at the start of a bar, add bar point */
1409
1410                         if (beat == 1) {
1411                                 if (current >= lower) {
1412                                         // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1413                                         points->push_back (BBTPoint (*meter, *tempo,(nframes64_t)rint(current), Bar, bar, 1));
1414
1415                                 }
1416                         }
1417
1418                         /* add some beats if we can */
1419
1420                         beat_frame = current;
1421
1422                         while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1423                                 if (beat_frame >= lower) {
1424                                         // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1425                                         points->push_back (BBTPoint (*meter, *tempo, (nframes64_t) rint(beat_frame), Beat, bar, beat));
1426                                 }
1427                                 beat_frame += beat_frames;
1428                                 current+= beat_frames;
1429
1430                                 beat++;
1431                         }
1432
1433                         //  cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
1434                         // << (beat > ceil(beats_per_bar))
1435                         // << endl;
1436
1437                         if (beat > ceil(beats_per_bar) || i != metrics->end()) {
1438
1439                                 /* we walked an entire bar. its
1440                                    important to move `current' forward
1441                                    by the actual frames_per_bar, not move it to
1442                                    an integral beat_frame, so that metrics with
1443                                    non-integral beats-per-bar have
1444                                    their bar positions set
1445                                    correctly. consider a metric with
1446                                    9-1/2 beats-per-bar. the bar we
1447                                    just filled had  10 beat marks,
1448                                    but the bar end is 1/2 beat before
1449                                    the last beat mark.
1450                                    And it is also possible that a tempo
1451                                    change occured in the middle of a bar,
1452                                    so we subtract the possible extra fraction from the current
1453                                 */
1454
1455                                 if (beat > ceil (beats_per_bar)) {
1456                                         /* next bar goes where the numbers suggest */
1457                                         current -=  beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1458                                         // cerr << "++ next bar from numbers\n";
1459                                 } else {
1460                                         /* next bar goes where the next metric is */
1461                                         current = limit;
1462                                         // cerr << "++ next bar at next metric\n";
1463                                 }
1464                                 bar++;
1465                                 beat = 1;
1466                         }
1467
1468                 }
1469
1470                 /* if we're done, then we're done */
1471
1472                 if (current >= upper) {
1473                         break;
1474                 }
1475
1476                 /* i is an iterator that refers to the next metric (or none).
1477                    if there is a next metric, move to it, and continue.
1478                 */
1479
1480                 if (i != metrics->end()) {
1481
1482                         if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1483                                 tempo = t;
1484                         } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1485                                 meter = m;
1486                                 /* new MeterSection, beat always returns to 1 */
1487                                 beat = 1;
1488                         }
1489
1490                         current = (*i)->frame ();
1491                         // cerr << "loop around with current @ " << current << endl;
1492
1493                         beats_per_bar = meter->beats_per_bar ();
1494                         frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1495                         beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1496
1497                         ++i;
1498                 }
1499
1500         } while (1);
1501
1502         return points;
1503 }
1504
1505 const TempoSection&
1506 TempoMap::tempo_section_at (nframes64_t frame)
1507 {
1508         Glib::RWLock::ReaderLock lm (lock);
1509         Metrics::iterator i;
1510         TempoSection* prev = 0;
1511
1512         for (i = metrics->begin(); i != metrics->end(); ++i) {
1513                 TempoSection* t;
1514
1515                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1516
1517                         if ((*i)->frame() > frame) {
1518                                 break;
1519                         }
1520
1521                         prev = t;
1522                 }
1523         }
1524
1525         if (prev == 0) {
1526                 fatal << endmsg;
1527         }
1528
1529         return *prev;
1530 }
1531
1532 const Tempo&
1533 TempoMap::tempo_at (nframes64_t frame) const
1534 {
1535         TempoMetric m (metric_at (frame));
1536         return m.tempo();
1537 }
1538
1539
1540 const Meter&
1541 TempoMap::meter_at (nframes64_t frame) const
1542 {
1543         TempoMetric m (metric_at (frame));
1544         return m.meter();
1545 }
1546
1547 XMLNode&
1548 TempoMap::get_state ()
1549 {
1550         Metrics::const_iterator i;
1551         XMLNode *root = new XMLNode ("TempoMap");
1552
1553         {
1554                 Glib::RWLock::ReaderLock lm (lock);
1555                 for (i = metrics->begin(); i != metrics->end(); ++i) {
1556                         root->add_child_nocopy ((*i)->get_state());
1557                 }
1558         }
1559
1560         return *root;
1561 }
1562
1563 int
1564 TempoMap::set_state (const XMLNode& node, int /*version*/)
1565 {
1566         {
1567                 Glib::RWLock::WriterLock lm (lock);
1568
1569                 XMLNodeList nlist;
1570                 XMLNodeConstIterator niter;
1571                 Metrics old_metrics (*metrics);
1572
1573                 metrics->clear();
1574
1575                 nlist = node.children();
1576
1577                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1578                         XMLNode* child = *niter;
1579
1580                         if (child->name() == TempoSection::xml_state_node_name) {
1581
1582                                 try {
1583                                         metrics->push_back (new TempoSection (*child));
1584                                 }
1585
1586                                 catch (failed_constructor& err){
1587                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1588                                         *metrics = old_metrics;
1589                                         break;
1590                                 }
1591
1592                         } else if (child->name() == MeterSection::xml_state_node_name) {
1593
1594                                 try {
1595                                         metrics->push_back (new MeterSection (*child));
1596                                 }
1597
1598                                 catch (failed_constructor& err) {
1599                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1600                                         *metrics = old_metrics;
1601                                         break;
1602                                 }
1603                         }
1604                 }
1605
1606                 if (niter == nlist.end()) {
1607
1608                         MetricSectionSorter cmp;
1609                         metrics->sort (cmp);
1610                         timestamp_metrics (true);
1611                 }
1612         }
1613
1614         PropertyChanged (PropertyChange ());
1615
1616         return 0;
1617 }
1618
1619 void
1620 TempoMap::dump (std::ostream& o) const
1621 {
1622         const MeterSection* m;
1623         const TempoSection* t;
1624
1625         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1626
1627                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1628                         o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1629                           << t->movable() << ')' << endl;
1630                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1631                         o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
1632                           << " (move? " << m->movable() << ')' << endl;
1633                 }
1634         }
1635 }
1636
1637 int
1638 TempoMap::n_tempos() const
1639 {
1640         Glib::RWLock::ReaderLock lm (lock);
1641         int cnt = 0;
1642
1643         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1644                 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1645                         cnt++;
1646                 }
1647         }
1648
1649         return cnt;
1650 }
1651
1652 int
1653 TempoMap::n_meters() const
1654 {
1655         Glib::RWLock::ReaderLock lm (lock);
1656         int cnt = 0;
1657
1658         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1659                 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1660                         cnt++;
1661                 }
1662         }
1663
1664         return cnt;
1665 }
1666
1667 void
1668 TempoMap::insert_time (nframes64_t where, nframes64_t amount)
1669 {
1670         for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1671                 if ((*i)->frame() >= where) {
1672                         (*i)->set_frame ((*i)->frame() + amount);
1673                 }
1674         }
1675
1676         timestamp_metrics (false);
1677
1678         PropertyChanged (PropertyChange ());
1679 }
1680
1681 BBT_Time
1682 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& other) const
1683 {
1684         TempoMetric metric =  metric_at (start);
1685         return bbt_add (start, other, metric);
1686 }
1687
1688 /**
1689  * add the BBT interval @param increment to  @param start and return the result
1690  */
1691 BBT_Time
1692 TempoMap::bbt_add (const BBT_Time& start, const BBT_Time& increment, const TempoMetric& /*metric*/) const
1693 {
1694         BBT_Time result = start;
1695         BBT_Time op = increment; /* argument is const, but we need to modify it */
1696         uint32_t ticks = result.ticks + op.ticks;
1697
1698         if (ticks >= Meter::ticks_per_beat) {
1699                 op.beats++;
1700                 result.ticks = ticks % (uint32_t) Meter::ticks_per_beat;
1701         } else {
1702                 result.ticks += op.ticks;
1703         }
1704
1705         /* now comes the complicated part. we have to add one beat a time,
1706            checking for a new metric on every beat.
1707         */
1708         
1709         /* grab all meter sections */
1710         
1711         list<const MeterSection*> meter_sections;
1712         
1713         for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1714                 const MeterSection* ms;
1715                 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1716                         meter_sections.push_back (ms);
1717                 }
1718         }
1719         
1720         assert (!meter_sections.empty());
1721         
1722         list<const MeterSection*>::const_iterator next_meter;
1723         const Meter* meter = 0;
1724         
1725         /* go forwards through the meter sections till we get to the one
1726            covering the current value of result. this positions i to point to 
1727            the next meter section too, or the end.
1728         */
1729         
1730         for (next_meter = meter_sections.begin(); next_meter != meter_sections.end(); ++next_meter) {
1731                 
1732                 if (result < (*next_meter)->start()) {
1733                         /* this metric is past the result time. stop looking, we have what we need */
1734                         break;
1735                 }
1736
1737                 if (result == (*next_meter)->start()) {
1738                         /* this meter section starts at result, push i beyond it so that it points
1739                            to the NEXT section, opwise we will get stuck later, and use this meter section.
1740                         */
1741                         meter = *next_meter;
1742                         ++next_meter;
1743                         break;
1744                 }
1745                 
1746                 meter = *next_meter;
1747         }
1748         
1749         assert (meter != 0);
1750                 
1751         /* OK, now have the meter for the bar start we are on, and i is an iterator 
1752            that points to the metric after the one we are currently dealing with 
1753            (or to metrics->end(), of course) 
1754         */
1755         
1756         while (op.beats) {
1757                 
1758                 /* given the current meter, have we gone past the end of the bar ? */
1759                 
1760                 if (result.beats >= meter->beats_per_bar()) {
1761                         /* move to next bar, first beat */
1762                         result.bars++;
1763                         result.beats = 1;
1764                 } else {
1765                         result.beats++;
1766                 }
1767                 
1768                 /* one down ... */
1769                 
1770                 op.beats--;
1771                 
1772                 /* check if we need to use a new meter section: has adding beats to result taken us 
1773                    to or after the start of the next meter section? in which case, use it.
1774                 */
1775
1776                 if (next_meter != meter_sections.end() && (((*next_meter)->start () < result) || (result == (*next_meter)->start()))) {
1777                         meter = *next_meter;
1778                         ++next_meter;
1779                 }
1780         }
1781
1782         /* finally, add bars */
1783
1784         result.bars += op.bars++;
1785
1786         return result;
1787 }
1788
1789 /**
1790  * subtract the BBT interval @param decrement from @param start and return the result
1791  */
1792 BBT_Time
1793 TempoMap::bbt_subtract (const BBT_Time& start, const BBT_Time& decrement) const
1794 {
1795         BBT_Time result = start;
1796         BBT_Time op = decrement; /* argument is const, but we need to modify it */
1797
1798         if (op.ticks > result.ticks) {
1799                 /* subtract an extra beat later; meanwhile set ticks to the right "carry" value */
1800                 op.beats++;
1801                 result.ticks = Meter::ticks_per_beat - (op.ticks - result.ticks);
1802         } else {
1803                 result.ticks -= op.ticks;
1804         }
1805
1806         /* now comes the complicated part. we have to subtract one beat a time,
1807            checking for a new metric on every beat.
1808         */
1809         
1810         /* grab all meter sections */
1811         
1812         list<const MeterSection*> meter_sections;
1813         
1814         for (Metrics::const_iterator x = metrics->begin(); x != metrics->end(); ++x) {
1815                 const MeterSection* ms;
1816                 if ((ms = dynamic_cast<const MeterSection*>(*x)) != 0) {
1817                         meter_sections.push_back (ms);
1818                 }
1819                 }
1820         
1821         assert (!meter_sections.empty());
1822         
1823         /* go backwards through the meter sections till we get to the one
1824            covering the current value of result. this positions i to point to 
1825            the next (previous) meter section too, or the end.
1826         */
1827         
1828         const MeterSection* meter = 0;
1829         list<const MeterSection*>::reverse_iterator next_meter; // older versions of GCC don't 
1830                                                                 // support const_reverse_iterator::operator!=()
1831         
1832         for (next_meter = meter_sections.rbegin(); next_meter != meter_sections.rend(); ++next_meter) {
1833                 
1834                 /* when we find the first meter section that is before or at result, use it,
1835                    and set next_meter to the previous one 
1836                 */
1837                 
1838                 if ((*next_meter)->start() < result || (*next_meter)->start() == result) {
1839                         meter = *next_meter;
1840                         ++next_meter;
1841                         break;
1842                 }
1843         }
1844
1845         assert (meter != 0);
1846         
1847         /* OK, now have the meter for the bar start we are on, and i is an iterator 
1848            that points to the metric after the one we are currently dealing with 
1849            (or to metrics->end(), of course) 
1850         */
1851         
1852         while (op.beats) {
1853
1854                 /* have we reached the start of the bar? if so, move to the last beat of the previous
1855                    bar. opwise, just step back 1 beat.
1856                 */
1857                 
1858                 if (result.beats == 1) {
1859                         
1860                         /* move to previous bar, last beat */
1861                         
1862                         if (result.bars <= 1) {
1863                                 /* i'm sorry dave, i can't do that */
1864                                 throw std::out_of_range ("illegal BBT subtraction");
1865                         }
1866                         
1867                         result.bars--;
1868                         result.beats = meter->beats_per_bar();
1869                 } else {
1870
1871                         /* back one beat */
1872
1873                         result.beats--;
1874                 }
1875                 
1876                 /* one down ... */
1877                 op.beats--;
1878                 
1879                 /* check if we need to use a new meter section: has subtracting beats to result taken us 
1880                    to before the start of the current meter section? in which case, use the prior one.
1881                 */
1882
1883                 if (result < meter->start() && next_meter != meter_sections.rend()) {
1884                         meter = *next_meter;
1885                         ++next_meter;
1886                 }
1887         }
1888
1889         /* finally, subtract bars */
1890
1891         if (op.bars >= result.bars) {
1892                 /* i'm sorry dave, i can't do that */
1893                 throw std::out_of_range ("illegal BBT subtraction");
1894         }
1895
1896         result.bars -= op.bars;
1897         return result;
1898 }
1899
1900 /** Compare the time of this with that of another MetricSection.
1901  *  @param with_bbt True to compare using ::start(), false to use ::frame().
1902  *  @return -1 for less than, 0 for equal, 1 for greater than.
1903  */
1904
1905 int
1906 MetricSection::compare (MetricSection* other, bool with_bbt) const
1907 {
1908         if (with_bbt) {
1909                 if (start() == other->start()) {
1910                         return 0;
1911                 } else if (start() < other->start()) {
1912                         return -1;
1913                 } else {
1914                         return 1;
1915                 }
1916         } else {
1917                 if (frame() == other->frame()) {
1918                         return 0;
1919                 } else if (frame() < other->frame()) {
1920                         return -1;
1921                 } else {
1922                         return 1;
1923                 }
1924         }
1925
1926         /* NOTREACHED */
1927         return 0;
1928 }