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