fixes for destructive track offsets of various kinds; move from jack_nframes_t -...
[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         in_set_state = false;
210         
211         start.bars = 1;
212         start.beats = 1;
213         start.ticks = 0;
214
215         TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute());
216         MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor());
217
218         t->set_movable (false);
219         m->set_movable (false);
220
221         /* note: frame time is correct (zero) for both of these */
222         
223         metrics->push_back (t);
224         metrics->push_back (m);
225         
226         save_state (_("initial"));
227 }
228
229 TempoMap::~TempoMap ()
230 {
231 }
232
233 int
234 TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
235 {
236         if (when == section.start()) {
237                 return -1;
238         }
239
240         if (!section.movable()) {
241                 return 1;
242         }
243
244         Glib::RWLock::WriterLock  lm (lock);
245         MetricSectionSorter cmp;
246         BBT_Time corrected (when);
247         
248         if (dynamic_cast<MeterSection*>(&section) != 0) {
249                 if (corrected.beats > 1) {
250                         corrected.beats = 1;
251                         corrected.bars++;
252                 }
253         }
254         corrected.ticks = 0;
255
256         section.set_start (corrected);
257         metrics->sort (cmp);
258         timestamp_metrics ();
259         save_state (_("move metric"));
260
261         return 0;
262 }
263
264 void
265 TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
266 {
267         if (move_metric_section (tempo, when) == 0) {
268                 send_state_changed (Change (0));
269         }
270 }
271
272 void
273 TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
274 {
275         if (move_metric_section (meter, when) == 0) {
276                 send_state_changed (Change (0));
277         }
278 }
279                 
280
281 void
282 TempoMap::remove_tempo (const TempoSection& tempo)
283 {
284         bool removed = false;
285
286         {
287                 Glib::RWLock::WriterLock lm (lock);
288                 Metrics::iterator i;
289
290                 for (i = metrics->begin(); i != metrics->end(); ++i) {
291                         if (dynamic_cast<TempoSection*> (*i) != 0) {
292                                 if (tempo.frame() == (*i)->frame()) {
293                                         if ((*i)->movable()) {
294                                                 metrics->erase (i);
295                                                 removed = true;
296                                                 break;
297                                         }
298                                 }
299                         }
300                 }
301         }
302
303         if (removed) {
304                 send_state_changed (Change (0));
305         }
306 }
307
308 void
309 TempoMap::remove_meter (const MeterSection& tempo)
310 {
311         bool removed = false;
312
313         {
314                 Glib::RWLock::WriterLock lm (lock);
315                 Metrics::iterator i;
316
317                 for (i = metrics->begin(); i != metrics->end(); ++i) {
318                         if (dynamic_cast<MeterSection*> (*i) != 0) {
319                                 if (tempo.frame() == (*i)->frame()) {
320                                         if ((*i)->movable()) {
321                                                 metrics->erase (i);
322                                                 removed = true;
323                                                 break;
324                                         }
325                                 }
326                         }
327                 }
328
329                 if (removed) {
330                         save_state (_("metric removed"));
331                 }
332         }
333
334         if (removed) {
335                 send_state_changed (Change (0));
336         }
337 }
338
339 void
340 TempoMap::do_insert (MetricSection* section)
341 {
342         Metrics::iterator i;
343
344         for (i = metrics->begin(); i != metrics->end(); ++i) {
345                 
346                 if ((*i)->start() < section->start()) {
347                         continue;
348                 }
349                 
350                 metrics->insert (i, section);
351                 break;
352         }
353         
354         if (i == metrics->end()) {
355                 metrics->insert (metrics->end(), section);
356         }
357         
358         timestamp_metrics ();
359 }       
360
361 void
362 TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
363 {
364         {
365                 Glib::RWLock::WriterLock lm (lock);
366
367                 /* new tempos always start on a beat */
368         
369                 where.ticks = 0;
370                 
371                 do_insert (new TempoSection (where, tempo.beats_per_minute()));
372
373                 save_state (_("add tempo"));
374         }
375
376         send_state_changed (Change (0));
377 }
378
379 void
380 TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
381 {
382         bool replaced = false;
383
384         { 
385                 Glib::RWLock::WriterLock lm (lock);
386                 Metrics::iterator i;
387                 
388                 for (i = metrics->begin(); i != metrics->end(); ++i) {
389                         TempoSection *ts;
390
391                         if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
392                                 
393                                 *((Tempo *) ts) = replacement;
394
395                                 replaced = true;
396                                 timestamp_metrics ();
397                                 break;
398                         }
399                 }
400
401                 if (replaced) {
402                         save_state (_("replace tempo"));
403                 }
404         }
405         
406         if (replaced) {
407                 send_state_changed (Change (0));
408         }
409 }
410
411 void
412 TempoMap::add_meter (const Meter& meter, BBT_Time where)
413 {
414         {
415                 Glib::RWLock::WriterLock lm (lock);
416
417                 /* a new meter always starts a new bar on the first beat. so
418                    round the start time appropriately. remember that
419                    `where' is based on the existing tempo map, not
420                    the result after we insert the new meter.
421
422                 */
423
424                 if (where.beats != 1) {
425                         where.beats = 1;
426                         where.bars++;
427                 }
428
429                 /* new meters *always* start on a beat. */
430                 
431                 where.ticks = 0;
432
433                 do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()));
434
435                 save_state (_("add meter"));
436         }
437
438         send_state_changed (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                 if (replaced) {
463                         save_state (_("replaced meter"));
464                 }
465         }
466         
467         if (replaced) {
468                 send_state_changed (Change (0));
469         }
470 }
471
472 const MeterSection&
473 TempoMap::first_meter () const
474 {
475         const MeterSection *m = 0;
476
477         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
478                 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
479                         return *m;
480                 }
481         }
482
483         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
484         /*NOTREACHED*/
485         return *m;
486 }
487
488 const TempoSection&
489 TempoMap::first_tempo () const
490 {
491         const TempoSection *t = 0;
492
493         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
494                 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
495                         return *t;
496                 }
497         }
498
499         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
500         /*NOTREACHED*/
501         return *t;
502 }
503
504 void
505 TempoMap::timestamp_metrics ()
506 {
507         Metrics::iterator i;
508         const Meter* meter;
509         const Tempo* tempo;
510         Meter *m;
511         Tempo *t;
512         nframes_t current;
513         nframes_t section_frames;
514         BBT_Time start;
515         BBT_Time end;
516
517         meter = &first_meter ();
518         tempo = &first_tempo ();
519         current = 0;
520
521         for (i = metrics->begin(); i != metrics->end(); ++i) {
522                 
523                 end = (*i)->start();
524
525                 section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
526
527                 current += section_frames;
528
529                 start = end;
530
531                 (*i)->set_frame (current);
532
533                 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
534                         tempo = t;
535                 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
536                         meter = m;
537                 } else {
538                         fatal << _("programming error: unhandled MetricSection type") << endmsg;
539                         /*NOTREACHED*/
540                 }
541         }
542 }
543
544 TempoMap::Metric
545 TempoMap::metric_at (nframes_t frame) const
546 {
547         Metric m (first_meter(), first_tempo());
548         const Meter* meter;
549         const Tempo* tempo;
550
551         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
552            at something, because we insert the default tempo and meter during
553            TempoMap construction.
554
555            now see if we can find better candidates.
556         */
557
558         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
559
560                 if ((*i)->frame() > frame) {
561                         break;
562                 }
563
564                 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
565                         m.set_tempo (*tempo);
566                 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
567                         m.set_meter (*meter);
568                 }
569
570                 m.set_frame ((*i)->frame ());
571                 m.set_start ((*i)->start ());
572         }
573         
574         return m;
575 }
576
577 TempoMap::Metric
578 TempoMap::metric_at (BBT_Time bbt) const
579 {
580         Metric m (first_meter(), first_tempo());
581         const Meter* meter;
582         const Tempo* tempo;
583
584         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
585            at something, because we insert the default tempo and meter during
586            TempoMap construction.
587
588            now see if we can find better candidates.
589         */
590
591         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
592
593                 BBT_Time section_start ((*i)->start());
594
595                 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
596                         break;
597                 }
598
599                 if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
600                         m.set_tempo (*tempo);
601                 } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
602                         m.set_meter (*meter);
603                 }
604                 
605                 m.set_frame ((*i)->frame ());
606                 m.set_start (section_start);
607         }
608
609         return m;
610 }
611
612 void
613 TempoMap::bbt_time (nframes_t frame, BBT_Time& bbt) const
614 {
615         {
616                 Glib::RWLock::ReaderLock lm (lock);
617                 bbt_time_unlocked (frame, bbt);
618         }
619 }
620
621 void
622 TempoMap::bbt_time_unlocked (nframes_t frame, BBT_Time& bbt) const
623 {
624         bbt_time_with_metric (frame, bbt, metric_at (frame));
625 }
626
627 void
628 TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& metric) const
629 {
630         nframes_t frame_diff;
631
632         uint32_t xtra_bars = 0;
633         double xtra_beats = 0;
634         double beats = 0;
635
636         const double beats_per_bar = metric.meter().beats_per_bar();
637         const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
638         const double beat_frames = metric.tempo().frames_per_beat (_frame_rate);
639
640         /* now compute how far beyond that point we actually are. */
641
642         frame_diff = frame - metric.frame();
643
644         xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar);
645         frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar);
646         xtra_beats = (double) frame_diff / beat_frames;
647
648
649         /* and set the returned value */
650
651         /* and correct beat/bar shifts to match the meter.
652           remember: beat and bar counting is 1-based, 
653           not zero-based 
654           also the meter may contain a fraction
655         */
656         
657         bbt.bars = metric.start().bars + xtra_bars; 
658
659         beats = (double) metric.start().beats + xtra_beats;
660
661         bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) );
662
663         beats = fmod(beats - 1, beats_per_bar )+ 1.0;
664         bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat));
665         bbt.beats = (uint32_t) floor(beats);
666
667 }
668
669
670 nframes_t 
671 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
672 {
673
674         /* for this to work with fractional measure types, start and end have to "legal" BBT types, 
675         that means that  the  beats and ticks should be  inside a bar
676         */
677
678
679         nframes_t frames = 0;
680         nframes_t start_frame = 0;
681         nframes_t end_frame = 0;
682
683         Metric m = metric_at(start);
684
685         uint32_t bar_offset = start.bars - m.start().bars;
686
687         double  beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1) 
688                 + start.ticks/Meter::ticks_per_beat;
689
690
691         start_frame = m.frame() + (nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate));
692
693         m =  metric_at(end);
694
695         bar_offset = end.bars - m.start().bars;
696
697         beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1) 
698                 + end.ticks/Meter::ticks_per_beat;
699
700         end_frame = m.frame() + (nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate));
701
702         frames = end_frame - start_frame;
703
704         return frames;
705         
706 }       
707
708 nframes_t 
709 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
710 {
711         /*this is used in timestamping the metrics by actually counting the beats */ 
712
713         nframes_t frames = 0;
714         uint32_t bar = start.bars;
715         double beat = (double) start.beats;
716         double beats_counted = 0;
717         double beats_per_bar = 0;
718         double beat_frames = 0;
719
720         beats_per_bar = meter.beats_per_bar();
721         beat_frames = tempo.frames_per_beat (_frame_rate);
722
723         frames = 0;
724
725         while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
726                 
727                 if (beat >= beats_per_bar) {
728                         beat = 1;
729                         ++bar;
730                         ++beats_counted;
731                 } else {
732                         ++beat;
733                         ++beats_counted;
734                         if (beat > beats_per_bar) {
735                                 /* this is a fractional beat at the end of a fractional bar
736                                    so it should only count for the fraction */
737                                 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
738                         }
739                 }
740         }
741         
742         frames = (nframes_t) floor (beats_counted * beat_frames);
743
744         return frames;
745         
746 }       
747
748 nframes_t 
749 TempoMap::frame_time (const BBT_Time& bbt) const
750 {
751         BBT_Time start ; /* 1|1|0 */
752
753         return  count_frames_between ( start, bbt);
754 }
755
756 nframes_t 
757 TempoMap::bbt_duration_at (nframes_t pos, const BBT_Time& bbt, int dir) const
758 {
759         nframes_t frames = 0;
760
761         BBT_Time when;
762         bbt_time(pos,when);
763
764         {
765                 Glib::RWLock::ReaderLock lm (lock);
766                 frames = bbt_duration_at_unlocked (when, bbt,dir);
767         }
768
769         return frames;
770 }
771
772 nframes_t 
773 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
774 {
775
776         nframes_t frames = 0;
777
778         double beats_per_bar;
779         BBT_Time result;
780         
781         result.bars = max(1U,when.bars + dir * bbt.bars) ;
782         result.beats = 1;
783         result.ticks = 0;
784
785         Metric  metric = metric_at(result);
786         beats_per_bar = metric.meter().beats_per_bar();
787
788
789
790         /*reduce things to legal bbt  values 
791           we have to handle possible fractional=shorter beats at the end of measures
792           and things like 0|11|9000  as a duration in a 4.5/4 measure
793           the musical decision is that the fractional beat is also a beat , although a shorter one 
794         */
795
796     
797         if (dir >= 0) {
798                 result.beats = when.beats +  bbt.beats;
799                 result.ticks = when.ticks +  bbt.ticks;
800
801                 while (result.beats >= (beats_per_bar+1)) {
802                         result.bars++;
803                         result.beats -=  (uint32_t) ceil(beats_per_bar);
804                         metric = metric_at(result); // maybe there is a meter change
805                         beats_per_bar = metric.meter().beats_per_bar();
806                         
807                 }
808                 /*we now counted the beats and landed in the target measure, now deal with ticks 
809                   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
810                   and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
811                 */
812
813                 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
814                  */
815
816                 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
817                                         (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat 
818                                            : Meter::ticks_per_beat );
819
820                 while (result.ticks >= ticks_at_beat) {
821                         result.beats++;
822                         result.ticks -= ticks_at_beat;
823                         if  (result.beats >= (beats_per_bar+1)) {
824                                 result.bars++;
825                                 result.beats = 1;
826                                 metric = metric_at(result); // maybe there is a meter change
827                                 beats_per_bar = metric.meter().beats_per_bar();
828                         }
829                         ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
830                                        (1 - (ceil(beats_per_bar) - beats_per_bar) )* Meter::ticks_per_beat 
831                                        : Meter::ticks_per_beat);
832
833                 }
834
835           
836         } else {
837                 uint32_t b = bbt.beats;
838
839                 /* count beats */
840                 while( b > when.beats ) {
841                         
842                         result.bars = max(1U,result.bars-- ) ;
843                         metric = metric_at(result); // maybe there is a meter change
844                         beats_per_bar = metric.meter().beats_per_bar();
845                         if (b >= ceil(beats_per_bar)) {
846                                 
847                                 b -= (uint32_t) ceil(beats_per_bar);
848                         } else {
849                                 b = (uint32_t) ceil(beats_per_bar)- b + when.beats ;
850                         }
851                 }
852                 result.beats = when.beats - b;
853                 
854                 /*count ticks */
855
856                 if (bbt.ticks <= when.ticks) {
857                         result.ticks = when.ticks - bbt.ticks;
858                 } else {
859
860                         uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
861                         uint32_t t = bbt.ticks - when.ticks;
862
863                         do {
864
865                                 if (result.beats == 1) {
866                                         result.bars = max(1U,result.bars-- ) ;
867                                         metric = metric_at(result); // maybe there is a meter change
868                                         beats_per_bar = metric.meter().beats_per_bar();
869                                         result.beats = (uint32_t) ceil(beats_per_bar);
870                                         ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat) ;
871                                 } else {
872                                         result.beats --;
873                                         ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
874                                 }
875                                                 
876                                 if (t <= ticks_at_beat) {
877                                         result.ticks = ticks_at_beat - t; 
878                                 } else {
879                                         t-= ticks_at_beat;
880                                 }
881                         } while (t > ticks_at_beat);
882
883                 }
884
885
886         }
887
888         if (dir < 0 ) {
889                 frames = count_frames_between( result,when);
890         } else {
891                 frames = count_frames_between(when,result);
892         }
893
894         return frames;
895 }
896
897
898
899 nframes_t
900 TempoMap::round_to_bar (nframes_t fr, int dir)
901 {
902         {
903                 Glib::RWLock::ReaderLock lm (lock);
904                 return round_to_type (fr, dir, Bar);
905         }
906 }
907
908
909 nframes_t
910 TempoMap::round_to_beat (nframes_t fr, int dir)
911 {
912         {
913                 Glib::RWLock::ReaderLock lm (lock);
914                 return round_to_type (fr, dir, Beat);
915         }
916 }
917
918 nframes_t
919
920 TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
921 {
922
923         BBT_Time the_beat;
924         uint32_t ticks_one_half_subdivisions_worth;
925         uint32_t ticks_one_subdivisions_worth;
926
927         bbt_time(fr, the_beat);
928
929         ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
930         ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
931
932         if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
933           uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
934           if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
935             the_beat.beats++;
936             the_beat.ticks += difference;
937             the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
938           } else {  
939             the_beat.ticks += difference;
940           }
941         } else {
942           the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
943         }
944
945         return frame_time (the_beat);
946
947         /* XXX just keeping this for reference
948
949         TempoMap::BBTPointList::iterator i;
950         TempoMap::BBTPointList *more_zoomed_bbt_points;
951         nframes_t frame_one_beats_worth;
952         nframes_t pos = 0;
953         nframes_t next_pos = 0 ;
954         double tempo = 1;
955         double frames_one_subdivisions_worth;
956         bool fr_has_changed = false;
957
958         int n;
959
960         frame_one_beats_worth = (nframes_t) ::floor ((double)  _frame_rate *  60 / 20 ); //one beat @ 20 bpm
961         {
962           Glib::RWLock::ReaderLock lm (lock);
963           more_zoomed_bbt_points = get_points((fr >= frame_one_beats_worth) ? 
964                                             fr - frame_one_beats_worth : 0, fr+frame_one_beats_worth );
965         }
966         if (more_zoomed_bbt_points == 0 || more_zoomed_bbt_points->empty()) {
967                 return fr;
968         }
969
970         for (i = more_zoomed_bbt_points->begin(); i != more_zoomed_bbt_points->end(); i++) {
971                 if  ((*i).frame <= fr) {
972                         pos = (*i).frame;
973                         tempo = (*i).tempo->beats_per_minute();
974                         
975                 } else {
976                         i++;
977                         next_pos = (*i).frame;
978                         break;
979                 }
980         }
981         frames_one_subdivisions_worth = ((double) _frame_rate *  60 / (sub_num * tempo));
982
983         for (n = sub_num; n > 0; n--) {
984                 if (fr >= (pos + ((n - 0.5) * frames_one_subdivisions_worth))) {
985                         fr = (nframes_t) round(pos + (n  * frames_one_subdivisions_worth));
986                         if (fr > next_pos) {
987                                 fr = next_pos;  //take care of fractional beats that don't match the subdivision asked
988                         }
989                         fr_has_changed = true;
990                         break;
991                 }
992         }
993
994         if (!fr_has_changed) {
995                 fr = pos;
996         }
997
998         delete more_zoomed_bbt_points;
999         return fr ;
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         nframes_t limit;
1075
1076         meter = &first_meter ();
1077         tempo = &first_tempo ();
1078
1079         /* find the starting point */
1080
1081         for (i = metrics->begin(); i != metrics->end(); ++i) {
1082
1083                 if ((*i)->frame() > lower) {
1084                         break;
1085                 }
1086
1087                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1088                         tempo = t;
1089                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1090                         meter = m;
1091                 }
1092         }
1093
1094         /* We now have:
1095            
1096            meter -> the Meter for "lower"
1097            tempo -> the Tempo for "lower"
1098            i     -> for first new metric after "lower", possibly metrics->end()
1099
1100            Now start generating points.
1101         */
1102
1103         if (meter->frame() > tempo->frame()) {
1104                 bar = meter->start().bars;
1105                 beat = meter->start().beats;
1106                 current = meter->frame();
1107         } else {
1108                 bar = tempo->start().bars;
1109                 beat = tempo->start().beats;
1110                 current = tempo->frame();
1111         }
1112
1113         points = new BBTPointList;
1114                 
1115         beats_per_bar = meter->beats_per_bar ();
1116         frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1117         beat_frames = tempo->frames_per_beat (_frame_rate);
1118
1119         do {
1120
1121                 if (i == metrics->end()) {
1122                         limit = upper;
1123                 } else {
1124                         limit = (*i)->frame();
1125                 }
1126
1127                 limit = min (limit, upper);
1128
1129                 while (current < limit) {
1130                         
1131                         /* if we're at the start of a bar, add bar point */
1132
1133                         if (beat == 1) {
1134                                 if (current >= lower) {
1135                                         points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1));
1136
1137                                 }
1138                         }
1139
1140                         /* add some beats if we can */
1141
1142                         beat_frame = current;
1143
1144                         while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1145                                 if (beat_frame >= lower) {
1146                                         points->push_back (BBTPoint (*meter, *tempo, (nframes_t) rint(beat_frame), Beat, bar, beat));
1147                                 }
1148                                 beat_frame += beat_frames;
1149                                 current+= beat_frames;
1150                                
1151                                 beat++;
1152                         }
1153
1154                         if (beat > ceil(beats_per_bar) ) {
1155
1156                                 /* we walked an entire bar. its
1157                                    important to move `current' forward
1158                                    by the actual frames_per_bar, not move it to
1159                                    an integral beat_frame, so that metrics with
1160                                    non-integral beats-per-bar have
1161                                    their bar positions set
1162                                    correctly. consider a metric with
1163                                    9-1/2 beats-per-bar. the bar we
1164                                    just filled had  10 beat marks,
1165                                    but the bar end is 1/2 beat before
1166                                    the last beat mark.
1167                                    And it is also possible that a tempo 
1168                                    change occured in the middle of a bar, 
1169                                    so we subtract the possible extra fraction from the current
1170                                 */
1171
1172                                 current -=  beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1173                                 bar++;
1174                                 beat = 1;
1175
1176                         } 
1177                 
1178                 }
1179
1180                 /* if we're done, then we're done */
1181
1182                 if (current >= upper) {
1183                         break;
1184                 }
1185
1186                 /* i is an iterator that refers to the next metric (or none).
1187                    if there is a next metric, move to it, and continue.
1188                 */
1189
1190                 if (i != metrics->end()) {
1191
1192                         if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1193                                 tempo = t;
1194                         } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1195                                 meter = m;
1196                                 /* new MeterSection, beat always returns to 1 */
1197                                 beat = 1;
1198                         }
1199
1200                         ++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                 in_set_state = true;
1250                 
1251                 metrics->clear();
1252
1253                 nlist = node.children();
1254                 
1255                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1256                         XMLNode* child = *niter;
1257                         
1258                         if (child->name() == TempoSection::xml_state_node_name) {
1259                                 
1260                                 try {
1261                                         metrics->push_back (new TempoSection (*child));
1262                                 }
1263                                 
1264                                 catch (failed_constructor& err){
1265                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1266                                         *metrics = old_metrics;
1267                                         break;
1268                                 }
1269                                 
1270                         } else if (child->name() == MeterSection::xml_state_node_name) {
1271                                 
1272                                 try {
1273                                         metrics->push_back (new MeterSection (*child));
1274                                 }
1275                                 
1276                                 catch (failed_constructor& err) {
1277                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1278                                         *metrics = old_metrics;
1279                                         break;
1280                                 }
1281                         }
1282                 }
1283                 
1284                 if (niter == nlist.end()) {
1285                         
1286                         MetricSectionSorter cmp;
1287                         metrics->sort (cmp);
1288                         timestamp_metrics ();
1289                 }
1290
1291                 in_set_state = false;
1292         }
1293         
1294         /* This state needs to be saved. This string will never be a part of the 
1295            object's history though, because the allow_save flag is false during 
1296            session load. This state will eventually be tagged "initial state", 
1297            by a call to StateManager::allow_save from Session::set_state.
1298
1299            If this state is not saved, there is no way to reach it through undo actions.
1300         */
1301         save_state(_("load XML data"));
1302         
1303         send_state_changed (Change (0));
1304
1305         return 0;
1306 }
1307
1308 void
1309 TempoMap::dump (std::ostream& o) const
1310 {
1311         const MeterSection* m;
1312         const TempoSection* t;
1313         
1314         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1315
1316                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1317                         o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM at " << t->start() << " frame= " << t->frame() << " (move? "
1318                           << t->movable() << ')' << endl;
1319                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1320                         o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame() 
1321                           << " (move? " << m->movable() << ')' << endl;
1322                 }
1323         }
1324 }
1325
1326 UndoAction
1327 TempoMap::get_memento () const
1328 {
1329         return sigc::bind (mem_fun (*(const_cast<TempoMap *> (this)), &StateManager::use_state), _current_state_id);
1330 }
1331
1332 Change
1333 TempoMap::restore_state (StateManager::State& state)
1334 {
1335         Glib::RWLock::ReaderLock lm (lock);
1336
1337         TempoMapState* tmstate = dynamic_cast<TempoMapState*> (&state);
1338
1339         /* We can't just set the metrics pointer to the address of the metrics list 
1340            stored in the state, cause this would ruin this state for restoring in
1341            the future. If they have the same address, they are the same list.
1342            Thus we need to copy all the elements from the state metrics list to the 
1343            current metrics list.
1344         */
1345         metrics->clear();
1346         for (Metrics::iterator i = tmstate->metrics->begin(); i != tmstate->metrics->end(); ++i) {
1347                 TempoSection *ts;
1348                 MeterSection *ms;
1349                 
1350                 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1351                         metrics->push_back (new TempoSection (*ts));
1352                 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1353                         metrics->push_back (new MeterSection (*ms));
1354                 }
1355         }
1356         
1357         last_bbt_valid = false;
1358
1359         return Change (0);
1360 }
1361
1362 StateManager::State* 
1363 TempoMap::state_factory (std::string why) const
1364 {
1365         TempoMapState* state = new TempoMapState (why);
1366
1367         for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1368                 TempoSection *ts;
1369                 MeterSection *ms;
1370                 
1371                 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1372                         state->metrics->push_back (new TempoSection (*ts));
1373                 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1374                         state->metrics->push_back (new MeterSection (*ms));
1375                 }
1376         }
1377                 
1378         return state;
1379 }
1380
1381 void
1382 TempoMap::save_state (std::string why)
1383 {
1384         if (!in_set_state) {
1385                 StateManager::save_state (why);
1386         }
1387 }