remove StateManager code entirely and more debugging output cruft
[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         /* XXX just keeping this for reference
928
929         TempoMap::BBTPointList::iterator i;
930         TempoMap::BBTPointList *more_zoomed_bbt_points;
931         nframes_t frame_one_beats_worth;
932         nframes_t pos = 0;
933         nframes_t next_pos = 0 ;
934         double tempo = 1;
935         double frames_one_subdivisions_worth;
936         bool fr_has_changed = false;
937
938         int n;
939
940         frame_one_beats_worth = (nframes_t) ::floor ((double)  _frame_rate *  60 / 20 ); //one beat @ 20 bpm
941         {
942           Glib::RWLock::ReaderLock lm (lock);
943           more_zoomed_bbt_points = get_points((fr >= frame_one_beats_worth) ? 
944                                             fr - frame_one_beats_worth : 0, fr+frame_one_beats_worth );
945         }
946         if (more_zoomed_bbt_points == 0 || more_zoomed_bbt_points->empty()) {
947                 return fr;
948         }
949
950         for (i = more_zoomed_bbt_points->begin(); i != more_zoomed_bbt_points->end(); i++) {
951                 if  ((*i).frame <= fr) {
952                         pos = (*i).frame;
953                         tempo = (*i).tempo->beats_per_minute();
954                         
955                 } else {
956                         i++;
957                         next_pos = (*i).frame;
958                         break;
959                 }
960         }
961         frames_one_subdivisions_worth = ((double) _frame_rate *  60 / (sub_num * tempo));
962
963         for (n = sub_num; n > 0; n--) {
964                 if (fr >= (pos + ((n - 0.5) * frames_one_subdivisions_worth))) {
965                         fr = (nframes_t) round(pos + (n  * frames_one_subdivisions_worth));
966                         if (fr > next_pos) {
967                                 fr = next_pos;  //take care of fractional beats that don't match the subdivision asked
968                         }
969                         fr_has_changed = true;
970                         break;
971                 }
972         }
973
974         if (!fr_has_changed) {
975                 fr = pos;
976         }
977
978         delete more_zoomed_bbt_points;
979         return fr ;
980
981         */
982
983 }
984
985 nframes_t
986
987 TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
988 {
989         Metric metric = metric_at (frame);
990         BBT_Time bbt;
991         BBT_Time start;
992         bbt_time_with_metric (frame, bbt, metric);
993
994         switch (type) {
995         case Bar:
996                 if (dir < 0) {
997                         /* relax */
998
999                 } else if (dir > 0) {
1000                         if (bbt.beats > 0) {
1001                                 bbt.bars++;
1002                         }
1003                 } else {
1004                         if (bbt.beats > metric.meter().beats_per_bar()/2) {
1005                                 bbt.bars++;
1006                         }
1007
1008                 }
1009                 bbt.beats = 1;
1010                 bbt.ticks = 0;
1011                 break;
1012         
1013         case Beat:
1014                 if (dir < 0) {
1015                         /* relax */
1016                 } else if (dir > 0) {
1017                         if (bbt.ticks > 0) {
1018                                 bbt.beats++;
1019                         }
1020                 } else {
1021                         if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1022                                 bbt.beats++;
1023                         }
1024                 }
1025                 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1026                         bbt.beats = 1;
1027                         bbt.bars++;
1028                 }
1029                 bbt.ticks = 0;
1030                 break;
1031         
1032         }
1033
1034         return metric.frame() + count_frames_between (metric.start(), bbt);
1035 }
1036
1037 TempoMap::BBTPointList *
1038 TempoMap::get_points (nframes_t lower, nframes_t upper) const
1039 {
1040
1041         Metrics::const_iterator i;
1042         BBTPointList *points;
1043         double current;
1044         const MeterSection* meter;
1045         const MeterSection* m;
1046         const TempoSection* tempo;
1047         const TempoSection* t;
1048         uint32_t bar;
1049         uint32_t beat;
1050         double beats_per_bar;
1051         double beat_frame;
1052         double beat_frames;
1053         double frames_per_bar;
1054         double delta_bars;
1055         double delta_beats;
1056         double dummy;
1057         nframes_t limit;
1058
1059         meter = &first_meter ();
1060         tempo = &first_tempo ();
1061
1062         /* find the starting point */
1063
1064         for (i = metrics->begin(); i != metrics->end(); ++i) {
1065
1066                 if ((*i)->frame() > lower) {
1067                         break;
1068                 }
1069
1070                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1071                         tempo = t;
1072                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1073                         meter = m;
1074                 }
1075         }
1076
1077         /* We now have:
1078            
1079            meter -> the Meter for "lower"
1080            tempo -> the Tempo for "lower"
1081            i     -> for first new metric after "lower", possibly metrics->end()
1082
1083            Now start generating points.
1084         */
1085
1086         beats_per_bar = meter->beats_per_bar ();
1087         frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1088         beat_frames = tempo->frames_per_beat (_frame_rate);
1089         
1090         if (meter->frame() > tempo->frame()) {
1091                 bar = meter->start().bars;
1092                 beat = meter->start().beats;
1093                 current = meter->frame();
1094         } else {
1095                 bar = tempo->start().bars;
1096                 beat = tempo->start().beats;
1097                 current = tempo->frame();
1098         }
1099
1100         /* initialize current to point to the bar/beat just prior to the
1101            lower frame bound passed in.  assumes that current is initialized
1102            above to be on a beat.
1103         */
1104         
1105         delta_bars = (lower-current) / frames_per_bar;
1106         delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
1107         current += (floor(delta_bars) * frames_per_bar) +  (floor(delta_beats) * beat_frames);
1108
1109         // adjust bars and beats too
1110         bar += (uint32_t) (floor(delta_bars));
1111         beat += (uint32_t) (floor(delta_beats));
1112
1113         points = new BBTPointList;
1114                 
1115         do {
1116
1117                 if (i == metrics->end()) {
1118                         limit = upper;
1119                 } else {
1120                         limit = (*i)->frame();
1121                 }
1122
1123                 limit = min (limit, upper);
1124
1125                 while (current < limit) {
1126                         
1127                         /* if we're at the start of a bar, add bar point */
1128
1129                         if (beat == 1) {
1130                                 if (current >= lower) {
1131                                         points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1));
1132
1133                                 }
1134                         }
1135
1136                         /* add some beats if we can */
1137
1138                         beat_frame = current;
1139
1140                         while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1141                                 if (beat_frame >= lower) {
1142                                         points->push_back (BBTPoint (*meter, *tempo, (nframes_t) rint(beat_frame), Beat, bar, beat));
1143                                 }
1144                                 beat_frame += beat_frames;
1145                                 current+= beat_frames;
1146                                
1147                                 beat++;
1148                         }
1149
1150                         if (beat > ceil(beats_per_bar) ) {
1151
1152                                 /* we walked an entire bar. its
1153                                    important to move `current' forward
1154                                    by the actual frames_per_bar, not move it to
1155                                    an integral beat_frame, so that metrics with
1156                                    non-integral beats-per-bar have
1157                                    their bar positions set
1158                                    correctly. consider a metric with
1159                                    9-1/2 beats-per-bar. the bar we
1160                                    just filled had  10 beat marks,
1161                                    but the bar end is 1/2 beat before
1162                                    the last beat mark.
1163                                    And it is also possible that a tempo 
1164                                    change occured in the middle of a bar, 
1165                                    so we subtract the possible extra fraction from the current
1166                                 */
1167
1168                                 current -=  beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1169                                 bar++;
1170                                 beat = 1;
1171
1172                         } 
1173                 
1174                 }
1175
1176                 /* if we're done, then we're done */
1177
1178                 if (current >= upper) {
1179                         break;
1180                 }
1181
1182                 /* i is an iterator that refers to the next metric (or none).
1183                    if there is a next metric, move to it, and continue.
1184                 */
1185
1186                 if (i != metrics->end()) {
1187
1188                         if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1189                                 tempo = t;
1190                         } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1191                                 meter = m;
1192                                 /* new MeterSection, beat always returns to 1 */
1193                                 beat = 1;
1194                         }
1195
1196                         beats_per_bar = meter->beats_per_bar ();
1197                         frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1198                         beat_frames = tempo->frames_per_beat (_frame_rate);
1199                         
1200                         ++i;
1201                 }
1202
1203         } while (1);
1204
1205         return points;
1206 }       
1207
1208 const Tempo&
1209 TempoMap::tempo_at (nframes_t frame)
1210 {
1211         Metric m (metric_at (frame));
1212         return m.tempo();
1213 }
1214
1215
1216 const Meter&
1217 TempoMap::meter_at (nframes_t frame)
1218 {
1219         Metric m (metric_at (frame));
1220         return m.meter();
1221 }
1222
1223 XMLNode&
1224 TempoMap::get_state ()
1225 {
1226         Metrics::const_iterator i;
1227         XMLNode *root = new XMLNode ("TempoMap");
1228
1229         {
1230                 Glib::RWLock::ReaderLock lm (lock);
1231                 for (i = metrics->begin(); i != metrics->end(); ++i) {
1232                         root->add_child_nocopy ((*i)->get_state());
1233                 }
1234         }
1235
1236         return *root;
1237 }
1238
1239 int
1240 TempoMap::set_state (const XMLNode& node)
1241 {
1242         {
1243                 Glib::RWLock::WriterLock lm (lock);
1244
1245                 XMLNodeList nlist;
1246                 XMLNodeConstIterator niter;
1247                 Metrics old_metrics (*metrics);
1248                 
1249                 metrics->clear();
1250
1251                 nlist = node.children();
1252                 
1253                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1254                         XMLNode* child = *niter;
1255                         
1256                         if (child->name() == TempoSection::xml_state_node_name) {
1257                                 
1258                                 try {
1259                                         metrics->push_back (new TempoSection (*child));
1260                                 }
1261                                 
1262                                 catch (failed_constructor& err){
1263                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1264                                         *metrics = old_metrics;
1265                                         break;
1266                                 }
1267                                 
1268                         } else if (child->name() == MeterSection::xml_state_node_name) {
1269                                 
1270                                 try {
1271                                         metrics->push_back (new MeterSection (*child));
1272                                 }
1273                                 
1274                                 catch (failed_constructor& err) {
1275                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1276                                         *metrics = old_metrics;
1277                                         break;
1278                                 }
1279                         }
1280                 }
1281                 
1282                 if (niter == nlist.end()) {
1283                         
1284                         MetricSectionSorter cmp;
1285                         metrics->sort (cmp);
1286                         timestamp_metrics ();
1287                 }
1288         }
1289         
1290         StateChanged (Change (0));
1291
1292         return 0;
1293 }
1294
1295 void
1296 TempoMap::dump (std::ostream& o) const
1297 {
1298         const MeterSection* m;
1299         const TempoSection* t;
1300         
1301         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1302
1303                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1304                         o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM at " << t->start() << " frame= " << t->frame() << " (move? "
1305                           << t->movable() << ')' << endl;
1306                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1307                         o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame() 
1308                           << " (move? " << m->movable() << ')' << endl;
1309                 }
1310         }
1311 }
1312