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