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