Tempo ramps - update midi regions and locations when dragging tempo.
[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 */
19
20 #include <algorithm>
21 #include <stdexcept>
22 #include <cmath>
23
24 #include <unistd.h>
25
26 #include <glibmm/threads.h>
27 #include "pbd/xml++.h"
28 #include "evoral/Beats.hpp"
29 #include "ardour/debug.h"
30 #include "ardour/lmath.h"
31 #include "ardour/tempo.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 using Timecode::BBT_Time;
41
42 /* _default tempo is 4/4 qtr=120 */
43
44 Meter    TempoMap::_default_meter (4.0, 4.0);
45 Tempo    TempoMap::_default_tempo (120.0);
46
47 /***********************************************************************/
48
49 double
50 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
51 {
52         /* This is tempo- and meter-sensitive. The number it returns
53            is based on the interval between any two lines in the
54            grid that is constructed from tempo and meter sections.
55
56            The return value IS NOT interpretable in terms of "beats".
57         */
58
59         return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
60 }
61
62 double
63 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
64 {
65         return frames_per_grid (tempo, sr) * _divisions_per_bar;
66 }
67
68
69 /***********************************************************************/
70
71 const string TempoSection::xml_state_node_name = "Tempo";
72
73 TempoSection::TempoSection (const XMLNode& node)
74         : MetricSection (0.0), Tempo (TempoMap::default_tempo())
75 {
76         const XMLProperty *prop;
77         BBT_Time start;
78         LocaleGuard lg;
79
80         if ((prop = node.property ("start")) == 0) {
81                 error << _("MeterSection XML node has no \"start\" property") << endmsg;
82                 throw failed_constructor();
83         }
84
85         if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
86                     &bbt.bars,
87                     &bbt.beats,
88                     &bbt.ticks) == 3) {
89                 /* legacy session - start used to be in bbt*/
90                 _legacy_bbt = bbt;
91                 start = -1.0;
92         } else if (sscanf (prop->value().c_str(), "%lf", &start) != 1 || start < 0.0) {
93                 error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
94         }
95
96         set_start (start);
97
98         if ((prop = node.property ("beats-per-minute")) == 0) {
99                 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
100                 throw failed_constructor();
101         }
102
103         if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
104                 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
105                 throw failed_constructor();
106         }
107
108         if ((prop = node.property ("note-type")) == 0) {
109                 /* older session, make note type be quarter by default */
110                 _note_type = 4.0;
111         } else {
112                 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
113                         error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
114                         throw failed_constructor();
115                 }
116         }
117
118         if ((prop = node.property ("movable")) == 0) {
119                 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
120                 throw failed_constructor();
121         }
122
123         set_movable (string_is_affirmative (prop->value()));
124
125         if ((prop = node.property ("bar-offset")) == 0) {
126                 _bar_offset = -1.0;
127         } else {
128                 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
129                         error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
130                         throw failed_constructor();
131                 }
132         }
133
134         if ((prop = node.property ("tempo-type")) == 0) {
135                 _type = Type::Constant;
136         } else {
137                 if (strstr(prop->value().c_str(),"Constant")) {
138                         _type = Type::Constant;
139                 } else {
140                         _type = Type::Ramp;
141                 }
142         }
143 }
144
145 XMLNode&
146 TempoSection::get_state() const
147 {
148         XMLNode *root = new XMLNode (xml_state_node_name);
149         char buf[256];
150         LocaleGuard lg;
151
152         snprintf (buf, sizeof (buf), "%f", beat());
153         root->add_property ("start", buf);
154         snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
155         root->add_property ("beats-per-minute", buf);
156         snprintf (buf, sizeof (buf), "%f", _note_type);
157         root->add_property ("note-type", buf);
158         // snprintf (buf, sizeof (buf), "%f", _bar_offset);
159         // root->add_property ("bar-offset", buf);
160         snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
161         root->add_property ("movable", buf);
162
163         snprintf (buf, sizeof (buf), "%s", _type == Constant?"Constant":"Ramp");
164         root->add_property ("tempo-type", buf);
165
166         return *root;
167 }
168
169 void
170
171 TempoSection::update_bar_offset_from_bbt (const Meter& m)
172 {
173         _bar_offset = (start() * BBT_Time::ticks_per_beat) /
174                 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
175
176         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, start(), m.divisions_per_bar()));
177 }
178
179 void
180 TempoSection::set_type (Type type)
181 {
182         _type = type;
183 }
184
185 /** returns the tempo at the zero-based (relative to tempo section) frame.
186 */
187 double
188 TempoSection::tempo_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
189 {
190
191         if (_type == Constant) {
192                 return beats_per_minute();
193         }
194
195         return tick_tempo_at_time (frame_to_minute (frame, frame_rate), end_bpm *  BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)) / BBT_Time::ticks_per_beat;
196 }
197
198 /** returns the zero-based frame (relative to tempo section)
199    where the tempo occurs.
200 */
201 framepos_t
202 TempoSection::frame_at_tempo (double tempo, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
203 {
204         if (_type == Constant) {
205                 return 0;
206         }
207
208         return minute_to_frame (time_at_tick_tempo (tempo *  BBT_Time::ticks_per_beat,  end_bpm *  BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
209 }
210
211 /** returns the zero-based tick (relative to tempo section)
212    where the zero-based frame (relative to tempo section)
213    lies.
214 */
215 double
216 TempoSection::tick_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
217 {
218         if (_type == Constant) {
219                 return (frame / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat;
220         }
221
222         return tick_at_time (frame_to_minute (frame, frame_rate), end_bpm *  BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
223 }
224
225 /** returns the zero-based frame (relative to tempo section)
226    where the zero-based tick (relative to tempo section)
227    falls.
228 */
229 framepos_t
230 TempoSection::frame_at_tick (double tick, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
231 {
232         if (_type == Constant) {
233                 return (framepos_t) floor ((tick  / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate));
234         }
235
236         return minute_to_frame (time_at_tick (tick, end_bpm *  BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate)), frame_rate);
237 }
238
239 /** returns the zero-based beat (relative to tempo section)
240    where the zero-based frame (relative to tempo section)
241    lies.
242 */
243 double
244 TempoSection::beat_at_frame (framepos_t frame, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
245 {
246         return tick_at_frame (frame, end_bpm, end_frame, frame_rate) / BBT_Time::ticks_per_beat;
247 }
248
249 /** returns the zero-based frame (relative to tempo section start frame)
250    where the zero-based beat (relative to tempo section start)
251    falls.
252 */
253
254 framepos_t
255 TempoSection::frame_at_beat (double beat, double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
256 {
257         return frame_at_tick (beat * BBT_Time::ticks_per_beat, end_bpm, end_frame, frame_rate);
258 }
259
260 framecnt_t
261 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
262 {
263         return time * 60.0 * frame_rate;
264 }
265
266 double
267 TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
268 {
269         return (frame / (double) frame_rate) / 60.0;
270 }
271
272 /* position function */
273 double
274 TempoSection::a_func (double end_tpm, double c_func) const
275 {
276         return log (end_tpm / ticks_per_minute()) /  c_func;
277 }
278
279 /*function constant*/
280 double
281 TempoSection::c_func (double end_tpm, double end_time) const
282 {
283         return log (end_tpm / ticks_per_minute()) /  end_time;
284 }
285
286 /* tempo in tpm at time in minutes */
287 double
288 TempoSection::tick_tempo_at_time (double time, double end_tpm, double end_time) const
289 {
290         return exp (c_func (end_tpm, end_time) * time) * ticks_per_minute();
291 }
292
293 /* time in minutes at tempo in tpm */
294 double
295 TempoSection::time_at_tick_tempo (double tick_tempo, double end_tpm, double end_time) const
296 {
297         return log (tick_tempo / ticks_per_minute()) / c_func (end_tpm, end_time);
298 }
299
300 /* tick at time in minutes */
301 double
302 TempoSection::tick_at_time (double time, double end_tpm, double end_time) const
303 {
304         return ((exp (c_func (end_tpm, end_time) * time)) - 1) * ticks_per_minute() / c_func (end_tpm, end_time);
305 }
306
307 /* time in minutes at tick */
308 double
309 TempoSection::time_at_tick (double tick, double end_tpm, double end_time) const
310 {
311         return log (((c_func (end_tpm, end_time) * tick) / ticks_per_minute()) + 1) / c_func (end_tpm, end_time);
312 }
313
314 /* beat at time in minutes */
315 double
316 TempoSection::beat_at_time (double time, double end_tpm, double end_time) const
317 {
318         return tick_at_time (time, end_tpm, end_time) / BBT_Time::ticks_per_beat;
319 }
320
321 /* time in munutes at beat */
322 double
323 TempoSection::time_at_beat (double beat, double end_tpm, double end_time) const
324 {
325         return time_at_tick (beat * BBT_Time::ticks_per_beat, end_tpm, end_time);
326 }
327
328 void
329 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
330 {
331         double new_start;
332
333         if (_bar_offset < 0.0) {
334                 /* not set yet */
335                 return;
336         }
337
338         new_start = start();
339
340         double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
341         new_start = ticks / BBT_Time::ticks_per_beat;
342
343         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
344                                                        _bar_offset, meter.divisions_per_bar(), ticks, new_start, new_start));
345
346         set_start (new_start);
347 }
348
349 /***********************************************************************/
350
351 const string MeterSection::xml_state_node_name = "Meter";
352
353 MeterSection::MeterSection (const XMLNode& node)
354         : MetricSection (0.0), Meter (TempoMap::default_meter())
355 {
356         XMLProperty const * prop;
357         BBT_Time start;
358         LocaleGuard lg;
359         const XMLProperty *prop;
360         BBT_Time bbt;
361         double beat = 0.0;
362         pair<double, BBT_Time> start;
363
364         if ((prop = node.property ("start")) == 0) {
365                 error << _("MeterSection XML node has no \"start\" property") << endmsg;
366                 throw failed_constructor();
367         }
368         if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
369                     &bbt.bars,
370                     &bbt.beats,
371                     &bbt.ticks) == 3) {
372                 /* legacy session - start used to be in bbt*/
373                 beat = -1.0;
374         } else if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
375                 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
376                 throw failed_constructor();
377         }
378         start.first = beat;
379
380         if ((prop = node.property ("bbt")) == 0) {
381                 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
382         } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
383                     &bbt.bars,
384                     &bbt.beats,
385                     &bbt.ticks) < 3) {
386                 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
387                 throw failed_constructor();
388         }
389         start.second = bbt;
390
391         set_start (start);
392
393         /* beats-per-bar is old; divisions-per-bar is new */
394
395         if ((prop = node.property ("divisions-per-bar")) == 0) {
396                 if ((prop = node.property ("beats-per-bar")) == 0) {
397                         error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
398                         throw failed_constructor();
399                 }
400         }
401
402         if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
403                 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
404                 throw failed_constructor();
405         }
406
407         if ((prop = node.property ("note-type")) == 0) {
408                 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
409                 throw failed_constructor();
410         }
411
412         if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
413                 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
414                 throw failed_constructor();
415         }
416
417         if ((prop = node.property ("movable")) == 0) {
418                 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
419                 throw failed_constructor();
420         }
421
422         set_movable (string_is_affirmative (prop->value()));
423 }
424
425 XMLNode&
426 MeterSection::get_state() const
427 {
428         XMLNode *root = new XMLNode (xml_state_node_name);
429         char buf[256];
430         LocaleGuard lg;
431
432         snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
433                   bbt().bars,
434                   bbt().beats,
435                   bbt().ticks);
436         root->add_property ("bbt", buf);
437         snprintf (buf, sizeof (buf), "%lf", start());
438         root->add_property ("start", buf);
439         snprintf (buf, sizeof (buf), "%f", _note_type);
440         root->add_property ("note-type", buf);
441         snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
442         root->add_property ("divisions-per-bar", buf);
443         snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
444         root->add_property ("movable", buf);
445
446         return *root;
447 }
448
449 /***********************************************************************/
450
451 struct MetricSectionSorter {
452     bool operator() (const MetricSection* a, const MetricSection* b) {
453             return a->start() < b->start();
454     }
455 };
456
457 struct MetricSectionFrameSorter {
458     bool operator() (const MetricSection* a, const MetricSection* b) {
459             return a->frame() < b->frame();
460     }
461 };
462
463 TempoMap::TempoMap (framecnt_t fr)
464 {
465         _frame_rate = fr;
466         BBT_Time start;
467
468         start.bars = 1;
469         start.beats = 1;
470         start.ticks = 0;
471
472         TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Type::Constant);
473         MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
474
475         t->set_movable (false);
476         m->set_movable (false);
477
478         /* note: frame time is correct (zero) for both of these */
479
480         metrics.push_back (t);
481         metrics.push_back (m);
482
483 }
484
485 TempoMap::~TempoMap ()
486 {
487 }
488
489 void
490 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
491 {
492         bool removed = false;
493
494         {
495                 Glib::Threads::RWLock::WriterLock lm (lock);
496                 if ((removed = remove_tempo_locked (tempo))) {
497                         if (complete_operation) {
498                                 recompute_map (true);
499                         }
500                 }
501         }
502
503         if (removed && complete_operation) {
504                 PropertyChanged (PropertyChange ());
505         }
506 }
507
508 bool
509 TempoMap::remove_tempo_locked (const TempoSection& tempo)
510 {
511         Metrics::iterator i;
512
513         for (i = metrics.begin(); i != metrics.end(); ++i) {
514                 if (dynamic_cast<TempoSection*> (*i) != 0) {
515                         if (tempo.frame() == (*i)->frame()) {
516                                 if ((*i)->movable()) {
517                                         metrics.erase (i);
518                                         return true;
519                                 }
520                         }
521                 }
522         }
523
524         return false;
525 }
526
527 void
528 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
529 {
530         bool removed = false;
531
532         {
533                 Glib::Threads::RWLock::WriterLock lm (lock);
534                 if ((removed = remove_meter_locked (tempo))) {
535                         if (complete_operation) {
536                                 recompute_map (true);
537                         }
538                 }
539         }
540
541         if (removed && complete_operation) {
542                 PropertyChanged (PropertyChange ());
543         }
544 }
545
546 bool
547 TempoMap::remove_meter_locked (const MeterSection& tempo)
548 {
549         Metrics::iterator i;
550
551         for (i = metrics.begin(); i != metrics.end(); ++i) {
552                 if (dynamic_cast<MeterSection*> (*i) != 0) {
553                         if (tempo.frame() == (*i)->frame()) {
554                                 if ((*i)->movable()) {
555                                         metrics.erase (i);
556                                         return true;
557                                 }
558                         }
559                 }
560         }
561
562         return false;
563 }
564
565 void
566 TempoMap::do_insert (MetricSection* section)
567 {
568         bool need_add = true;
569
570         /* we only allow new meters to be inserted on beat 1 of an existing
571          * measure.
572          */
573         MeterSection* m = 0;
574         if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
575                 assert (m->bbt().ticks == 0);
576
577                 /* we need to (potentially) update the BBT times of tempo
578                    sections based on this new meter.
579                 */
580
581                 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
582
583                         pair<double, BBT_Time> corrected = make_pair (m->start(), m->bbt());
584                         corrected.second.beats = 1;
585                         corrected.second.ticks = 0;
586                         corrected.first = bbt_to_beats (corrected.second);
587                         warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
588                                                    m->bbt(), corrected.second) << endmsg;
589                         m->set_start (corrected);
590                 }
591         }
592
593
594
595         /* Look for any existing MetricSection that is of the same type and
596            in the same bar as the new one, and remove it before adding
597            the new one. Note that this means that if we find a matching,
598            existing section, we can break out of the loop since we're
599            guaranteed that there is only one such match.
600         */
601
602         for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
603
604                 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
605                 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
606
607                 if (tempo && insert_tempo) {
608
609                         /* Tempo sections */
610
611                         if (tempo->start() == insert_tempo->start()) {
612
613                                 if (!tempo->movable()) {
614
615                                         /* can't (re)move this section, so overwrite
616                                          * its data content (but not its properties as
617                                          * a section).
618                                          */
619
620                                         *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
621                                         need_add = false;
622                                 } else {
623                                         metrics.erase (i);
624                                 }
625                                 break;
626                         }
627
628                 } else if (!tempo && !insert_tempo) {
629
630                         /* Meter Sections */
631                         MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
632                         MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
633                         if (meter->start() == insert_meter->start()) {
634
635                                 if (!meter->movable()) {
636
637                                         /* can't (re)move this section, so overwrite
638                                          * its data content (but not its properties as
639                                          * a section
640                                          */
641
642                                         *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
643                                         need_add = false;
644                                 } else {
645                                         metrics.erase (i);
646
647                                 }
648
649                                 break;
650                         }
651                 } else {
652                         /* non-matching types, so we don't care */
653                 }
654         }
655
656         /* Add the given MetricSection, if we didn't just reset an existing
657          * one above
658          */
659
660         if (need_add) {
661                 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
662                 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
663
664                 Metrics::iterator i;
665                 if (insert_meter) {
666                         for (i = metrics.begin(); i != metrics.end(); ++i) {
667                                 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
668
669                                 if (meter && meter->start() > insert_meter->start()) {
670                                         break;
671                                 }
672                         }
673                 } else if (insert_tempo) {
674                         for (i = metrics.begin(); i != metrics.end(); ++i) {
675                                 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
676
677                                 if (tempo) {
678                                         if (tempo->start() > insert_tempo->start()) {
679                                                 break;
680                                         }
681                                 }
682                         }
683                 }
684
685                 metrics.insert (i, section);
686         }
687 }
688
689 void
690 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
691 {
692         {
693                 Glib::Threads::RWLock::WriterLock lm (lock);
694                 TempoSection& first (first_tempo());
695
696                 if (ts.start() != first.start()) {
697                         remove_tempo_locked (ts);
698                         add_tempo_locked (tempo, where, true, type);
699                 } else {
700                         first.set_type (type);
701                         {
702                                 /* cannot move the first tempo section */
703                                 *static_cast<Tempo*>(&first) = tempo;
704                                 recompute_map (false);
705                         }
706                 }
707         }
708
709         PropertyChanged (PropertyChange ());
710 }
711
712 void
713 TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double  beat_where)
714 {
715         {
716                 Glib::Threads::RWLock::WriterLock lm (lock);
717
718                 /* currently this is always done in audio time */
719                 //if (ts.position_lock_style() == MusicTime) {
720                 if (0) {
721                         /* MusicTime */
722                         ts.set_start (beat_where);
723                         MetricSectionSorter cmp;
724                         metrics.sort (cmp);
725                 } else {
726                         /*AudioTime*/
727                         ts.set_frame (frame);
728
729                         MetricSectionFrameSorter fcmp;
730                         metrics.sort (fcmp);
731
732                         Metrics::const_iterator i;
733                         TempoSection* prev_ts = 0;
734                         TempoSection* next_ts = 0;
735
736                         for (i = metrics.begin(); i != metrics.end(); ++i) {
737                                 TempoSection* t;
738                                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
739
740                                         if (t->frame() >= frame) {
741                                                 break;
742                                         }
743
744                                         prev_ts = t;
745                                 }
746                         }
747
748                         for (i = metrics.begin(); i != metrics.end(); ++i) {
749                                 TempoSection* t;
750                                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
751
752                                         if (t->frame() > frame) {
753                                                 next_ts = t;
754                                                 break;
755                                         }
756                                 }
757                         }
758
759                         if (prev_ts) {
760                                 /* set the start beat */
761                                 double beats_to_ts = prev_ts->beat_at_frame (frame - prev_ts->frame(), ts.beats_per_minute(), frame - prev_ts->frame(), _frame_rate);
762                                 double beats = beats_to_ts + prev_ts->start();
763
764                                 if (next_ts) {
765                                         if (next_ts->start() < beats) {
766                                                 /* with frame-based editing, it is possible to get in a
767                                                    situation where if the tempo was placed at the mouse pointer frame,
768                                                    the following music-based tempo would jump to an earlier frame,
769                                                    changing the start beat of the moved tempo.
770                                                    in this case, we have to do some beat-based comparison TODO
771                                                 */
772                                         } else if (prev_ts->start() > beats) {
773                                                 ts.set_start (prev_ts->start());
774                                         } else {
775                                                 ts.set_start (beats);
776                                         }
777                                 } else {
778                                         ts.set_start (beats);
779                                 }
780                                 MetricSectionSorter cmp;
781                                 metrics.sort (cmp);
782                         }
783                 }
784
785                 recompute_map (false);
786         }
787
788         MetricPositionChanged (); // Emit Signal
789 }
790
791 void
792 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
793 {
794         {
795                 Glib::Threads::RWLock::WriterLock lm (lock);
796                 add_tempo_locked (tempo, where, true, type);
797         }
798
799
800         PropertyChanged (PropertyChange ());
801 }
802
803 void
804 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
805 {
806         TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
807
808         do_insert (ts);
809
810         if (recompute) {
811                 recompute_map (false);
812         }
813 }
814
815 void
816 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const double& start, const BBT_Time& where)
817 {
818         {
819                 Glib::Threads::RWLock::WriterLock lm (lock);
820                 MeterSection& first (first_meter());
821                 if (ms.start() != first.start()) {
822                         remove_meter_locked (ms);
823                         add_meter_locked (meter, start, where, true);
824                 } else {
825                         /* cannot move the first meter section */
826                         *static_cast<Meter*>(&first) = meter;
827                         recompute_map (true);
828                 }
829         }
830
831         PropertyChanged (PropertyChange ());
832 }
833
834 void
835 TempoMap::add_meter (const Meter& meter, double start, BBT_Time where)
836 {
837         {
838                 Glib::Threads::RWLock::WriterLock lm (lock);
839                 add_meter_locked (meter, start, where, true);
840         }
841
842
843 #ifndef NDEBUG
844         if (DEBUG_ENABLED(DEBUG::TempoMap)) {
845                 dump (std::cerr);
846         }
847 #endif
848
849         PropertyChanged (PropertyChange ());
850 }
851
852 void
853 TempoMap::add_meter_locked (const Meter& meter, double start, BBT_Time where, bool recompute)
854 {
855         /* a new meter always starts a new bar on the first beat. so
856            round the start time appropriately. remember that
857            `where' is based on the existing tempo map, not
858            the result after we insert the new meter.
859
860         */
861
862         if (where.beats != 1) {
863                 where.beats = 1;
864                 where.bars++;
865         }
866
867         /* new meters *always* start on a beat. */
868         where.ticks = 0;
869
870         do_insert (new MeterSection (start, where, meter.divisions_per_bar(), meter.note_divisor()));
871
872         if (recompute) {
873                 recompute_map (true);
874         }
875
876 }
877
878 void
879 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
880 {
881         Tempo newtempo (beats_per_minute, note_type);
882         TempoSection* t;
883
884         for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
885                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
886                         {
887                                 Glib::Threads::RWLock::WriterLock lm (lock);
888                                 *((Tempo*) t) = newtempo;
889                                 recompute_map (false);
890                         }
891                         PropertyChanged (PropertyChange ());
892                         break;
893                 }
894         }
895 }
896
897 void
898 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
899 {
900         Tempo newtempo (beats_per_minute, note_type);
901
902         TempoSection* prev;
903         TempoSection* first;
904         Metrics::iterator i;
905
906         /* find the TempoSection immediately preceding "where"
907          */
908
909         for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
910
911                 if ((*i)->frame() > where) {
912                         break;
913                 }
914
915                 TempoSection* t;
916
917                 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
918                         if (!first) {
919                                 first = t;
920                         }
921                         prev = t;
922                 }
923         }
924
925         if (!prev) {
926                 if (!first) {
927                         error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
928                         return;
929                 }
930
931                 prev = first;
932         }
933
934         /* reset */
935
936         {
937                 Glib::Threads::RWLock::WriterLock lm (lock);
938                 /* cannot move the first tempo section */
939                 *((Tempo*)prev) = newtempo;
940                 recompute_map (false);
941         }
942
943         PropertyChanged (PropertyChange ());
944 }
945
946 const MeterSection&
947 TempoMap::first_meter () const
948 {
949         const MeterSection *m = 0;
950
951         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
952                 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
953                         return *m;
954                 }
955         }
956
957         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
958         abort(); /*NOTREACHED*/
959         return *m;
960 }
961
962 MeterSection&
963 TempoMap::first_meter ()
964 {
965         MeterSection *m = 0;
966
967         /* CALLER MUST HOLD LOCK */
968
969         for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
970                 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
971                         return *m;
972                 }
973         }
974
975         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
976         abort(); /*NOTREACHED*/
977         return *m;
978 }
979
980 const TempoSection&
981 TempoMap::first_tempo () const
982 {
983         const TempoSection *t = 0;
984
985         /* CALLER MUST HOLD LOCK */
986
987         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
988                 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
989                         return *t;
990                 }
991         }
992
993         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
994         abort(); /*NOTREACHED*/
995         return *t;
996 }
997
998 TempoSection&
999 TempoMap::first_tempo ()
1000 {
1001         TempoSection *t = 0;
1002
1003         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1004                 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1005                         return *t;
1006                 }
1007         }
1008
1009         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1010         abort(); /*NOTREACHED*/
1011         return *t;
1012 }
1013
1014 void
1015 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
1016 {
1017         /* CALLER MUST HOLD WRITE LOCK */
1018
1019         if (end < 0) {
1020
1021                 /* we will actually stop once we hit
1022                    the last metric.
1023                 */
1024                 end = max_framepos;
1025
1026         }
1027
1028         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1029
1030         if (end == 0) {
1031                 /* silly call from Session::process() during startup
1032                  */
1033                 return;
1034         }
1035
1036         Metrics::const_iterator i;
1037
1038         TempoSection* prev_ts = 0;
1039
1040         for (i = metrics.begin(); i != metrics.end(); ++i) {
1041                 TempoSection* t;
1042
1043                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1044
1045                         if (prev_ts) {
1046                                 double const beats_relative_to_prev_ts = t->start() - prev_ts->start();
1047                                 double const ticks_relative_to_prev_ts = beats_relative_to_prev_ts * BBT_Time::ticks_per_beat;
1048
1049                                 /* assume (falsely) that the target tempo is constant */
1050                                 double const t_fpb = t->frames_per_beat (_frame_rate);
1051                                 double const av_fpb = (prev_ts->frames_per_beat (_frame_rate) + t_fpb) / 2.0;
1052                                 /* this walk shouldn't be needed as given c, time a = log (Ta / T0) / c. what to do? */
1053                                 double length_estimate = beats_relative_to_prev_ts * av_fpb;
1054
1055                                 if (prev_ts->type() == TempoSection::Type::Constant) {
1056                                         length_estimate = beats_relative_to_prev_ts * prev_ts->frames_per_beat (_frame_rate);
1057                                 }
1058
1059                                 double const system_precision_at_target_tempo = (_frame_rate / t->ticks_per_minute()) * 1.5;
1060                                 double tick_error = system_precision_at_target_tempo + 1.0; // sorry for the wtf
1061
1062                                 while (fabs (tick_error) > system_precision_at_target_tempo) {
1063
1064                                         double const actual_ticks = prev_ts->tick_at_frame (length_estimate, t->beats_per_minute(),
1065                                                                                             (framepos_t) length_estimate, _frame_rate);
1066                                         tick_error = ticks_relative_to_prev_ts - actual_ticks;
1067                                         length_estimate += tick_error * (t->ticks_per_minute() / _frame_rate);
1068                                 }
1069
1070                                 t->set_frame (length_estimate + prev_ts->frame());
1071                         }
1072                         prev_ts = t;
1073                 }
1074         }
1075
1076         Metrics::const_iterator mi;
1077         MeterSection* meter = 0;
1078
1079         for (mi = metrics.begin(); mi != metrics.end(); ++mi) {
1080                 /* we can do this beacuse we have the tempo section frames set */
1081                 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1082                         meter->set_frame (frame_at_tick (meter->start() * BBT_Time::ticks_per_beat));
1083                 }
1084         }
1085 }
1086
1087
1088 TempoMetric
1089 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1090 {
1091         Glib::Threads::RWLock::ReaderLock lm (lock);
1092         TempoMetric m (first_meter(), first_tempo());
1093
1094         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1095            at something, because we insert the default tempo and meter during
1096            TempoMap construction.
1097
1098            now see if we can find better candidates.
1099         */
1100
1101         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1102
1103                 if ((*i)->frame() > frame) {
1104                         break;
1105                 }
1106
1107                 m.set_metric(*i);
1108
1109                 if (last) {
1110                         *last = i;
1111                 }
1112         }
1113
1114         return m;
1115 }
1116 /* XX meters only */
1117 TempoMetric
1118 TempoMap::metric_at (BBT_Time bbt) const
1119 {
1120         Glib::Threads::RWLock::ReaderLock lm (lock);
1121         TempoMetric m (first_meter(), first_tempo());
1122
1123         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1124            at something, because we insert the default tempo and meter during
1125            TempoMap construction.
1126
1127            now see if we can find better candidates.
1128         */
1129
1130         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1131                 MeterSection* mw;
1132                 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1133                         BBT_Time section_start (mw->bbt());
1134
1135                         if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1136                                 break;
1137                         }
1138
1139                         m.set_metric (*i);
1140                 }
1141         }
1142
1143         return m;
1144 }
1145
1146 void
1147 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1148 {
1149         Glib::Threads::RWLock::ReaderLock lm (lock);
1150
1151         if (frame < 0) {
1152                 bbt.bars = 1;
1153                 bbt.beats = 1;
1154                 bbt.ticks = 0;
1155                 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1156                 return;
1157         }
1158         bbt = beats_to_bbt (beat_at_frame (frame));
1159 }
1160
1161 double
1162 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1163 {
1164         /* CALLER HOLDS READ LOCK */
1165
1166         double accumulated_beats = 0.0;
1167         double accumulated_bars = 0.0;
1168         MeterSection* prev_ms = 0;
1169
1170         Metrics::const_iterator i;
1171
1172         for (i = metrics.begin(); i != metrics.end(); ++i) {
1173                 MeterSection* m;
1174                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1175                         double bars_to_m = 0.0;
1176                         if (prev_ms) {
1177                                 bars_to_m = (m->start() - prev_ms->start()) / prev_ms->divisions_per_bar();
1178                         }
1179                         if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1180                                 break;
1181                         }
1182                         if (prev_ms) {
1183                                 accumulated_beats += m->start() - prev_ms->start();
1184                                 accumulated_bars += bars_to_m;
1185                         }
1186                         prev_ms = m;
1187                 }
1188         }
1189
1190         double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1191         double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1192         double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.ticks / BBT_Time::ticks_per_beat);
1193
1194         return ret;
1195 }
1196
1197 Timecode::BBT_Time
1198 TempoMap::beats_to_bbt (double beats)
1199 {
1200         /* CALLER HOLDS READ LOCK */
1201
1202         MeterSection* prev_ms = 0;
1203         uint32_t accumulated_bars = 0;
1204
1205         Metrics::const_iterator i;
1206
1207         for (i = metrics.begin(); i != metrics.end(); ++i) {
1208                 MeterSection* m = 0;
1209
1210                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1211
1212                         if (beats < m->start()) {
1213                                 /* this is the meter after the one our beat is on*/
1214                                 break;
1215                         }
1216
1217                         if (prev_ms) {
1218                                 /* we need a whole number of bars. */
1219                                 accumulated_bars += ((m->start() - prev_ms->start()) + 1) / prev_ms->divisions_per_bar();
1220                         }
1221
1222                         prev_ms = m;
1223                 }
1224         }
1225
1226         double const beats_in_ms = beats - prev_ms->start();
1227         uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1228         uint32_t const total_bars = bars_in_ms + accumulated_bars;
1229         double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1230         double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1231
1232         BBT_Time ret;
1233
1234         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1235         ret.beats = (uint32_t) floor (remaining_beats);
1236         ret.bars = total_bars;
1237
1238         /* 0 0 0 to 1 1 0 - based mapping*/
1239         ++ret.bars;
1240         ++ret.beats;
1241
1242         if (ret.ticks >= BBT_Time::ticks_per_beat) {
1243                 ++ret.beats;
1244                 ret.ticks -= BBT_Time::ticks_per_beat;
1245         }
1246
1247         if (ret.beats > prev_ms->divisions_per_bar()) {
1248                 ++ret.bars;
1249                 ret.beats = 1;
1250         }
1251
1252         return ret;
1253 }
1254
1255 double
1256 TempoMap::tick_at_frame (framecnt_t frame) const
1257 {
1258         /* HOLD (at least) THE READER LOCK */
1259
1260         Metrics::const_iterator i;
1261         TempoSection* prev_ts = 0;
1262         double accumulated_ticks = 0.0;
1263
1264         for (i = metrics.begin(); i != metrics.end(); ++i) {
1265                 TempoSection* t;
1266
1267                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1268
1269                         if ((prev_ts) && frame < t->frame()) {
1270                                 /*the previous ts is the one containing the frame */
1271
1272                                 framepos_t const time = frame - prev_ts->frame();
1273                                 framepos_t const last_frame = t->frame() - prev_ts->frame();
1274                                 double const last_beats_per_minute = t->beats_per_minute();
1275
1276                                 return prev_ts->tick_at_frame (time, last_beats_per_minute, last_frame, _frame_rate) + accumulated_ticks;
1277                         }
1278
1279                         if (prev_ts && t->frame() > prev_ts->frame()) {
1280                                 accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
1281                         }
1282
1283                         prev_ts = t;
1284                 }
1285         }
1286
1287         /* treated as constant for this ts */
1288         framecnt_t const frames_in_section = frame - prev_ts->frame();
1289         double const ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1290
1291         return ticks_in_section + accumulated_ticks;
1292
1293 }
1294
1295 framecnt_t
1296 TempoMap::frame_at_tick (double tick) const
1297 {
1298         /* HOLD THE READER LOCK */
1299
1300         double accumulated_ticks = 0.0;
1301         double accumulated_ticks_to_prev = 0.0;
1302         const TempoSection* prev_ts = 0;
1303
1304         Metrics::const_iterator i;
1305
1306         for (i = metrics.begin(); i != metrics.end(); ++i) {
1307                 TempoSection* t;
1308                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1309
1310                         if (prev_ts && t->frame() > prev_ts->frame()) {
1311                                 accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
1312                         }
1313
1314                         if (prev_ts && tick < accumulated_ticks) {
1315                                 /* prev_ts is the one affecting us. */
1316
1317                                 double const ticks_in_section = tick - accumulated_ticks_to_prev;
1318                                 framepos_t const last_time = t->frame() - prev_ts->frame();
1319                                 double const last_beats_per_minute = t->beats_per_minute();
1320
1321                                 return prev_ts->frame_at_tick (ticks_in_section, last_beats_per_minute, last_time, _frame_rate) + prev_ts->frame();
1322                         }
1323                         accumulated_ticks_to_prev = accumulated_ticks;
1324                         prev_ts = t;
1325                 }
1326         }
1327         /* must be treated as constant, irrespective of _type */
1328         double const ticks_in_section = tick - accumulated_ticks_to_prev;
1329         double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
1330
1331         framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1332
1333         return ret;
1334 }
1335
1336 double
1337 TempoMap::beat_at_frame (framecnt_t frame) const
1338 {
1339         Glib::Threads::RWLock::ReaderLock lm (lock);
1340
1341         return tick_at_frame (frame) / BBT_Time::ticks_per_beat;
1342 }
1343
1344 framecnt_t
1345 TempoMap::frame_at_beat (double beat) const
1346 {
1347         Glib::Threads::RWLock::ReaderLock lm (lock);
1348
1349         return frame_at_tick (beat * BBT_Time::ticks_per_beat);
1350 }
1351
1352 framepos_t
1353 TempoMap::frame_time (const BBT_Time& bbt)
1354 {
1355         if (bbt.bars < 1) {
1356                 warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
1357                 return 0;
1358         }
1359
1360         if (bbt.beats < 1) {
1361                 throw std::logic_error ("beats are counted from one");
1362         }
1363         Glib::Threads::RWLock::ReaderLock lm (lock);
1364
1365         framepos_t const ret = frame_at_beat (bbt_to_beats (bbt));
1366
1367         return ret;
1368 }
1369
1370
1371 framecnt_t
1372 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1373 {
1374
1375         Glib::Threads::RWLock::ReaderLock lm (lock);
1376
1377         Metrics::const_iterator i;
1378         TempoSection* first = 0;
1379         TempoSection* second = 0;
1380
1381         for (i = metrics.begin(); i != metrics.end(); ++i) {
1382                 TempoSection* t;
1383
1384                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1385
1386                         if ((*i)->frame() > pos) {
1387                                 second = t;
1388                                 break;
1389                         }
1390
1391                         first = t;
1392                 }
1393         }
1394         if (first && second) {
1395                 framepos_t const last_time = second->frame() - first->frame();
1396                 double const last_beats_per_minute = second->beats_per_minute();
1397
1398                 framepos_t const time = pos - first->frame();
1399                 double const tick_at_time = first->tick_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1400                 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1401
1402                 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, last_beats_per_minute, last_time, _frame_rate);
1403
1404                 return time_at_bbt - time;
1405         }
1406
1407         double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1408         return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1409 }
1410
1411 framepos_t
1412 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1413 {
1414         return round_to_type (fr, dir, Bar);
1415 }
1416
1417 framepos_t
1418 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1419 {
1420         return round_to_type (fr, dir, Beat);
1421 }
1422
1423 framepos_t
1424 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1425 {
1426         Glib::Threads::RWLock::ReaderLock lm (lock);
1427
1428         uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5);
1429         uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1430         uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1431
1432         ticks -= beats * BBT_Time::ticks_per_beat;
1433
1434         if (dir > 0) {
1435                 /* round to next (or same iff dir == RoundUpMaybe) */
1436
1437                 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1438
1439                 if (mod == 0 && dir == RoundUpMaybe) {
1440                         /* right on the subdivision, which is fine, so do nothing */
1441
1442                 } else if (mod == 0) {
1443                         /* right on the subdivision, so the difference is just the subdivision ticks */
1444                         ticks += ticks_one_subdivisions_worth;
1445
1446                 } else {
1447                         /* not on subdivision, compute distance to next subdivision */
1448
1449                         ticks += ticks_one_subdivisions_worth - mod;
1450                 }
1451
1452                 if (ticks >= BBT_Time::ticks_per_beat) {
1453                         ticks -= BBT_Time::ticks_per_beat;
1454                 }
1455         } else if (dir < 0) {
1456
1457                 /* round to previous (or same iff dir == RoundDownMaybe) */
1458
1459                 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1460
1461                 if (difference == 0 && dir == RoundDownAlways) {
1462                         /* right on the subdivision, but force-rounding down,
1463                            so the difference is just the subdivision ticks */
1464                         difference = ticks_one_subdivisions_worth;
1465                 }
1466
1467                 if (ticks < difference) {
1468                         ticks = BBT_Time::ticks_per_beat - ticks;
1469                 } else {
1470                         ticks -= difference;
1471                 }
1472
1473         } else {
1474                 /* round to nearest */
1475                 double rem;
1476
1477                 /* compute the distance to the previous and next subdivision */
1478
1479                 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1480
1481                         /* closer to the next subdivision, so shift forward */
1482
1483                         ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1484
1485                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1486
1487                         if (ticks > BBT_Time::ticks_per_beat) {
1488                                 ++beats;
1489                                 ticks -= BBT_Time::ticks_per_beat;
1490                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1491                         }
1492
1493                 } else if (rem > 0) {
1494
1495                         /* closer to previous subdivision, so shift backward */
1496
1497                         if (rem > ticks) {
1498                                 if (beats == 0) {
1499                                         /* can't go backwards past zero, so ... */
1500                                         return 0;
1501                                 }
1502                                 /* step back to previous beat */
1503                                 --beats;
1504                                 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1505                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1506                         } else {
1507                                 ticks = lrint (ticks - rem);
1508                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1509                         }
1510                 } else {
1511                         /* on the subdivision, do nothing */
1512                 }
1513         }
1514         return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks);
1515 }
1516
1517 framepos_t
1518 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1519 {
1520         Glib::Threads::RWLock::ReaderLock lm (lock);
1521
1522         double const beat_at_framepos = beat_at_frame (frame);
1523
1524         BBT_Time bbt (beats_to_bbt (beat_at_framepos));
1525
1526         switch (type) {
1527         case Bar:
1528                 if (dir < 0) {
1529                         /* find bar previous to 'frame' */
1530                         bbt.beats = 1;
1531                         bbt.ticks = 0;
1532                         return frame_time (bbt);
1533
1534                 } else if (dir > 0) {
1535                         /* find bar following 'frame' */
1536                         ++bbt.bars;
1537                         bbt.beats = 1;
1538                         bbt.ticks = 0;
1539                         return frame_time (bbt);
1540                 } else {
1541                         /* true rounding: find nearest bar */
1542                         framepos_t raw_ft = frame_time (bbt);
1543                         bbt.beats = 1;
1544                         bbt.ticks = 0;
1545                         framepos_t prev_ft = frame_time (bbt);
1546                         ++bbt.bars;
1547                         framepos_t next_ft = frame_time (bbt);
1548
1549                         if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) { 
1550                                 return next_ft;
1551                         } else {
1552                                 return prev_ft;
1553                         }
1554                 }
1555
1556                 break;
1557
1558         case Beat:
1559                 if (dir < 0) {
1560                         return frame_at_beat (floor (beat_at_framepos));
1561                 } else if (dir > 0) {
1562                         return frame_at_beat (ceil (beat_at_framepos));
1563                 } else {
1564                         return frame_at_beat (floor (beat_at_framepos + 0.5));
1565                 }
1566                 break;
1567         }
1568
1569         return 0;
1570 }
1571
1572 void
1573 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
1574                     framepos_t lower, framepos_t upper)
1575 {
1576         Glib::Threads::RWLock::ReaderLock lm (lock);
1577         uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper));
1578         uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower));
1579
1580         while (cnt <= upper_beat) {
1581                 framecnt_t const pos = frame_at_beat (cnt);
1582                 MeterSection const meter = meter_section_at (pos);
1583                 Tempo const tempo = tempo_at (pos);
1584                 BBT_Time const bbt = beats_to_bbt ((double) cnt);
1585
1586                 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
1587                 ++cnt;
1588         }
1589 }
1590
1591 const TempoSection&
1592 TempoMap::tempo_section_at (framepos_t frame) const
1593 {
1594         Glib::Threads::RWLock::ReaderLock lm (lock);
1595
1596         Metrics::const_iterator i;
1597         TempoSection* prev = 0;
1598
1599         for (i = metrics.begin(); i != metrics.end(); ++i) {
1600                 TempoSection* t;
1601
1602                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1603
1604                         if ((*i)->frame() > frame) {
1605                                 break;
1606                         }
1607
1608                         prev = t;
1609                 }
1610         }
1611
1612         if (prev == 0) {
1613                 fatal << endmsg;
1614                 abort(); /*NOTREACHED*/
1615         }
1616
1617         return *prev;
1618 }
1619
1620 /* don't use this to calculate length (the tempo is only correct for this frame).
1621    do that stuff based on the beat_at_frame and frame_at_beat api
1622 */
1623 double
1624 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
1625 {
1626         Glib::Threads::RWLock::ReaderLock lm (lock);
1627
1628         const TempoSection* ts_at = &tempo_section_at (frame);
1629         const TempoSection* ts_after = 0;
1630         Metrics::const_iterator i;
1631
1632         for (i = metrics.begin(); i != metrics.end(); ++i) {
1633                 TempoSection* t;
1634
1635                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1636
1637                         if ((*i)->frame() > frame) {
1638                                 ts_after = t;
1639                                 break;
1640                         }
1641                 }
1642         }
1643
1644         if (ts_after) {
1645                 return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), ts_after->beats_per_minute(), ts_after->frame(), _frame_rate));
1646         }
1647         /* must be treated as constant tempo */
1648         return ts_at->frames_per_beat (_frame_rate);
1649 }
1650
1651 const Tempo
1652 TempoMap::tempo_at (framepos_t frame) const
1653 {
1654         Glib::Threads::RWLock::ReaderLock lm (lock);
1655
1656         TempoMetric m (metric_at (frame));
1657         TempoSection* prev_ts = 0;
1658
1659         Metrics::const_iterator i;
1660
1661         for (i = metrics.begin(); i != metrics.end(); ++i) {
1662                 TempoSection* t;
1663                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1664                         if ((prev_ts) && t->frame() > frame) {
1665                                 /* this is the one past frame */
1666                                 framepos_t const time = frame - prev_ts->frame();
1667                                 framepos_t const last_time = t->frame() - prev_ts->frame();
1668                                 double const last_beats_per_minute = t->beats_per_minute();
1669                                 double const ret = prev_ts->tempo_at_frame (time, last_beats_per_minute, last_time, _frame_rate);
1670                                 Tempo const ret_tempo (ret, m.tempo().note_type ());
1671                                 return ret_tempo;
1672                         }
1673                         prev_ts = t;
1674                 }
1675         }
1676
1677         return m.tempo();
1678
1679 }
1680
1681 const MeterSection&
1682 TempoMap::meter_section_at (framepos_t frame) const
1683 {
1684         Glib::Threads::RWLock::ReaderLock lm (lock);
1685         Metrics::const_iterator i;
1686         MeterSection* prev = 0;
1687
1688         for (i = metrics.begin(); i != metrics.end(); ++i) {
1689                 MeterSection* t;
1690
1691                 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
1692
1693                         if ((*i)->frame() > frame) {
1694                                 break;
1695                         }
1696
1697                         prev = t;
1698                 }
1699         }
1700
1701         if (prev == 0) {
1702                 fatal << endmsg;
1703                 abort(); /*NOTREACHED*/
1704         }
1705
1706         return *prev;
1707 }
1708
1709 const Meter&
1710 TempoMap::meter_at (framepos_t frame) const
1711 {
1712         TempoMetric m (metric_at (frame));
1713         return m.meter();
1714 }
1715
1716 XMLNode&
1717 TempoMap::get_state ()
1718 {
1719         Metrics::const_iterator i;
1720         XMLNode *root = new XMLNode ("TempoMap");
1721
1722         {
1723                 Glib::Threads::RWLock::ReaderLock lm (lock);
1724                 for (i = metrics.begin(); i != metrics.end(); ++i) {
1725                         root->add_child_nocopy ((*i)->get_state());
1726                 }
1727         }
1728
1729         return *root;
1730 }
1731
1732 int
1733 TempoMap::set_state (const XMLNode& node, int /*version*/)
1734 {
1735         {
1736                 Glib::Threads::RWLock::WriterLock lm (lock);
1737
1738                 XMLNodeList nlist;
1739                 XMLNodeConstIterator niter;
1740                 Metrics old_metrics (metrics);
1741                 MeterSection* last_meter = 0;
1742                 metrics.clear();
1743
1744                 nlist = node.children();
1745
1746                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1747                         XMLNode* child = *niter;
1748
1749                         if (child->name() == TempoSection::xml_state_node_name) {
1750
1751                                 try {
1752                                         TempoSection* ts = new TempoSection (*child);
1753                                         metrics.push_back (ts);
1754
1755                                         if (ts->bar_offset() < 0.0) {
1756                                                 if (last_meter) {
1757                                                         //ts->update_bar_offset_from_bbt (*last_meter);
1758                                                 }
1759                                         }
1760                                 }
1761
1762                                 catch (failed_constructor& err){
1763                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1764                                         metrics = old_metrics;
1765                                         break;
1766                                 }
1767
1768                         } else if (child->name() == MeterSection::xml_state_node_name) {
1769
1770                                 try {
1771                                         MeterSection* ms = new MeterSection (*child);
1772                                         metrics.push_back (ms);
1773                                         last_meter = ms;
1774                                 }
1775
1776                                 catch (failed_constructor& err) {
1777                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1778                                         metrics = old_metrics;
1779                                         break;
1780                                 }
1781                         }
1782                 }
1783
1784                 if (niter == nlist.end()) {
1785                         MetricSectionSorter cmp;
1786                         metrics.sort (cmp);
1787                 }
1788                 /* check for legacy sessions where bbt was the base musical unit for tempo */
1789                 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1790                         MeterSection* prev_ms;
1791                         TempoSection* prev_ts;
1792                         if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1793                                 if (prev_ms->start() < 0.0) {
1794                                         /*XX we cannot possibly make this work??. */
1795                                         pair<double, BBT_Time> start = make_pair (((prev_ms->bbt().bars - 1) * 4.0) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat), prev_ms->bbt());
1796                                         prev_ms->set_start (start);
1797                                 }
1798                         } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1799                                 if (prev_ts->start() < 0.0) {
1800                                         double const start = ((prev_ts->legacy_bbt().bars - 1) * 4.0) + (prev_ts->legacy_bbt().beats - 1) + (prev_ts->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
1801                                         prev_ts->set_start (start);
1802
1803                                 }
1804                         }
1805                 }
1806                 /* check for multiple tempo/meters at the same location, which
1807                    ardour2 somehow allowed.
1808                 */
1809
1810                 Metrics::iterator prev = metrics.end();
1811                 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1812                         if (prev != metrics.end()) {
1813                                 MeterSection* ms;
1814                                 MeterSection* prev_ms;
1815                                 TempoSection* ts;
1816                                 TempoSection* prev_ts;
1817                                 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1818                                         if (prev_ms->start() == ms->start()) {
1819                                                 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->start()) << endmsg;
1820                                                 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->start()) << endmsg;
1821                                                 return -1;
1822                                         }
1823                                 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1824                                         if (prev_ts->start() == ts->start()) {
1825                                                 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->start()) << endmsg;
1826                                                 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->start()) << endmsg;
1827                                                 return -1;
1828                                         }
1829                                 }
1830                         }
1831                         prev = i;
1832                 }
1833
1834                 recompute_map (true, -1);
1835         }
1836
1837         PropertyChanged (PropertyChange ());
1838
1839         return 0;
1840 }
1841
1842 void
1843 TempoMap::dump (std::ostream& o) const
1844 {
1845         Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1846         const MeterSection* m;
1847         const TempoSection* t;
1848
1849         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1850
1851                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1852                         o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (movable? "
1853                           << t->movable() << ')' << endl;
1854                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1855                         o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
1856                           << " (movable? " << m->movable() << ')' << endl;
1857                 }
1858         }
1859 }
1860
1861 int
1862 TempoMap::n_tempos() const
1863 {
1864         Glib::Threads::RWLock::ReaderLock lm (lock);
1865         int cnt = 0;
1866
1867         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1868                 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1869                         cnt++;
1870                 }
1871         }
1872
1873         return cnt;
1874 }
1875
1876 int
1877 TempoMap::n_meters() const
1878 {
1879         Glib::Threads::RWLock::ReaderLock lm (lock);
1880         int cnt = 0;
1881
1882         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1883                 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1884                         cnt++;
1885                 }
1886         }
1887
1888         return cnt;
1889 }
1890
1891 void
1892 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1893 {
1894         {
1895                 Glib::Threads::RWLock::WriterLock lm (lock);
1896                 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1897                         if ((*i)->frame() >= where && (*i)->movable ()) {
1898                                 (*i)->set_frame ((*i)->frame() + amount);
1899                         }
1900                 }
1901
1902                 /* now reset the BBT time of all metrics, based on their new
1903                  * audio time. This is the only place where we do this reverse
1904                  * timestamp.
1905                  */
1906
1907                 Metrics::iterator i;
1908                 const MeterSection* meter;
1909                 const TempoSection* tempo;
1910                 MeterSection *m;
1911                 TempoSection *t;
1912
1913                 meter = &first_meter ();
1914                 tempo = &first_tempo ();
1915
1916                 BBT_Time start;
1917                 BBT_Time end;
1918
1919                 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
1920
1921                 bool first = true;
1922                 MetricSection* prev = 0;
1923
1924                 for (i = metrics.begin(); i != metrics.end(); ++i) {
1925
1926                         BBT_Time bbt;
1927                         //TempoMetric metric (*meter, *tempo);
1928                         MeterSection* ms = const_cast<MeterSection*>(meter);
1929                         TempoSection* ts = const_cast<TempoSection*>(tempo);
1930                         if (prev) {
1931                                 if (ts){
1932                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
1933                                                 ts->set_start (t->start());
1934                                         }
1935                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
1936                                                 ts->set_start (m->start());
1937                                         }
1938                                         ts->set_frame (prev->frame());
1939
1940                                 }
1941                                 if (ms) {
1942                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
1943                                                 pair<double, BBT_Time> start = make_pair (m->start(), m->bbt());
1944                                                 ms->set_start (start);
1945                                         }
1946                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
1947                                                 pair<double, BBT_Time> start = make_pair (t->start(), beats_to_bbt (t->start()));
1948                                                 ms->set_start (start);
1949                                         }
1950                                         ms->set_frame (prev->frame());
1951                                 }
1952
1953                         } else {
1954                                 // metric will be at frames=0 bbt=1|1|0 by default
1955                                 // which is correct for our purpose
1956                         }
1957
1958                         // cerr << bbt << endl;
1959
1960                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1961                                 t->set_start (beat_at_frame (m->frame()));
1962                                 tempo = t;
1963                                 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1964                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
1965                                 bbt_time (m->frame(), bbt);
1966
1967                                 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
1968
1969                                 if (first) {
1970                                         first = false;
1971                                 } else {
1972
1973                                         if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
1974                                                 /* round up to next beat */
1975                                                 bbt.beats += 1;
1976                                         }
1977
1978                                         bbt.ticks = 0;
1979
1980                                         if (bbt.beats != 1) {
1981                                                 /* round up to next bar */
1982                                                 bbt.bars += 1;
1983                                                 bbt.beats = 1;
1984                                         }
1985                                 }
1986                                 pair<double, BBT_Time> start = make_pair (beat_at_frame (m->frame()), bbt);
1987                                 m->set_start (start);
1988                                 meter = m;
1989                                 // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
1990                         } else {
1991                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
1992                                 abort(); /*NOTREACHED*/
1993                         }
1994
1995                         prev = (*i);
1996                 }
1997
1998                 recompute_map (true);
1999         }
2000
2001
2002         PropertyChanged (PropertyChange ());
2003 }
2004 bool
2005 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2006 {
2007         bool moved = false;
2008
2009         std::list<MetricSection*> metric_kill_list;
2010
2011         TempoSection* last_tempo = NULL;
2012         MeterSection* last_meter = NULL;
2013         bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2014         bool meter_after = false; // is there a meter marker likewise?
2015         {
2016                 Glib::Threads::RWLock::WriterLock lm (lock);
2017                 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2018                         if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2019                                 metric_kill_list.push_back(*i);
2020                                 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2021                                 if (lt)
2022                                         last_tempo = lt;
2023                                 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2024                                 if (lm)
2025                                         last_meter = lm;
2026                         }
2027                         else if ((*i)->frame() >= where) {
2028                                 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2029                                 (*i)->set_frame ((*i)->frame() - amount);
2030                                 if ((*i)->frame() == where) {
2031                                         // marker was immediately after end of range
2032                                         tempo_after = dynamic_cast<TempoSection*> (*i);
2033                                         meter_after = dynamic_cast<MeterSection*> (*i);
2034                                 }
2035                                 moved = true;
2036                         }
2037                 }
2038
2039                 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2040                 if (last_tempo && !tempo_after) {
2041                         metric_kill_list.remove(last_tempo);
2042                         last_tempo->set_frame(where);
2043                         moved = true;
2044                 }
2045                 if (last_meter && !meter_after) {
2046                         metric_kill_list.remove(last_meter);
2047                         last_meter->set_frame(where);
2048                         moved = true;
2049                 }
2050
2051                 //remove all the remaining metrics
2052                 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2053                         metrics.remove(*i);
2054                         moved = true;
2055                 }
2056
2057                 if (moved) {
2058                         recompute_map (true);
2059                 }
2060         }
2061         PropertyChanged (PropertyChange ());
2062         return moved;
2063 }
2064
2065 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2066  *  pos can be -ve, if required.
2067  */
2068 framepos_t
2069 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2070 {
2071         return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2072 }
2073
2074 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2075 framepos_t
2076 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2077 {
2078         return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2079 }
2080
2081 /** Add the BBT interval op to pos and return the result */
2082 framepos_t
2083 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2084 {
2085         cerr << "framepos_plus_bbt - untested" << endl;
2086         Glib::Threads::RWLock::ReaderLock lm (lock);
2087
2088         Metrics::const_iterator i;
2089         const MeterSection* meter;
2090         const MeterSection* m;
2091         const TempoSection* tempo;
2092         const TempoSection* next_tempo = 0;
2093         const TempoSection* t;
2094         double frames_per_beat;
2095         framepos_t effective_pos = max (pos, (framepos_t) 0);
2096
2097         meter = &first_meter ();
2098         tempo = &first_tempo ();
2099
2100         assert (meter);
2101         assert (tempo);
2102
2103         /* find the starting metrics for tempo & meter */
2104
2105         for (i = metrics.begin(); i != metrics.end(); ++i) {
2106
2107                 if ((*i)->frame() > effective_pos) {
2108                         break;
2109                 }
2110
2111                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2112                         tempo = t;
2113                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2114                         meter = m;
2115                 }
2116         }
2117
2118         for (i = metrics.begin(); i != metrics.end(); ++i) {
2119                 if ((*i)->frame() > effective_pos) {
2120                         if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2121                                 next_tempo = t;
2122                         }
2123                         break;
2124                 }
2125         }
2126
2127         /* We now have:
2128
2129            meter -> the Meter for "pos"
2130            tempo -> the Tempo for "pos"
2131            next_tempo -> the Tempo after "pos" or 0
2132            i     -> for first new metric after "pos", possibly metrics.end()
2133         */
2134
2135         /* now comes the complicated part. we have to add one beat a time,
2136            checking for a new metric on every beat.
2137         */
2138
2139         uint64_t bars = 0;
2140         /* fpb is used for constant tempo */
2141         frames_per_beat = tempo->frames_per_beat (_frame_rate);
2142
2143         while (op.bars) {
2144
2145                 bars++;
2146                 op.bars--;
2147
2148                 /* check if we need to use a new metric section: has adding frames moved us
2149                    to or after the start of the next metric section? in which case, use it.
2150                 */
2151
2152                 if (i != metrics.end()) {
2153                         if ((*i)->frame() <= pos) {
2154
2155                                 /* about to change tempo or meter, so add the
2156                                  * number of frames for the bars we've just
2157                                  * traversed before we change the
2158                                  * frames_per_beat value.
2159                                  */
2160
2161                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2162                                         next_tempo = t;
2163                                 }
2164
2165                                 if (next_tempo) {
2166                                         pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2167                                 } else {
2168                                         pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2169                                 }
2170
2171                                 bars = 0;
2172
2173                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2174                                         tempo = t;
2175                                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2176                                         meter = m;
2177                                 }
2178                                 ++i;
2179                                 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2180                         }
2181                 }
2182
2183         }
2184
2185         if (next_tempo) {
2186                 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2187         } else {
2188                 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2189         }
2190
2191         uint64_t beats = 0;
2192
2193         while (op.beats) {
2194
2195                 /* given the current meter, have we gone past the end of the bar ? */
2196
2197                 beats++;
2198                 op.beats--;
2199
2200                 /* check if we need to use a new metric section: has adding frames moved us
2201                    to or after the start of the next metric section? in which case, use it.
2202                 */
2203
2204                 if (i != metrics.end()) {
2205                         if ((*i)->frame() <= pos) {
2206
2207                                 /* about to change tempo or meter, so add the
2208                                  * number of frames for the beats we've just
2209                                  * traversed before we change the
2210                                  * frames_per_beat value.
2211                                  */
2212
2213                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2214                                         next_tempo = t;
2215                                 }
2216
2217                                 if (next_tempo) {
2218                                         pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2219                                 } else {
2220                                         pos += llrint (beats * frames_per_beat);
2221                                 }
2222
2223                                 beats = 0;
2224
2225                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2226                                         tempo = t;
2227                                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2228                                         meter = m;
2229                                 }
2230                                 ++i;
2231                                 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2232                         }
2233                 }
2234         }
2235
2236         if (next_tempo) {
2237                 pos += tempo->frame_at_beat (beats, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2238         } else {
2239                 pos += llrint (beats * frames_per_beat);
2240         }
2241
2242         if (op.ticks) {
2243                 pos += tempo->frame_at_tick (op.ticks, next_tempo->beats_per_minute(), next_tempo->frame(), _frame_rate);
2244         }
2245
2246         return pos;
2247
2248 }
2249
2250 /** Count the number of beats that are equivalent to distance when going forward,
2251     starting at pos.
2252 */
2253 Evoral::Beats
2254 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2255 {
2256         return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2257 }
2258
2259 struct bbtcmp {
2260     bool operator() (const BBT_Time& a, const BBT_Time& b) {
2261             return a < b;
2262     }
2263 };
2264
2265 std::ostream&
2266 operator<< (std::ostream& o, const Meter& m) {
2267         return o << m.divisions_per_bar() << '/' << m.note_divisor();
2268 }
2269
2270 std::ostream&
2271 operator<< (std::ostream& o, const Tempo& t) {
2272         return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2273 }
2274
2275 std::ostream&
2276 operator<< (std::ostream& o, const MetricSection& section) {
2277
2278         o << "MetricSection @ " << section.frame() << ' ';
2279
2280         const TempoSection* ts;
2281         const MeterSection* ms;
2282
2283         if ((ts = dynamic_cast<const TempoSection*> (&section)) != 0) {
2284                 o << *((const Tempo*) ts);
2285         } else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
2286                 //o << *((const Meter*) ms);
2287         }
2288
2289         return o;
2290 }