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