Merging from trunk
[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, jack_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 (jack_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::Mutex::Lock 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::Mutex::Lock 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::Mutex::Lock 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::Mutex::Lock 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::Mutex::Lock 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::Mutex::Lock 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::Mutex::Lock 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         jack_nframes_t current;
513         jack_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 (jack_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 (jack_nframes_t frame, BBT_Time& bbt) const
614 {
615         Glib::Mutex::Lock lm (lock);
616         bbt_time_unlocked (frame, bbt);
617 }
618
619 void
620 TempoMap::bbt_time_unlocked (jack_nframes_t frame, BBT_Time& bbt) const
621 {
622         bbt_time_with_metric (frame, bbt, metric_at (frame));
623 }
624
625 void
626 TempoMap::bbt_time_with_metric (jack_nframes_t frame, BBT_Time& bbt, const Metric& metric) const
627 {
628         jack_nframes_t frame_diff;
629
630         uint32_t xtra_bars = 0;
631         double xtra_beats = 0;
632         double beats = 0;
633
634         const double beats_per_bar = metric.meter().beats_per_bar();
635         const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
636         const double beat_frames = metric.tempo().frames_per_beat (_frame_rate);
637
638         /* now compute how far beyond that point we actually are. */
639
640         frame_diff = frame - metric.frame();
641
642         xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar);
643         frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar);
644         xtra_beats = (double) frame_diff / beat_frames;
645
646
647         /* and set the returned value */
648
649         /* and correct beat/bar shifts to match the meter.
650           remember: beat and bar counting is 1-based, 
651           not zero-based 
652           also the meter may contain a fraction
653         */
654         
655         bbt.bars = metric.start().bars + xtra_bars; 
656
657         beats = (double) metric.start().beats + xtra_beats;
658
659         bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) );
660
661         beats = fmod(beats - 1, beats_per_bar )+ 1.0;
662         bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat));
663         bbt.beats = (uint32_t) floor(beats);
664
665 }
666
667
668 jack_nframes_t 
669 TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
670 {
671
672         /* for this to work with fractional measure types, start and end have to "legal" BBT types, 
673         that means that  the  beats and ticks should be  inside a bar
674         */
675
676
677         jack_nframes_t frames = 0;
678         jack_nframes_t start_frame = 0;
679         jack_nframes_t end_frame = 0;
680
681         Metric m = metric_at(start);
682
683         uint32_t bar_offset = start.bars - m.start().bars;
684
685         double  beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1) 
686                 + start.ticks/Meter::ticks_per_beat;
687
688
689         start_frame = m.frame() + (jack_nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate));
690
691         m =  metric_at(end);
692
693         bar_offset = end.bars - m.start().bars;
694
695         beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1) 
696                 + end.ticks/Meter::ticks_per_beat;
697
698         end_frame = m.frame() + (jack_nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate));
699
700         frames = end_frame - start_frame;
701
702         return frames;
703         
704 }       
705
706 jack_nframes_t 
707 TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
708 {
709         /*this is used in timestamping the metrics by actually counting the beats */ 
710
711         jack_nframes_t frames = 0;
712         uint32_t bar = start.bars;
713         double beat = (double) start.beats;
714         double beats_counted = 0;
715         double beats_per_bar = 0;
716         double beat_frames = 0;
717
718         beats_per_bar = meter.beats_per_bar();
719         beat_frames = tempo.frames_per_beat (_frame_rate);
720
721         frames = 0;
722
723         while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
724                 
725                 if (beat >= beats_per_bar) {
726                         beat = 1;
727                         ++bar;
728                         ++beats_counted;
729                 } else {
730                         ++beat;
731                         ++beats_counted;
732                         if (beat > beats_per_bar) {
733                                 /* this is a fractional beat at the end of a fractional bar
734                                    so it should only count for the fraction */
735                                 beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
736                         }
737                 }
738         }
739         
740         frames = (jack_nframes_t) floor (beats_counted * beat_frames);
741
742         return frames;
743         
744 }       
745
746 jack_nframes_t 
747 TempoMap::frame_time (const BBT_Time& bbt) const
748 {
749         BBT_Time start ; /* 1|1|0 */
750
751         return  count_frames_between ( start, bbt);
752 }
753
754 jack_nframes_t 
755 TempoMap::bbt_duration_at (jack_nframes_t pos, const BBT_Time& bbt, int dir) const
756 {
757         jack_nframes_t frames = 0;
758
759         BBT_Time when;
760         bbt_time(pos,when);
761
762         {
763                 Glib::Mutex::Lock lm (lock);
764                 frames = bbt_duration_at_unlocked (when, bbt,dir);
765         }
766
767         return frames;
768 }
769
770 jack_nframes_t 
771 TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
772 {
773
774         jack_nframes_t frames = 0;
775
776         double beats_per_bar;
777         BBT_Time result;
778         
779         result.bars = max(1U,when.bars + dir * bbt.bars) ;
780         result.beats = 1;
781         result.ticks = 0;
782
783         Metric  metric = metric_at(result);
784         beats_per_bar = metric.meter().beats_per_bar();
785
786
787
788         /*reduce things to legal bbt  values 
789           we have to handle possible fractional=shorter beats at the end of measures
790           and things like 0|11|9000  as a duration in a 4.5/4 measure
791           the musical decision is that the fractional beat is also a beat , although a shorter one 
792         */
793
794     
795         if (dir >= 0) {
796                 result.beats = when.beats +  bbt.beats;
797                 result.ticks = when.ticks +  bbt.ticks;
798
799                 while (result.beats >= (beats_per_bar+1)) {
800                         result.bars++;
801                         result.beats -=  (uint32_t) ceil(beats_per_bar);
802                         metric = metric_at(result); // maybe there is a meter change
803                         beats_per_bar = metric.meter().beats_per_bar();
804                         
805                 }
806                 /*we now counted the beats and landed in the target measure, now deal with ticks 
807                   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
808                   and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
809                 */
810
811                 /* of course gtk_ardour only allows bar with at least 1.0 beats .....
812                  */
813
814                 uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
815                                         (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat 
816                                            : Meter::ticks_per_beat );
817
818                 while (result.ticks >= ticks_at_beat) {
819                         result.beats++;
820                         result.ticks -= ticks_at_beat;
821                         if  (result.beats >= (beats_per_bar+1)) {
822                                 result.bars++;
823                                 result.beats = 1;
824                                 metric = metric_at(result); // maybe there is a meter change
825                                 beats_per_bar = metric.meter().beats_per_bar();
826                         }
827                         ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
828                                        (1 - (ceil(beats_per_bar) - beats_per_bar) )* Meter::ticks_per_beat 
829                                        : Meter::ticks_per_beat);
830
831                 }
832
833           
834         } else {
835                 uint32_t b = bbt.beats;
836
837                 /* count beats */
838                 while( b > when.beats ) {
839                         
840                         result.bars = max(1U,result.bars-- ) ;
841                         metric = metric_at(result); // maybe there is a meter change
842                         beats_per_bar = metric.meter().beats_per_bar();
843                         if (b >= ceil(beats_per_bar)) {
844                                 
845                                 b -= (uint32_t) ceil(beats_per_bar);
846                         } else {
847                                 b = (uint32_t) ceil(beats_per_bar)- b + when.beats ;
848                         }
849                 }
850                 result.beats = when.beats - b;
851                 
852                 /*count ticks */
853
854                 if (bbt.ticks <= when.ticks) {
855                         result.ticks = when.ticks - bbt.ticks;
856                 } else {
857
858                         uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
859                         uint32_t t = bbt.ticks - when.ticks;
860
861                         do {
862
863                                 if (result.beats == 1) {
864                                         result.bars = max(1U,result.bars-- ) ;
865                                         metric = metric_at(result); // maybe there is a meter change
866                                         beats_per_bar = metric.meter().beats_per_bar();
867                                         result.beats = (uint32_t) ceil(beats_per_bar);
868                                         ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat) ;
869                                 } else {
870                                         result.beats --;
871                                         ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
872                                 }
873                                                 
874                                 if (t <= ticks_at_beat) {
875                                         result.ticks = ticks_at_beat - t; 
876                                 } else {
877                                         t-= ticks_at_beat;
878                                 }
879                         } while (t > ticks_at_beat);
880
881                 }
882
883
884         }
885
886         if (dir < 0 ) {
887                 frames = count_frames_between( result,when);
888         } else {
889                 frames = count_frames_between(when,result);
890         }
891
892         return frames;
893 }
894
895
896
897 jack_nframes_t
898 TempoMap::round_to_bar (jack_nframes_t fr, int dir)
899 {
900         Glib::Mutex::Lock lm (lock);
901         return round_to_type (fr, dir, Bar);
902 }
903
904
905 jack_nframes_t
906 TempoMap::round_to_beat (jack_nframes_t fr, int dir)
907 {
908         Glib::Mutex::Lock lm (lock);
909         return round_to_type (fr, dir, Beat);
910 }
911
912 jack_nframes_t
913
914 TempoMap::round_to_beat_subdivision (jack_nframes_t fr, int sub_num)
915 {
916         Glib::Mutex::Lock lm (lock);
917         TempoMap::BBTPointList::iterator i;
918         TempoMap::BBTPointList *more_zoomed_bbt_points;
919         jack_nframes_t frame_one_beats_worth;
920         jack_nframes_t pos = 0;
921         jack_nframes_t next_pos = 0 ;
922         double tempo = 1;
923         double frames_one_subdivisions_worth;
924         bool fr_has_changed = false;
925
926         int n;
927
928         frame_one_beats_worth = (jack_nframes_t) ::floor ((double)  _frame_rate *  60 / 20 ); //one beat @ 20 bpm
929         more_zoomed_bbt_points = get_points((fr >= frame_one_beats_worth) ? 
930                                             fr - frame_one_beats_worth : 0, fr+frame_one_beats_worth );
931
932         if (more_zoomed_bbt_points == 0 || more_zoomed_bbt_points->empty()) {
933                 return fr;
934         }
935
936         for (i = more_zoomed_bbt_points->begin(); i != more_zoomed_bbt_points->end(); i++) {
937                 if  ((*i).frame <= fr) {
938                         pos = (*i).frame;
939                         tempo = (*i).tempo->beats_per_minute();
940                         
941                 } else {
942                         i++;
943                         next_pos = (*i).frame;
944                         break;
945                 }
946         }
947         frames_one_subdivisions_worth = ((double) _frame_rate *  60 / (sub_num * tempo));
948
949         for (n = sub_num; n > 0; n--) {
950                 if (fr >= (pos + ((n - 0.5) * frames_one_subdivisions_worth))) {
951                         fr = (jack_nframes_t) round(pos + (n  * frames_one_subdivisions_worth));
952                         if (fr > next_pos) {
953                                 fr = next_pos;  //take care of fractional beats that don't match the subdivision asked
954                         }
955                         fr_has_changed = true;
956                         break;
957                 }
958         }
959
960         if (!fr_has_changed) {
961                 fr = pos;
962         }
963
964         delete more_zoomed_bbt_points;
965         return fr ;
966 }
967
968 jack_nframes_t
969
970 TempoMap::round_to_type (jack_nframes_t frame, int dir, BBTPointType type)
971 {
972         Metric metric = metric_at (frame);
973         BBT_Time bbt;
974         BBT_Time start;
975         bbt_time_with_metric (frame, bbt, metric);
976
977         switch (type) {
978         case Bar:
979                 if (dir < 0) {
980                         /* relax */
981
982                 } else if (dir > 0) {
983                         if (bbt.beats > 0) {
984                                 bbt.bars++;
985                         }
986                 } else {
987                         if (bbt.beats > metric.meter().beats_per_bar()/2) {
988                                 bbt.bars++;
989                         }
990
991                 }
992                 bbt.beats = 1;
993                 bbt.ticks = 0;
994                 break;
995         
996         case Beat:
997                 if (dir < 0) {
998                         /* relax */
999                 } else if (dir > 0) {
1000                         if (bbt.ticks > 0) {
1001                                 bbt.beats++;
1002                         }
1003                 } else {
1004                         if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
1005                                 bbt.beats++;
1006                         }
1007                 }
1008                 if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
1009                         bbt.beats = 1;
1010                         bbt.bars++;
1011                 }
1012                 bbt.ticks = 0;
1013                 break;
1014         
1015         }
1016
1017         return metric.frame() + count_frames_between (metric.start(), bbt);
1018 }
1019
1020 TempoMap::BBTPointList *
1021 TempoMap::get_points (jack_nframes_t lower, jack_nframes_t upper) const
1022 {
1023
1024         Metrics::const_iterator i;
1025         BBTPointList *points;
1026         double current;
1027         const MeterSection* meter;
1028         const MeterSection* m;
1029         const TempoSection* tempo;
1030         const TempoSection* t;
1031         uint32_t bar;
1032         uint32_t beat;
1033
1034         meter = &first_meter ();
1035         tempo = &first_tempo ();
1036
1037         /* find the starting point */
1038
1039         for (i = metrics->begin(); i != metrics->end(); ++i) {
1040
1041                 if ((*i)->frame() > lower) {
1042                         break;
1043                 }
1044
1045                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1046                         tempo = t;
1047                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1048                         meter = m;
1049                 }
1050         }
1051
1052         /* We now have:
1053            
1054            meter -> the Meter for "lower"
1055            tempo -> the Tempo for "lower"
1056            i     -> for first new metric after "lower", possibly metrics->end()
1057
1058            Now start generating points.
1059         */
1060
1061         if (meter->frame() > tempo->frame()) {
1062                 bar = meter->start().bars;
1063                 beat = meter->start().beats;
1064                 current = meter->frame();
1065         } else {
1066                 bar = tempo->start().bars;
1067                 beat = tempo->start().beats;
1068                 current = tempo->frame();
1069         }
1070
1071         points = new BBTPointList;
1072
1073         do {
1074                 double beats_per_bar;
1075                 double beat_frame;
1076                 double beat_frames;
1077                 double frames_per_bar;
1078                 jack_nframes_t limit;
1079                 
1080                 beats_per_bar = meter->beats_per_bar ();
1081                 frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
1082                 beat_frames = tempo->frames_per_beat (_frame_rate);
1083
1084                 if (i == metrics->end()) {
1085                         limit = upper;
1086                 } else {
1087                         limit = (*i)->frame();
1088                 }
1089
1090                 limit = min (limit, upper);
1091
1092                 while (current < limit) {
1093                         
1094                         /* if we're at the start of a bar, add bar point */
1095
1096                         if (beat == 1) {
1097                                 if (current >= lower) {
1098                                         points->push_back (BBTPoint (*meter, *tempo,(jack_nframes_t)rint(current), Bar, bar, 1));
1099
1100                                 }
1101                         }
1102
1103                         /* add some beats if we can */
1104
1105                         beat_frame = current;
1106
1107                         while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
1108                                 if (beat_frame >= lower) {
1109                                         points->push_back (BBTPoint (*meter, *tempo, (jack_nframes_t) rint(beat_frame), Beat, bar, beat));
1110                                 }
1111                                 beat_frame += beat_frames;
1112                                 current+= beat_frames;
1113                                
1114                                 beat++;
1115                         }
1116
1117                         if (beat > ceil(beats_per_bar) ) {
1118
1119                                 /* we walked an entire bar. its
1120                                    important to move `current' forward
1121                                    by the actual frames_per_bar, not move it to
1122                                    an integral beat_frame, so that metrics with
1123                                    non-integral beats-per-bar have
1124                                    their bar positions set
1125                                    correctly. consider a metric with
1126                                    9-1/2 beats-per-bar. the bar we
1127                                    just filled had  10 beat marks,
1128                                    but the bar end is 1/2 beat before
1129                                    the last beat mark.
1130                                    And it is also possible that a tempo 
1131                                    change occured in the middle of a bar, 
1132                                    so we subtract the possible extra fraction from the current
1133                                 */
1134
1135                                 current -=  beat_frames * (ceil(beats_per_bar)-beats_per_bar);
1136                                 bar++;
1137                                 beat = 1;
1138
1139                         } 
1140                 
1141                 }
1142
1143                 /* if we're done, then we're done */
1144
1145                 if (current >= upper) {
1146                         break;
1147                 }
1148
1149                 /* i is an iterator that refers to the next metric (or none).
1150                    if there is a next metric, move to it, and continue.
1151                 */
1152
1153                 if (i != metrics->end()) {
1154
1155                         if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1156                                 tempo = t;
1157                         } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1158                                 meter = m;
1159                                 /* new MeterSection, beat always returns to 1 */
1160                                 beat = 1;
1161                         }
1162
1163                         ++i;
1164                 }
1165
1166         } while (1);
1167
1168         return points;
1169 }       
1170
1171 const Tempo&
1172 TempoMap::tempo_at (jack_nframes_t frame)
1173 {
1174         Metric m (metric_at (frame));
1175         return m.tempo();
1176 }
1177
1178
1179 const Meter&
1180 TempoMap::meter_at (jack_nframes_t frame)
1181 {
1182         Metric m (metric_at (frame));
1183         return m.meter();
1184 }
1185
1186 XMLNode&
1187 TempoMap::get_state ()
1188 {
1189         Glib::Mutex::Lock lm (lock);
1190         Metrics::const_iterator i;
1191         XMLNode *root = new XMLNode ("TempoMap");
1192
1193         for (i = metrics->begin(); i != metrics->end(); ++i) {
1194                 root->add_child_nocopy ((*i)->get_state());
1195         }
1196
1197         return *root;
1198 }
1199
1200 int
1201 TempoMap::set_state (const XMLNode& node)
1202 {
1203         {
1204                 Glib::Mutex::Lock lm (lock);
1205
1206                 XMLNodeList nlist;
1207                 XMLNodeConstIterator niter;
1208                 Metrics old_metrics (*metrics);
1209                 
1210                 in_set_state = true;
1211                 
1212                 metrics->clear();
1213
1214                 nlist = node.children();
1215                 
1216                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1217                         XMLNode* child = *niter;
1218                         
1219                         if (child->name() == TempoSection::xml_state_node_name) {
1220                                 
1221                                 try {
1222                                         metrics->push_back (new TempoSection (*child));
1223                                 }
1224                                 
1225                                 catch (failed_constructor& err){
1226                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1227                                         *metrics = old_metrics;
1228                                         break;
1229                                 }
1230                                 
1231                         } else if (child->name() == MeterSection::xml_state_node_name) {
1232                                 
1233                                 try {
1234                                         metrics->push_back (new MeterSection (*child));
1235                                 }
1236                                 
1237                                 catch (failed_constructor& err) {
1238                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1239                                         *metrics = old_metrics;
1240                                         break;
1241                                 }
1242                         }
1243                 }
1244                 
1245                 if (niter == nlist.end()) {
1246                         
1247                         MetricSectionSorter cmp;
1248                         metrics->sort (cmp);
1249                         timestamp_metrics ();
1250                 }
1251
1252                 in_set_state = false;
1253         }
1254         
1255         /* This state needs to be saved. This string will never be a part of the 
1256            object's history though, because the allow_save flag is false during 
1257            session load. This state will eventually be tagged "initial state", 
1258            by a call to StateManager::allow_save from Session::set_state.
1259
1260            If this state is not saved, there is no way to reach it through undo actions.
1261         */
1262         save_state(_("load XML data"));
1263         
1264         send_state_changed (Change (0));
1265
1266         return 0;
1267 }
1268
1269 void
1270 TempoMap::dump (std::ostream& o) const
1271 {
1272         const MeterSection* m;
1273         const TempoSection* t;
1274         
1275         for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
1276
1277                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1278                         o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM at " << t->start() << " frame= " << t->frame() << " (move? "
1279                           << t->movable() << ')' << endl;
1280                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1281                         o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame() 
1282                           << " (move? " << m->movable() << ')' << endl;
1283                 }
1284         }
1285 }
1286
1287 UndoAction
1288 TempoMap::get_memento () const
1289 {
1290         return sigc::bind (mem_fun (*(const_cast<TempoMap *> (this)), &StateManager::use_state), _current_state_id);
1291 }
1292
1293 Change
1294 TempoMap::restore_state (StateManager::State& state)
1295 {
1296         Glib::Mutex::Lock lm (lock);
1297
1298         TempoMapState* tmstate = dynamic_cast<TempoMapState*> (&state);
1299
1300         /* We can't just set the metrics pointer to the address of the metrics list 
1301            stored in the state, cause this would ruin this state for restoring in
1302            the future. If they have the same address, they are the same list.
1303            Thus we need to copy all the elements from the state metrics list to the 
1304            current metrics list.
1305         */
1306         metrics->clear();
1307         for (Metrics::iterator i = tmstate->metrics->begin(); i != tmstate->metrics->end(); ++i) {
1308                 TempoSection *ts;
1309                 MeterSection *ms;
1310                 
1311                 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1312                         metrics->push_back (new TempoSection (*ts));
1313                 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1314                         metrics->push_back (new MeterSection (*ms));
1315                 }
1316         }
1317         
1318         last_bbt_valid = false;
1319
1320         return Change (0);
1321 }
1322
1323 StateManager::State* 
1324 TempoMap::state_factory (std::string why) const
1325 {
1326         TempoMapState* state = new TempoMapState (why);
1327
1328         for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
1329                 TempoSection *ts;
1330                 MeterSection *ms;
1331                 
1332                 if ((ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1333                         state->metrics->push_back (new TempoSection (*ts));
1334                 } else if ((ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1335                         state->metrics->push_back (new MeterSection (*ms));
1336                 }
1337         }
1338                 
1339         return state;
1340 }
1341
1342 void
1343 TempoMap::save_state (std::string why)
1344 {
1345         if (!in_set_state) {
1346                 StateManager::save_state (why);
1347         }
1348 }