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