Merge with trunk R2978.
[ardour.git] / libs / ardour / tempo.cc
1 /*
2     Copyright (C) 2000-2002 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <algorithm>
21 #include <unistd.h>
22
23 #include <cmath>
24
25 #include <sigc++/bind.h>
26
27 #include <glibmm/thread.h>
28 #include <pbd/xml++.h>
29 #include <ardour/tempo.h>
30 #include <ardour/utils.h>
31
32 #include "i18n.h"
33 #include <locale.h>
34
35 using namespace std;
36 using namespace ARDOUR;
37 using namespace PBD;
38
39 /* _default tempo is 4/4 qtr=120 */
40
41 Meter    TempoMap::_default_meter (4.0, 4.0);
42 Tempo    TempoMap::_default_tempo (120.0);
43
44 const double Meter::ticks_per_beat = 1920.0;
45
46 double Tempo::frames_per_beat (nframes_t sr, const Meter& meter) const
47 {
48         return  ((60.0 * sr) / (_beats_per_minute * meter.note_divisor()/_note_type));
49 }
50
51 /***********************************************************************/
52
53 double
54 Meter::frames_per_bar (const Tempo& tempo, nframes_t sr) const
55 {
56         return ((60.0 * sr * _beats_per_bar) / (tempo.beats_per_minute() * _note_type/tempo.note_type()));
57 }
58
59 /***********************************************************************/
60
61 const string TempoSection::xml_state_node_name = "Tempo";
62
63 TempoSection::TempoSection (const XMLNode& node)
64         : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
65 {
66         const XMLProperty *prop;
67         BBT_Time start;
68         LocaleGuard lg (X_("POSIX"));
69
70         if ((prop = node.property ("start")) == 0) {
71                 error << _("TempoSection XML node has no \"start\" property") << endmsg;
72                 throw failed_constructor();
73         }
74
75         if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
76                     &start.bars,
77                     &start.beats,
78                     &start.ticks) < 3) {
79                 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
80                 throw failed_constructor();
81         }
82
83         set_start (start);
84
85         if ((prop = node.property ("beats-per-minute")) == 0) {
86                 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
87                 throw failed_constructor();
88         }
89
90         if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
91                 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
92                 throw failed_constructor();
93         }
94         
95         if ((prop = node.property ("note-type")) == 0) {
96                 /* older session, make note type be quarter by default */
97                 _note_type = 4.0;
98         } else {
99                 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
100                         error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
101                         throw failed_constructor();
102                 }
103         }
104
105         if ((prop = node.property ("movable")) == 0) {
106                 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
107                 throw failed_constructor();
108         }
109
110         set_movable (prop->value() == "yes");
111 }
112
113 XMLNode&
114 TempoSection::get_state() const
115 {
116         XMLNode *root = new XMLNode (xml_state_node_name);
117         char buf[256];
118         LocaleGuard lg (X_("POSIX"));
119
120         snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, 
121                   start().bars,
122                   start().beats,
123                   start().ticks);
124         root->add_property ("start", buf);
125         snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
126         root->add_property ("beats-per-minute", buf);
127         snprintf (buf, sizeof (buf), "%f", _note_type);
128         root->add_property ("note-type", buf);
129         snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
130         root->add_property ("movable", buf);
131
132         return *root;
133 }
134
135 /***********************************************************************/
136
137 const string MeterSection::xml_state_node_name = "Meter";
138
139 MeterSection::MeterSection (const XMLNode& node)
140         : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
141 {
142         const XMLProperty *prop;
143         BBT_Time start;
144         LocaleGuard lg (X_("POSIX"));
145
146         if ((prop = node.property ("start")) == 0) {
147                 error << _("MeterSection XML node has no \"start\" property") << endmsg;
148                 throw failed_constructor();
149         }
150
151         if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
152                     &start.bars,
153                     &start.beats,
154                     &start.ticks) < 3) {
155                 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
156                 throw failed_constructor();
157         }
158
159         set_start (start);
160
161         if ((prop = node.property ("beats-per-bar")) == 0) {
162                 error << _("MeterSection XML node has no \"beats-per-bar\" property") << endmsg;
163                 throw failed_constructor();
164         }
165
166         if (sscanf (prop->value().c_str(), "%lf", &_beats_per_bar) != 1 || _beats_per_bar < 0.0) {
167                 error << _("MeterSection XML node has an illegal \"beats-per-bar\" value") << endmsg;
168                 throw failed_constructor();
169         }
170
171         if ((prop = node.property ("note-type")) == 0) {
172                 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
173                 throw failed_constructor();
174         }
175         
176         if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
177                 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
178                 throw failed_constructor();
179         }
180
181         if ((prop = node.property ("movable")) == 0) {
182                 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
183                 throw failed_constructor();
184         }
185
186         set_movable (prop->value() == "yes");
187 }
188
189 XMLNode&
190 MeterSection::get_state() const
191 {
192         XMLNode *root = new XMLNode (xml_state_node_name);
193         char buf[256];
194         LocaleGuard lg (X_("POSIX"));
195
196         snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, 
197                   start().bars,
198                   start().beats,
199                   start().ticks);
200         root->add_property ("start", buf);
201         snprintf (buf, sizeof (buf), "%f", _note_type);
202         root->add_property ("note-type", buf);
203         snprintf (buf, sizeof (buf), "%f", _beats_per_bar);
204         root->add_property ("beats-per-bar", buf);
205         snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
206         root->add_property ("movable", buf);
207
208         return *root;
209 }
210
211 /***********************************************************************/
212
213 struct MetricSectionSorter {
214     bool operator() (const MetricSection* a, const MetricSection* b) {
215             return a->start() < b->start();
216     }
217 };
218
219 TempoMap::TempoMap (nframes_t fr)
220 {
221         metrics = new Metrics;
222         _frame_rate = fr;
223         last_bbt_valid = false;
224         BBT_Time start;
225         
226         start.bars = 1;
227         start.beats = 1;
228         start.ticks = 0;
229
230         TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
231         MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor());
232
233         t->set_movable (false);
234         m->set_movable (false);
235
236         /* note: frame time is correct (zero) for both of these */
237         
238         metrics->push_back (t);
239         metrics->push_back (m);
240 }
241
242 TempoMap::~TempoMap ()
243 {
244 }
245
246 int
247 TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
248 {
249         if (when == section.start()) {
250                 return -1;
251         }
252
253         if (!section.movable()) {
254                 return 1;
255         }
256
257         Glib::RWLock::WriterLock  lm (lock);
258         MetricSectionSorter cmp;
259         BBT_Time corrected (when);
260         
261         if (dynamic_cast<MeterSection*>(&section) != 0) {
262                 if (corrected.beats > 1) {
263                         corrected.beats = 1;
264                         corrected.bars++;
265                 }
266         }
267         corrected.ticks = 0;
268
269         section.set_start (corrected);
270         metrics->sort (cmp);
271         timestamp_metrics (true);
272
273         return 0;
274 }
275
276 void
277 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
278 {
279         if (move_metric_section (tempo, when) == 0) {
280                 StateChanged (Change (0));
281         }
282 }
283
284 void
285 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
286 {
287         if (move_metric_section (meter, when) == 0) {
288                 StateChanged (Change (0));
289         }
290 }
291                 
292
293 void
294 TempoMap::remove_tempo (const TempoSection& tempo)
295 {
296         bool removed = false;
297
298         {
299                 Glib::RWLock::WriterLock lm (lock);
300                 Metrics::iterator i;
301
302                 for (i = metrics->begin(); i != metrics->end(); ++i) {
303                         if (dynamic_cast<TempoSection*> (*i) != 0) {
304                                 if (tempo.frame() == (*i)->frame()) {
305                                         if ((*i)->movable()) {
306                                                 metrics->erase (i);
307                                                 removed = true;
308                                                 break;
309                                         }
310                                 }
311                         }
312                 }
313         }
314
315         if (removed) {
316                 StateChanged (Change (0));
317         }
318 }
319
320 void
321 TempoMap::remove_meter (const MeterSection& tempo)
322 {
323         bool removed = false;
324
325         {
326                 Glib::RWLock::WriterLock lm (lock);
327                 Metrics::iterator i;
328
329                 for (i = metrics->begin(); i != metrics->end(); ++i) {
330                         if (dynamic_cast<MeterSection*> (*i) != 0) {
331                                 if (tempo.frame() == (*i)->frame()) {
332                                         if ((*i)->movable()) {
333                                                 metrics->erase (i);
334                                                 removed = true;
335                                                 break;
336                                         }
337                                 }
338                         }
339                 }
340         }
341
342         if (removed) {
343                 StateChanged (Change (0));
344         }
345 }
346
347 void
348 TempoMap::do_insert (MetricSection* section, bool with_bbt)
349 {
350         Metrics::iterator i;
351
352         for (i = metrics->begin(); i != metrics->end(); ++i) {
353                 
354                 if (with_bbt) {
355                         if ((*i)->start() < section->start()) {
356                                 continue;
357                         }
358                 } else {
359                         if ((*i)->frame() < section->frame()) {
360                                 continue;
361                         }                       
362                 }
363
364                 metrics->insert (i, section);
365                 break;
366         }
367         
368         if (i == metrics->end()) {
369                 metrics->insert (metrics->end(), section);
370         }
371         
372         timestamp_metrics (with_bbt);
373 }       
374
375 void
376 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
377 {
378         {
379                 Glib::RWLock::WriterLock lm (lock);
380
381                 /* new tempos always start on a beat */
382         
383                 where.ticks = 0;
384                 
385                 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), true);
386         }
387
388         StateChanged (Change (0));
389 }
390
391 void
392 TempoMap::add_tempo (const Tempo& tempo, nframes_t where)
393 {
394         {
395                 Glib::RWLock::WriterLock lm (lock);
396                 do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), false);
397         }
398
399         StateChanged (Change (0));
400 }
401
402 void
403 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
404 {
405         bool replaced = false;
406
407         { 
408                 Glib::RWLock::WriterLock lm (lock);
409                 Metrics::iterator i;
410                 
411                 for (i = metrics->begin(); i != metrics->end(); ++i) {
412                         TempoSection *ts;
413
414                         if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
415                                 
416                                 *((Tempo *) ts) = replacement;
417
418                                 replaced = true;
419                                 timestamp_metrics (true);
420                                 break;
421                         }
422                 }
423         }
424         
425         if (replaced) {
426                 StateChanged (Change (0));
427         }
428 }
429
430 void
431 TempoMap::add_meter (const Meter& meter, BBT_Time where)
432 {
433         {
434                 Glib::RWLock::WriterLock lm (lock);
435
436                 /* a new meter always starts a new bar on the first beat. so
437                    round the start time appropriately. remember that
438                    `where' is based on the existing tempo map, not
439                    the result after we insert the new meter.
440
441                 */
442
443                 if (where.beats != 1) {
444                         where.beats = 1;
445                         where.bars++;
446                 }
447
448                 /* new meters *always* start on a beat. */
449                 
450                 where.ticks = 0;
451
452                 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), true);
453         }
454
455         StateChanged (Change (0));
456 }
457
458 void
459 TempoMap::add_meter (const Meter& meter, nframes_t where)
460 {
461         {
462                 Glib::RWLock::WriterLock lm (lock);
463                 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), false);
464         }
465
466         StateChanged (Change (0));
467 }
468
469 void
470 TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
471 {
472         bool replaced = false;
473
474         { 
475                 Glib::RWLock::WriterLock lm (lock);
476                 Metrics::iterator i;
477                 
478                 for (i = metrics->begin(); i != metrics->end(); ++i) {
479                         MeterSection *ms;
480                         if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
481                                 
482                                 *((Meter*) ms) = replacement;
483
484                                 replaced = true;
485                                 timestamp_metrics (true);
486                                 break;
487                         }
488                 }
489         }
490         
491         if (replaced) {
492                 StateChanged (Change (0));
493         }
494 }
495
496 void
497 TempoMap::change_existing_tempo_at (nframes_t where, double beats_per_minute, double note_type)
498 {
499         Tempo newtempo (beats_per_minute, note_type);
500
501         TempoSection* prev;
502         TempoSection* first;
503         Metrics::iterator i;
504
505         /* find the TempoSection immediately preceding "where"
506          */
507
508         for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
509
510                 if ((*i)->frame() > where) {
511                         break;
512                 }
513
514                 TempoSection* t;
515
516                 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
517                         if (!first) {
518                                 first = t;
519                         }
520                         prev = t;
521                 }
522         }
523
524         if (!prev) {
525                 if (!first) {
526                         error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
527                         return;
528                 }
529
530                 prev = first;
531         }
532
533         /* reset */
534
535         *((Tempo*)prev) = newtempo;
536         StateChanged (Change (0));
537 }
538
539 const MeterSection&
540 TempoMap::first_meter () const
541 {
542         const MeterSection *m = 0;
543
544         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
545                 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
546                         return *m;
547                 }
548         }
549
550         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
551         /*NOTREACHED*/
552         return *m;
553 }
554
555 const TempoSection&
556 TempoMap::first_tempo () const
557 {
558         const TempoSection *t = 0;
559
560         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
561                 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
562                         return *t;
563                 }
564         }
565
566         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
567         /*NOTREACHED*/
568         return *t;
569 }
570
571 void
572 TempoMap::timestamp_metrics (bool use_bbt)
573 {
574         Metrics::iterator i;
575         const Meter* meter;
576         const Tempo* tempo;
577         Meter *m;
578         Tempo *t;
579
580         meter = &first_meter ();
581         tempo = &first_tempo ();
582
583         if (use_bbt) {
584
585                 nframes_t current = 0;
586                 nframes_t section_frames;
587                 BBT_Time start;
588                 BBT_Time end;
589
590                 for (i = metrics->begin(); i != metrics->end(); ++i) {
591                         
592                         end = (*i)->start();
593                         
594                         section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
595                         
596                         current += section_frames;
597                         
598                         start = end;
599                         
600                         (*i)->set_frame (current);
601                         
602                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
603                                 tempo = t;
604                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
605                                 meter = m;
606                         } else {
607                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
608                                 /*NOTREACHED*/
609                         }
610                 }
611
612         } else {
613
614                 bool first = true;
615
616                 for (i = metrics->begin(); i != metrics->end(); ++i) {
617
618                         BBT_Time bbt;
619
620                         bbt_time_with_metric ((*i)->frame(), bbt, Metric (*meter, *tempo));
621
622                         // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
623
624                         if (first) {
625                                 first = false;
626                         } else {
627                                 if (bbt.beats != 1 || bbt.ticks != 0) {
628                                         bbt.bars += 1;
629                                         bbt.beats = 1;
630                                         bbt.ticks = 0;
631                                 }
632                         }
633
634                         // cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
635
636                         (*i)->set_start (bbt);
637
638                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
639                                 tempo = t;
640                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
641                                 meter = m;
642                         } else {
643                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
644                                 /*NOTREACHED*/
645                         }
646                 }
647         }
648
649         // dump (cerr);
650 }
651
652 TempoMap::Metric
653 TempoMap::metric_at (nframes_t frame) const
654 {
655         Metric m (first_meter(), first_tempo());
656         const Meter* meter;
657         const Tempo* tempo;
658
659         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
660            at something, because we insert the default tempo and meter during
661            TempoMap construction.
662
663            now see if we can find better candidates.
664         */
665
666         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
667
668                 if ((*i)->frame() > frame) {
669                         break;
670                 }
671
672                 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
673                         m.set_tempo (*tempo);
674                 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
675                         m.set_meter (*meter);
676                 }
677
678                 m.set_frame ((*i)->frame ());
679                 m.set_start ((*i)->start ());
680         }
681         
682         return m;
683 }
684
685 TempoMap::Metric
686 TempoMap::metric_at (BBT_Time bbt) const
687 {
688         Metric m (first_meter(), first_tempo());
689         const Meter* meter;
690         const Tempo* tempo;
691
692         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
693            at something, because we insert the default tempo and meter during
694            TempoMap construction.
695
696            now see if we can find better candidates.
697         */
698
699         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
700
701                 BBT_Time section_start ((*i)->start());
702
703                 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
704                         break;
705                 }
706
707                 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
708                         m.set_tempo (*tempo);
709                 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
710                         m.set_meter (*meter);
711                 }
712                 
713                 m.set_frame ((*i)->frame ());
714                 m.set_start (section_start);
715         }
716
717         return m;
718 }
719
720 void
721 TempoMap::bbt_time (nframes_t frame, BBT_Time& bbt) const
722 {
723         {
724                 Glib::RWLock::ReaderLock lm (lock);
725                 bbt_time_unlocked (frame, bbt);
726         }
727 }
728
729 void
730 TempoMap::bbt_time_unlocked (nframes_t frame, BBT_Time& bbt) const
731 {
732         bbt_time_with_metric (frame, bbt, metric_at (frame));
733 }
734
735 void
736 TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& metric) const
737 {
738         nframes_t frame_diff;
739
740         uint32_t xtra_bars = 0;
741         double xtra_beats = 0;
742         double beats = 0;
743
744         const double beats_per_bar = metric.meter().beats_per_bar();
745         const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
746         const double beat_frames = metric.tempo().frames_per_beat (_frame_rate, metric.meter());
747
748         /* now compute how far beyond that point we actually are. */
749
750         frame_diff = frame - metric.frame();
751
752         xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar);
753         frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar);
754         xtra_beats = (double) frame_diff / beat_frames;
755
756
757         /* and set the returned value */
758
759         /* and correct beat/bar shifts to match the meter.
760           remember: beat and bar counting is 1-based, 
761           not zero-based 
762           also the meter may contain a fraction
763         */
764         
765         bbt.bars = metric.start().bars + xtra_bars; 
766
767         beats = (double) metric.start().beats + xtra_beats;
768
769         bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) );
770
771         beats = fmod(beats - 1, beats_per_bar )+ 1.0;
772         bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat));
773         bbt.beats = (uint32_t) floor(beats);
774
775 }
776
777
778 nframes_t 
779 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
780 {
781         /* for this to work with fractional measure types, start and end have to be "legal" BBT types, 
782         that means that the beats and ticks should be inside a bar
783         */
784
785         nframes_t frames = 0;
786         nframes_t start_frame = 0;
787         nframes_t end_frame = 0;
788
789         Metric m = metric_at (start);
790
791         uint32_t bar_offset = start.bars - m.start().bars;
792
793         double  beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1) 
794                 + start.ticks/Meter::ticks_per_beat;
795
796
797         start_frame = m.frame() + (nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
798
799         m =  metric_at(end);
800
801         bar_offset = end.bars - m.start().bars;
802
803         beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1) 
804                 + end.ticks/Meter::ticks_per_beat;
805
806         end_frame = m.frame() + (nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
807
808         frames = end_frame - start_frame;
809
810         return frames;
811         
812 }       
813
814 nframes_t 
815 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
816 {
817         /*this is used in timestamping the metrics by actually counting the beats */ 
818
819         nframes_t frames = 0;
820         uint32_t bar = start.bars;
821         double beat = (double) start.beats;
822         double beats_counted = 0;
823         double beats_per_bar = 0;
824         double beat_frames = 0;
825
826         beats_per_bar = meter.beats_per_bar();
827         beat_frames = tempo.frames_per_beat (_frame_rate,meter);
828
829         frames = 0;
830
831         while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
832                 
833                 if (beat >= beats_per_bar) {
834                         beat = 1;
835                         ++bar;
836                         ++beats_counted;
837                 } else {
838                         ++beat;
839                         ++beats_counted;
840                         if (beat > beats_per_bar) {
841                                 /* this is a fractional beat at the end of a fractional bar
842                                    so it should only count for the fraction */
843                                 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
844                         }
845                 }
846         }
847         
848         frames = (nframes_t) floor (beats_counted * beat_frames);
849
850         return frames;
851         
852 }       
853
854 nframes_t 
855 TempoMap::frame_time (const BBT_Time& bbt) const
856 {
857         BBT_Time start ; /* 1|1|0 */
858
859         return  count_frames_between ( start, bbt);
860 }
861
862 nframes_t 
863 TempoMap::bbt_duration_at (nframes_t pos, const BBT_Time& bbt, int dir) const
864 {
865         nframes_t frames = 0;
866
867         BBT_Time when;
868         bbt_time(pos,when);
869
870         {
871                 Glib::RWLock::ReaderLock lm (lock);
872                 frames = bbt_duration_at_unlocked (when, bbt,dir);
873         }
874
875         return frames;
876 }
877
878 nframes_t 
879 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
880 {
881
882         nframes_t frames = 0;
883
884         double beats_per_bar;
885         BBT_Time result;
886         
887         result.bars = max(1U,when.bars + dir * bbt.bars) ;
888         result.beats = 1;
889         result.ticks = 0;
890
891         Metric  metric = metric_at(result);
892         beats_per_bar = metric.meter().beats_per_bar();
893
894
895
896         /*reduce things to legal bbt  values 
897           we have to handle possible fractional=shorter beats at the end of measures
898           and things like 0|11|9000  as a duration in a 4.5/4 measure
899           the musical decision is that the fractional beat is also a beat , although a shorter one 
900         */
901
902     
903         if (dir >= 0) {
904                 result.beats = when.beats +  bbt.beats;
905                 result.ticks = when.ticks +  bbt.ticks;
906
907                 while (result.beats >= (beats_per_bar+1)) {
908                         result.bars++;
909                         result.beats -=  (uint32_t) ceil(beats_per_bar);
910                         metric = metric_at(result); // maybe there is a meter change
911                         beats_per_bar = metric.meter().beats_per_bar();
912                         
913                 }
914                 /*we now counted the beats and landed in the target measure, now deal with ticks 
915                   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
916                   and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
917                 */
918
919                 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
920                  */
921
922                 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
923                                         (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat 
924                                            : Meter::ticks_per_beat );
925
926                 while (result.ticks >= ticks_at_beat) {
927                         result.beats++;
928                         result.ticks -= ticks_at_beat;
929                         if  (result.beats >= (beats_per_bar+1)) {
930                                 result.bars++;
931                                 result.beats = 1;
932                                 metric = metric_at(result); // maybe there is a meter change
933                                 beats_per_bar = metric.meter().beats_per_bar();
934                         }
935                         ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
936                                        (1 - (ceil(beats_per_bar) - beats_per_bar) )* Meter::ticks_per_beat 
937                                        : Meter::ticks_per_beat);
938
939                 }
940
941           
942         } else {
943                 uint32_t b = bbt.beats;
944
945                 /* count beats */
946                 while( b > when.beats ) {
947                         
948                         result.bars = max(1U,result.bars-- ) ;
949                         metric = metric_at(result); // maybe there is a meter change
950                         beats_per_bar = metric.meter().beats_per_bar();
951                         if (b >= ceil(beats_per_bar)) {
952                                 
953                                 b -= (uint32_t) ceil(beats_per_bar);
954                         } else {
955                                 b = (uint32_t) ceil(beats_per_bar)- b + when.beats ;
956                         }
957                 }
958                 result.beats = when.beats - b;
959                 
960                 /*count ticks */
961
962                 if (bbt.ticks <= when.ticks) {
963                         result.ticks = when.ticks - bbt.ticks;
964                 } else {
965
966                         uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
967                         uint32_t t = bbt.ticks - when.ticks;
968
969                         do {
970
971                                 if (result.beats == 1) {
972                                         result.bars = max(1U,result.bars-- ) ;
973                                         metric = metric_at(result); // maybe there is a meter change
974                                         beats_per_bar = metric.meter().beats_per_bar();
975                                         result.beats = (uint32_t) ceil(beats_per_bar);
976                                         ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat) ;
977                                 } else {
978                                         result.beats --;
979                                         ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
980                                 }
981                                                 
982                                 if (t <= ticks_at_beat) {
983                                         result.ticks = ticks_at_beat - t; 
984                                 } else {
985                                         t-= ticks_at_beat;
986                                 }
987                         } while (t > ticks_at_beat);
988
989                 }
990
991
992         }
993
994         if (dir < 0 ) {
995                 frames = count_frames_between( result,when);
996         } else {
997                 frames = count_frames_between(when,result);
998         }
999
1000         return frames;
1001 }
1002
1003
1004
1005 nframes_t
1006 TempoMap::round_to_bar (nframes_t fr, int dir)
1007 {
1008         {
1009                 Glib::RWLock::ReaderLock lm (lock);
1010                 return round_to_type (fr, dir, Bar);
1011         }
1012 }
1013
1014
1015 nframes_t
1016 TempoMap::round_to_beat (nframes_t fr, int dir)
1017 {
1018         {
1019                 Glib::RWLock::ReaderLock lm (lock);
1020                 return round_to_type (fr, dir, Beat);
1021         }
1022 }
1023
1024 nframes_t
1025
1026 TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
1027 {
1028
1029         BBT_Time the_beat;
1030         uint32_t ticks_one_half_subdivisions_worth;
1031         uint32_t ticks_one_subdivisions_worth;
1032
1033         bbt_time(fr, the_beat);
1034
1035         ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
1036         ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
1037
1038         if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
1039           uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
1040           if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
1041             the_beat.beats++;
1042             the_beat.ticks += difference;
1043             the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
1044           } else {  
1045             the_beat.ticks += difference;
1046           }
1047         } else {
1048           the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
1049         }
1050
1051         return frame_time (the_beat);
1052 }
1053
1054 nframes_t
1055
1056 TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
1057 {
1058         Metric metric = metric_at (frame);
1059         BBT_Time bbt;
1060         BBT_Time start;
1061         bbt_time_with_metric (frame, bbt, metric);
1062
1063         switch (type) {
1064         case Bar:
1065                 if (dir < 0) {
1066                         /* relax */
1067
1068                 } else if (dir > 0) {
1069                         if (bbt.beats > 0) {
1070                                 bbt.bars++;
1071                         }
1072                 } else {
1073                         if (bbt.beats > metric.meter().beats_per_bar()/2) {
1074                                 bbt.bars++;
1075                         }
1076
1077                 }
1078                 bbt.beats = 1;
1079                 bbt.ticks = 0;
1080                 break;
1081         
1082         case Beat:
1083                 if (dir < 0) {
1084                         /* relax */
1085                 } else if (dir > 0) {
1086                         if (bbt.ticks > 0) {
1087                                 bbt.beats++;
1088                         }
1089                 } else {
1090                         if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1091                                 bbt.beats++;
1092                         }
1093                 }
1094                 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1095                         bbt.beats = 1;
1096                         bbt.bars++;
1097                 }
1098                 bbt.ticks = 0;
1099                 break;
1100         
1101         }
1102
1103         /* 
1104            cerr << "for " << frame << " round to " << bbt << " using "
1105            << metric.start()
1106            << endl;
1107         */
1108
1109         return metric.frame() + count_frames_between (metric.start(), bbt);
1110 }
1111
1112 TempoMap::BBTPointList *
1113 TempoMap::get_points (nframes_t lower, nframes_t upper) const
1114 {
1115
1116         Metrics::const_iterator i;
1117         BBTPointList *points;
1118         double current;
1119         const MeterSection* meter;
1120         const MeterSection* m;
1121         const TempoSection* tempo;
1122         const TempoSection* t;
1123         uint32_t bar;
1124         uint32_t beat;
1125         double beats_per_bar;
1126         double beat_frame;
1127         double beat_frames;
1128         double frames_per_bar;
1129         double delta_bars;
1130         double delta_beats;
1131         double dummy;
1132         nframes_t limit;
1133
1134         meter = &first_meter ();
1135         tempo = &first_tempo ();
1136
1137         /* find the starting point */
1138
1139         for (i = metrics->begin(); i != metrics->end(); ++i) {
1140
1141                 if ((*i)->frame() > lower) {
1142                         break;
1143                 }
1144
1145                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1146                         tempo = t;
1147                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1148                         meter = m;
1149                 }
1150         }
1151
1152         /* We now have:
1153            
1154            meter -> the Meter for "lower"
1155            tempo -> the Tempo for "lower"
1156            i     -> for first new metric after "lower", possibly metrics->end()
1157
1158            Now start generating points.
1159         */
1160
1161         beats_per_bar = meter->beats_per_bar ();
1162         frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1163         beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1164         
1165         if (meter->frame() > tempo->frame()) {
1166                 bar = meter->start().bars;
1167                 beat = meter->start().beats;
1168                 current = meter->frame();
1169         } else {
1170                 bar = tempo->start().bars;
1171                 beat = tempo->start().beats;
1172                 current = tempo->frame();
1173         }
1174
1175         /* initialize current to point to the bar/beat just prior to the
1176            lower frame bound passed in.  assumes that current is initialized
1177            above to be on a beat.
1178         */
1179         
1180         delta_bars = (lower-current) / frames_per_bar;
1181         delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1182         current += (floor(delta_bars) * frames_per_bar) +  (floor(delta_beats) * beat_frames);
1183
1184         // adjust bars and beats too
1185         bar += (uint32_t) (floor(delta_bars));
1186         beat += (uint32_t) (floor(delta_beats));
1187
1188         points = new BBTPointList;
1189                 
1190         do {
1191
1192                 if (i == metrics->end()) {
1193                         limit = upper;
1194                 } else {
1195                         limit = (*i)->frame();
1196                 }
1197
1198                 limit = min (limit, upper);
1199
1200                 while (current < limit) {
1201                         
1202                         /* if we're at the start of a bar, add bar point */
1203
1204                         if (beat == 1) {
1205                                 if (current >= lower) {
1206                                         // cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
1207                                         points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1));
1208
1209                                 }
1210                         }
1211
1212                         /* add some beats if we can */
1213
1214                         beat_frame = current;
1215
1216                         while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1217                                 if (beat_frame >= lower) {
1218                                         // cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
1219                                         points->push_back (BBTPoint (*meter, *tempo, (nframes_t) rint(beat_frame), Beat, bar, beat));
1220                                 }
1221                                 beat_frame += beat_frames;
1222                                 current+= beat_frames;
1223                                
1224                                 beat++;
1225                         }
1226
1227                         if (beat > ceil(beats_per_bar) || i != metrics->end()) {
1228
1229                                 /* we walked an entire bar. its
1230                                    important to move `current' forward
1231                                    by the actual frames_per_bar, not move it to
1232                                    an integral beat_frame, so that metrics with
1233                                    non-integral beats-per-bar have
1234                                    their bar positions set
1235                                    correctly. consider a metric with
1236                                    9-1/2 beats-per-bar. the bar we
1237                                    just filled had  10 beat marks,
1238                                    but the bar end is 1/2 beat before
1239                                    the last beat mark.
1240                                    And it is also possible that a tempo 
1241                                    change occured in the middle of a bar, 
1242                                    so we subtract the possible extra fraction from the current
1243                                 */
1244
1245                                 if (beat > ceil (beats_per_bar)) {
1246                                         /* next bar goes where the numbers suggest */
1247                                         current -=  beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1248                                 } else {
1249                                         /* next bar goes where the next metric is */
1250                                         current = limit;
1251                                 }
1252                                 bar++;
1253                                 beat = 1;
1254                         } 
1255                 
1256                 }
1257
1258                 /* if we're done, then we're done */
1259
1260                 if (current >= upper) {
1261                         break;
1262                 }
1263
1264                 /* i is an iterator that refers to the next metric (or none).
1265                    if there is a next metric, move to it, and continue.
1266                 */
1267
1268                 if (i != metrics->end()) {
1269
1270                         if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1271                                 tempo = t;
1272                         } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1273                                 meter = m;
1274                                 /* new MeterSection, beat always returns to 1 */
1275                                 beat = 1;
1276                         }
1277
1278                         beats_per_bar = meter->beats_per_bar ();
1279                         frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1280                         beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
1281                         
1282                         ++i;
1283                 }
1284
1285         } while (1);
1286
1287         return points;
1288 }       
1289
1290 const TempoSection&
1291 TempoMap::tempo_section_at (nframes_t frame)
1292 {
1293         Glib::RWLock::ReaderLock lm (lock);
1294         Metrics::iterator i;
1295         TempoSection* prev = 0;
1296         
1297         for (i = metrics->begin(); i != metrics->end(); ++i) {
1298                 TempoSection* t;
1299
1300                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1301
1302                         if ((*i)->frame() > frame) {
1303                                 break;
1304                         }
1305
1306                         prev = t;
1307                 }
1308         }
1309
1310         if (prev == 0) {
1311                 fatal << endmsg;
1312         }
1313
1314         return *prev;
1315 }
1316
1317 const Tempo&
1318 TempoMap::tempo_at (nframes_t frame)
1319 {
1320         Metric m (metric_at (frame));
1321         return m.tempo();
1322 }
1323
1324
1325 const Meter&
1326 TempoMap::meter_at (nframes_t frame)
1327 {
1328         Metric m (metric_at (frame));
1329         return m.meter();
1330 }
1331
1332 XMLNode&
1333 TempoMap::get_state ()
1334 {
1335         Metrics::const_iterator i;
1336         XMLNode *root = new XMLNode ("TempoMap");
1337
1338         {
1339                 Glib::RWLock::ReaderLock lm (lock);
1340                 for (i = metrics->begin(); i != metrics->end(); ++i) {
1341                         root->add_child_nocopy ((*i)->get_state());
1342                 }
1343         }
1344
1345         return *root;
1346 }
1347
1348 int
1349 TempoMap::set_state (const XMLNode& node)
1350 {
1351         {
1352                 Glib::RWLock::WriterLock lm (lock);
1353
1354                 XMLNodeList nlist;
1355                 XMLNodeConstIterator niter;
1356                 Metrics old_metrics (*metrics);
1357                 
1358                 metrics->clear();
1359
1360                 nlist = node.children();
1361                 
1362                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1363                         XMLNode* child = *niter;
1364                         
1365                         if (child->name() == TempoSection::xml_state_node_name) {
1366                                 
1367                                 try {
1368                                         metrics->push_back (new TempoSection (*child));
1369                                 }
1370                                 
1371                                 catch (failed_constructor& err){
1372                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1373                                         *metrics = old_metrics;
1374                                         break;
1375                                 }
1376                                 
1377                         } else if (child->name() == MeterSection::xml_state_node_name) {
1378                                 
1379                                 try {
1380                                         metrics->push_back (new MeterSection (*child));
1381                                 }
1382                                 
1383                                 catch (failed_constructor& err) {
1384                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1385                                         *metrics = old_metrics;
1386                                         break;
1387                                 }
1388                         }
1389                 }
1390                 
1391                 if (niter == nlist.end()) {
1392                         
1393                         MetricSectionSorter cmp;
1394                         metrics->sort (cmp);
1395                         timestamp_metrics (true);
1396                 }
1397         }
1398         
1399         StateChanged (Change (0));
1400
1401         return 0;
1402 }
1403
1404 void
1405 TempoMap::dump (std::ostream& o) const
1406 {
1407         const MeterSection* m;
1408         const TempoSection* t;
1409         
1410         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1411
1412                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1413                         o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
1414                           << t->movable() << ')' << endl;
1415                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1416                         o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame() 
1417                           << " (move? " << m->movable() << ')' << endl;
1418                 }
1419         }
1420 }
1421