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