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