Tempo ramps -consolidate code, add precision check for tempo metrics.
[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
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
31
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
35
36 #include "i18n.h"
37 #include <locale.h>
38
39 using namespace std;
40 using namespace ARDOUR;
41 using namespace PBD;
42
43 using Timecode::BBT_Time;
44
45 /* _default tempo is 4/4 qtr=120 */
46
47 Meter    TempoMap::_default_meter (4.0, 4.0);
48 Tempo    TempoMap::_default_tempo (120.0);
49
50 /***********************************************************************/
51
52 double
53 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
54 {
55         /* This is tempo- and meter-sensitive. The number it returns
56            is based on the interval between any two lines in the
57            grid that is constructed from tempo and meter sections.
58
59            The return value IS NOT interpretable in terms of "beats".
60         */
61
62         return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
63 }
64
65 double
66 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
67 {
68         return frames_per_grid (tempo, sr) * _divisions_per_bar;
69 }
70
71
72 /***********************************************************************/
73
74 const string TempoSection::xml_state_node_name = "Tempo";
75
76 TempoSection::TempoSection (const XMLNode& node)
77         : MetricSection (0.0), Tempo (TempoMap::default_tempo())
78 {
79         const XMLProperty *prop;
80         LocaleGuard lg;
81         BBT_Time bbt;
82         double beat;
83         uint32_t frame;
84
85         if ((prop = node.property ("start")) != 0) {
86                 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
87                             &bbt.bars,
88                             &bbt.beats,
89                             &bbt.ticks) == 3) {
90                         /* legacy session - start used to be in bbt*/
91                         _legacy_bbt = bbt;
92                         beat = -1.0;
93                         set_beat (beat);
94                 }
95         } else {
96                 warning << _("TempoSection XML node has no \"start\" property") << endmsg;
97         }
98
99
100         if ((prop = node.property ("beat")) != 0) {
101                 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
102                         error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
103                 } else {
104                         set_beat (beat);
105                 }
106         }
107         if ((prop = node.property ("frame")) != 0) {
108                 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
109                         error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
110                 } else {
111                         set_frame (frame);
112                 }
113         }
114
115         if ((prop = node.property ("beats-per-minute")) == 0) {
116                 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
117                 throw failed_constructor();
118         }
119
120         if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
121                 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
122                 throw failed_constructor();
123         }
124
125         if ((prop = node.property ("note-type")) == 0) {
126                 /* older session, make note type be quarter by default */
127                 _note_type = 4.0;
128         } else {
129                 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
130                         error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
131                         throw failed_constructor();
132                 }
133         }
134
135         if ((prop = node.property ("movable")) == 0) {
136                 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
137                 throw failed_constructor();
138         }
139
140         set_movable (string_is_affirmative (prop->value()));
141
142         if ((prop = node.property ("bar-offset")) == 0) {
143                 _bar_offset = -1.0;
144         } else {
145                 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
146                         error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
147                         throw failed_constructor();
148                 }
149         }
150
151         if ((prop = node.property ("tempo-type")) == 0) {
152                 _type = Constant;
153         } else {
154                 _type = Type (string_2_enum (prop->value(), _type));
155         }
156
157         if ((prop = node.property ("lock-style")) == 0) {
158                 set_position_lock_style (MusicTime);
159         } else {
160                 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
161         }
162 }
163
164 XMLNode&
165 TempoSection::get_state() const
166 {
167         XMLNode *root = new XMLNode (xml_state_node_name);
168         char buf[256];
169         LocaleGuard lg;
170
171         snprintf (buf, sizeof (buf), "%f", beat());
172         root->add_property ("beat", buf);
173         snprintf (buf, sizeof (buf), "%li", frame());
174         root->add_property ("frame", buf);
175         snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
176         root->add_property ("beats-per-minute", buf);
177         snprintf (buf, sizeof (buf), "%f", _note_type);
178         root->add_property ("note-type", buf);
179         // snprintf (buf, sizeof (buf), "%f", _bar_offset);
180         // root->add_property ("bar-offset", buf);
181         snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
182         root->add_property ("movable", buf);
183         root->add_property ("tempo-type", enum_2_string (_type));
184         root->add_property ("lock-style", enum_2_string (position_lock_style()));
185
186         return *root;
187 }
188
189 void
190
191 TempoSection::update_bar_offset_from_bbt (const Meter& m)
192 {
193         _bar_offset = (beat() * BBT_Time::ticks_per_beat) /
194                 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
195
196         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, beat(), m.divisions_per_bar()));
197 }
198
199 void
200 TempoSection::set_type (Type type)
201 {
202         _type = type;
203 }
204
205 /** returns the tempo at the zero-based (relative to session) frame.
206 */
207 double
208 TempoSection::tempo_at_frame (framepos_t f, framecnt_t frame_rate) const
209 {
210
211         if (_type == Constant) {
212                 return beats_per_minute();
213         }
214
215         return tick_tempo_at_time (frame_to_minute (f - frame(), frame_rate)) / BBT_Time::ticks_per_beat;
216 }
217
218 /** returns the zero-based frame (relative to session)
219    where the tempo occurs in this section.
220    beat b is only used for constant tempos.
221    note that the tempo map may have multiple such values.
222 */
223 framepos_t
224 TempoSection::frame_at_tempo (double bpm, double b, framecnt_t frame_rate) const
225 {
226         if (_type == Constant) {
227                 return ((b - beat())  * frames_per_beat (frame_rate))  + frame();
228         }
229
230         return minute_to_frame (time_at_tick_tempo (bpm *  BBT_Time::ticks_per_beat), frame_rate) + frame();
231 }
232 /** returns the tempo at the zero-based (relative to session) beat.
233 */
234 double
235 TempoSection::tempo_at_beat (double b) const
236 {
237
238         if (_type == Constant) {
239                 return beats_per_minute();
240         }
241
242         return tick_tempo_at_tick ((b - beat()) * BBT_Time::ticks_per_beat) / BBT_Time::ticks_per_beat;
243 }
244
245 /** returns the zero-based beat (relative to session)
246    where the tempo occurs given frame f. frame f is only used for constant tempos.
247    note that the session tempo map may have multiple beats at a given tempo.
248 */
249 double
250 TempoSection::beat_at_tempo (double bpm, framepos_t f, framecnt_t frame_rate) const
251 {
252         if (_type == Constant) {
253                 double const ticks = (((f - frame()) / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat) + tick();
254                 return ticks / BBT_Time::ticks_per_beat;
255         }
256
257         return (tick_at_tick_tempo (bpm *  BBT_Time::ticks_per_beat) + tick()) / BBT_Time::ticks_per_beat;
258 }
259
260 /** returns the zero-based beat (relative to session origin)
261    where the zero-based frame (relative to session)
262    lies.
263 */
264 double
265 TempoSection::beat_at_frame (framepos_t frame, framecnt_t frame_rate) const
266 {
267         return tick_at_frame (frame, frame_rate) / BBT_Time::ticks_per_beat;
268 }
269
270 /** returns the zero-based frame (relative to session start frame)
271    where the zero-based beat (relative to session start)
272    falls.
273 */
274
275 framepos_t
276 TempoSection::frame_at_beat (double beat, framecnt_t frame_rate) const
277 {
278         return frame_at_tick (beat * BBT_Time::ticks_per_beat, frame_rate);
279 }
280
281 /** returns the zero-based tick (relative to session origin)
282    where the zero-based frame (relative to tempo section)
283    lies.
284 */
285 double
286 TempoSection::tick_at_frame (framepos_t f, framecnt_t frame_rate) const
287 {
288         if (_type == Constant) {
289                 return (((f - frame()) / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat) + tick();
290         }
291
292         return tick_at_time (frame_to_minute (f - frame(), frame_rate)) + tick();
293 }
294
295 /** returns the zero-based frame (relative to session origin)
296    where the zero-based tick (relative to session)
297    falls.
298 */
299 framepos_t
300 TempoSection::frame_at_tick (double t, framecnt_t frame_rate) const
301 {
302         if (_type == Constant) {
303                 return (framepos_t) floor (((t - tick()) / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate)) + frame();
304         }
305
306         return minute_to_frame (time_at_tick (t - tick()), frame_rate) + frame();
307 }
308
309 /*
310 Ramp Overview
311
312       |                     *
313 Tempo |                   *
314 Tt----|-----------------*|
315 Ta----|--------------|*  |
316       |            * |   |
317       |         *    |   |
318       |     *        |   |
319 T0----|*             |   |
320   *   |              |   |
321       _______________|___|____
322       time           a   t (next tempo)
323       [        c         ] defines c
324
325 Duration in beats at time a is the integral of some Tempo function.
326 In our case, the Tempo function (Tempo at time t) is
327 T(t) = T0(e^(ct))
328
329 with function constant
330 c = log(Ta/T0)/a
331 so
332 a = log(Ta/T0)/c
333
334 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
335 b(t) = T0(e^(ct) - 1) / c
336
337 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
338 t(b) = log((cb / T0) + 1) / c
339
340 The time t at which Tempo T occurs is a as above:
341 t(T) = log(T / T0) / c
342
343 The beat at which a Tempo T occurs is:
344 b(T) = (T - T0) / c
345
346 The Tempo at which beat b occurs is:
347 T(b) = b.c + T0
348
349 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
350 Our problem is that we usually don't know t.
351 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
352 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
353 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
354
355 By substituting our expanded t as a in the c function above, our problem is reduced to:
356 c = T0 (e^(log (Ta / T0)) - 1) / b
357
358 We can now store c for future time calculations.
359 If the following tempo section (the one that defines c in conjunction with this one)
360 is changed or moved, c is no longer valid.
361
362 The public methods are session-relative.
363
364 Most of this stuff is taken from this paper:
365
366 WHERE’S THE BEAT?
367 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
368 Jan C. Schacher
369 Martin Neukom
370 Zurich University of Arts
371 Institute for Computer Music and Sound Technology
372
373 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
374
375 */
376
377 /* compute this ramp's function constant using the end tempo and duration (beats into global start) of some later tempo section*/
378 double
379 TempoSection::compute_c_func_beat (double end_bpm, double end_beat, framecnt_t frame_rate)
380 {
381         double const log_tempo_ratio = log ((end_bpm * BBT_Time::ticks_per_beat) / ticks_per_minute());
382         return ticks_per_minute() *  (exp (log_tempo_ratio) - 1) / ((end_beat - beat()) * BBT_Time::ticks_per_beat);
383 }
384
385 /* compute the function constant from some later tempo section, given tempo (beats/min.) and distance (in frames) from session origin */
386 double
387 TempoSection::compute_c_func_frame (double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
388 {
389         return c_func (end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame - frame(), frame_rate));
390 }
391
392 framecnt_t
393 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
394 {
395         return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
396 }
397
398 double
399 TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
400 {
401         return (frame / (double) frame_rate) / 60.0;
402 }
403
404 /* position function */
405 double
406 TempoSection::a_func (double end_tpm, double c_func) const
407 {
408         return log (end_tpm / ticks_per_minute()) /  c_func;
409 }
410
411 /*function constant*/
412 double
413 TempoSection::c_func (double end_tpm, double end_time) const
414 {
415         return log (end_tpm / ticks_per_minute()) /  end_time;
416 }
417
418 /* tempo in tpm at time in minutes */
419 double
420 TempoSection::tick_tempo_at_time (double time) const
421 {
422         return exp (_c_func * time) * ticks_per_minute();
423 }
424
425 /* time in minutes at tempo in tpm */
426 double
427 TempoSection::time_at_tick_tempo (double tick_tempo) const
428 {
429         return log (tick_tempo / ticks_per_minute()) / _c_func;
430 }
431
432 /* tick at tempo in tpm */
433 double
434 TempoSection::tick_at_tick_tempo (double tick_tempo) const
435 {
436         return (tick_tempo - ticks_per_minute()) / _c_func;
437 }
438
439 /* tempo in tpm at tick */
440 double
441 TempoSection::tick_tempo_at_tick (double tick) const
442 {
443         return (tick * _c_func) + ticks_per_minute();
444 }
445
446 /* tick at time in minutes */
447 double
448 TempoSection::tick_at_time (double time) const
449 {
450         return ((exp (_c_func * time)) - 1) * ticks_per_minute() / _c_func;
451 }
452
453 /* time in minutes at tick */
454 double
455 TempoSection::time_at_tick (double tick) const
456 {
457         return log (((_c_func * tick) / ticks_per_minute()) + 1) / _c_func;
458 }
459
460 /* beat at time in minutes */
461 double
462 TempoSection::beat_at_time (double time) const
463 {
464         return tick_at_time (time) / BBT_Time::ticks_per_beat;
465 }
466
467 /* time in munutes at beat */
468 double
469 TempoSection::time_at_beat (double beat) const
470 {
471         return time_at_tick (beat * BBT_Time::ticks_per_beat);
472 }
473
474 void
475 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
476 {
477         double new_beat;
478
479         if (_bar_offset < 0.0) {
480                 /* not set yet */
481                 return;
482         }
483
484         new_beat = beat();
485
486         double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
487         new_beat = ticks / BBT_Time::ticks_per_beat;
488
489         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
490                                                        _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
491
492         set_beat (new_beat);
493 }
494
495 /***********************************************************************/
496
497 /*
498 if a meter section is position locked to frames, then it can only be at 1|1|0.
499 thus we can have multiple 1|1|0s in the session tempo map.
500
501 like this:
502
503 BBT  1|1|0 2|1|0 3|1|0 1|1|0
504 beat   0     4     8    12
505
506 all other meter sections are locked to beats.
507
508 the beat of a meter section is used to find its position rather than the stored bbt.
509 */
510
511 const string MeterSection::xml_state_node_name = "Meter";
512
513 MeterSection::MeterSection (const XMLNode& node)
514         : MetricSection (0.0), Meter (TempoMap::default_meter())
515 {
516         XMLProperty const * prop;
517         BBT_Time start;
518         LocaleGuard lg;
519         const XMLProperty *prop;
520         BBT_Time bbt;
521         double beat = 0.0;
522         framepos_t frame = 0;
523         pair<double, BBT_Time> start;
524
525         if ((prop = node.property ("start")) != 0) {
526                 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
527                     &bbt.bars,
528                     &bbt.beats,
529                     &bbt.ticks) < 3) {
530                         error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
531                 } else {
532                         /* legacy session - start used to be in bbt*/
533                         beat = -1.0;
534                 }
535         } else {
536                 error << _("MeterSection XML node has no \"start\" property") << endmsg;
537         }
538
539         if ((prop = node.property ("beat")) != 0) {
540                 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
541                         error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
542                 }
543         }
544
545         start.first = beat;
546
547         if ((prop = node.property ("bbt")) == 0) {
548                 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
549         } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
550                     &bbt.bars,
551                     &bbt.beats,
552                     &bbt.ticks) < 3) {
553                 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
554                 //throw failed_constructor();
555         }
556
557         start.second = bbt;
558
559         set_beat (start);
560
561         if ((prop = node.property ("frame")) != 0) {
562                 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
563                         error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
564                 } else {
565                         set_frame (frame);
566                 }
567         }
568
569         /* beats-per-bar is old; divisions-per-bar is new */
570
571         if ((prop = node.property ("divisions-per-bar")) == 0) {
572                 if ((prop = node.property ("beats-per-bar")) == 0) {
573                         error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
574                         throw failed_constructor();
575                 }
576         }
577         if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
578                 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
579                 throw failed_constructor();
580         }
581
582         if ((prop = node.property ("note-type")) == 0) {
583                 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
584                 throw failed_constructor();
585         }
586         if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
587                 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
588                 throw failed_constructor();
589         }
590
591         if ((prop = node.property ("lock-style")) == 0) {
592                 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
593                 set_position_lock_style (MusicTime);
594         } else {
595                 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
596         }
597
598         if ((prop = node.property ("movable")) == 0) {
599                 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
600                 throw failed_constructor();
601         }
602
603         set_movable (string_is_affirmative (prop->value()));
604 }
605
606 XMLNode&
607 MeterSection::get_state() const
608 {
609         XMLNode *root = new XMLNode (xml_state_node_name);
610         char buf[256];
611         LocaleGuard lg;
612
613         snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
614                   bbt().bars,
615                   bbt().beats,
616                   bbt().ticks);
617         root->add_property ("bbt", buf);
618         snprintf (buf, sizeof (buf), "%lf", beat());
619         root->add_property ("beat", buf);
620         snprintf (buf, sizeof (buf), "%f", _note_type);
621         root->add_property ("note-type", buf);
622         snprintf (buf, sizeof (buf), "%li", frame());
623         root->add_property ("frame", buf);
624         root->add_property ("lock-style", enum_2_string (position_lock_style()));
625         snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
626         root->add_property ("divisions-per-bar", buf);
627         snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
628         root->add_property ("movable", buf);
629
630         return *root;
631 }
632
633 /***********************************************************************/
634
635 struct MetricSectionSorter {
636     bool operator() (const MetricSection* a, const MetricSection* b) {
637             return a->beat() < b->beat();
638     }
639 };
640
641 struct MetricSectionFrameSorter {
642     bool operator() (const MetricSection* a, const MetricSection* b) {
643             return a->frame() < b->frame();
644     }
645 };
646
647 TempoMap::TempoMap (framecnt_t fr)
648 {
649         _frame_rate = fr;
650         BBT_Time start (1, 1, 0);
651
652         TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
653         MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
654
655         t->set_movable (false);
656         m->set_movable (false);
657
658         /* note: frame time is correct (zero) for both of these */
659
660         _metrics.push_back (t);
661         _metrics.push_back (m);
662
663 }
664
665 TempoMap::~TempoMap ()
666 {
667 }
668
669 void
670 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
671 {
672         bool removed = false;
673
674         {
675                 Glib::Threads::RWLock::WriterLock lm (lock);
676                 if ((removed = remove_tempo_locked (tempo))) {
677                         if (complete_operation) {
678                                 recompute_map (_metrics);
679                         }
680                 }
681         }
682
683         if (removed && complete_operation) {
684                 PropertyChanged (PropertyChange ());
685         }
686 }
687
688 bool
689 TempoMap::remove_tempo_locked (const TempoSection& tempo)
690 {
691         Metrics::iterator i;
692
693         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
694                 if (dynamic_cast<TempoSection*> (*i) != 0) {
695                         if (tempo.frame() == (*i)->frame()) {
696                                 if ((*i)->movable()) {
697                                         _metrics.erase (i);
698                                         return true;
699                                 }
700                         }
701                 }
702         }
703
704         return false;
705 }
706
707 void
708 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
709 {
710         bool removed = false;
711
712         {
713                 Glib::Threads::RWLock::WriterLock lm (lock);
714                 if ((removed = remove_meter_locked (tempo))) {
715                         if (complete_operation) {
716                                 recompute_map (_metrics);
717                         }
718                 }
719         }
720
721         if (removed && complete_operation) {
722                 PropertyChanged (PropertyChange ());
723         }
724 }
725
726 bool
727 TempoMap::remove_meter_locked (const MeterSection& tempo)
728 {
729         Metrics::iterator i;
730
731         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
732                 if (dynamic_cast<MeterSection*> (*i) != 0) {
733                         if (tempo.frame() == (*i)->frame()) {
734                                 if ((*i)->movable()) {
735                                         _metrics.erase (i);
736                                         return true;
737                                 }
738                         }
739                 }
740         }
741
742         return false;
743 }
744
745 void
746 TempoMap::do_insert (MetricSection* section)
747 {
748         bool need_add = true;
749
750         /* we only allow new meters to be inserted on beat 1 of an existing
751          * measure.
752          */
753         MeterSection* m = 0;
754         if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
755                 assert (m->bbt().ticks == 0);
756
757                 if (m->position_lock_style() == MusicTime && ((m->bbt().beats != 1) || (m->bbt().ticks != 0))) {
758
759                         pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
760                         corrected.second.beats = 1;
761                         corrected.second.ticks = 0;
762                         corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
763                         warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
764                                                    m->bbt(), corrected.second) << endmsg;
765                         m->set_beat (corrected);
766                 }
767         }
768
769         /* Look for any existing MetricSection that is of the same type and
770            in the same bar as the new one, and remove it before adding
771            the new one. Note that this means that if we find a matching,
772            existing section, we can break out of the loop since we're
773            guaranteed that there is only one such match.
774         */
775
776         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
777
778                 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
779                 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
780
781                 if (tempo && insert_tempo) {
782
783                         /* Tempo sections */
784                         PositionLockStyle const ipl = insert_tempo->position_lock_style();
785                         if ((ipl == MusicTime && tempo->beat() == insert_tempo->beat()) || (ipl == AudioTime && tempo->frame() == insert_tempo->frame())) {
786
787                                 if (!tempo->movable()) {
788
789                                         /* can't (re)move this section, so overwrite
790                                          * its data content (but not its properties as
791                                          * a section).
792                                          */
793
794                                         *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
795                                         need_add = false;
796                                 } else {
797                                         _metrics.erase (i);
798                                 }
799                                 break;
800                         }
801
802                 } else if (!tempo && !insert_tempo) {
803
804                         /* Meter Sections */
805                         MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
806                         MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
807                         PositionLockStyle const ipl = insert_meter->position_lock_style();
808
809                         if ((ipl == MusicTime && meter->beat() == insert_meter->beat()) || (ipl == AudioTime && meter->frame() == insert_meter->frame())) {
810
811                                 if (!meter->movable()) {
812
813                                         /* can't (re)move this section, so overwrite
814                                          * its data content (but not its properties as
815                                          * a section
816                                          */
817
818                                         *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
819                                         need_add = false;
820                                 } else {
821                                         _metrics.erase (i);
822
823                                 }
824
825                                 break;
826                         }
827                 } else {
828                         /* non-matching types, so we don't care */
829                 }
830         }
831
832         /* Add the given MetricSection, if we didn't just reset an existing
833          * one above
834          */
835
836         if (need_add) {
837                 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
838                 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
839
840                 Metrics::iterator i;
841                 if (insert_meter) {
842                         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
843                                 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
844
845                                 if (meter) {
846                                         PositionLockStyle const ipl = insert_meter->position_lock_style();
847                                         if ((ipl == MusicTime && meter->beat() > insert_meter->beat()) || (ipl == AudioTime && meter->frame() > insert_meter->frame())) {
848                                                 break;
849                                         }
850                                 }
851                         }
852                 } else if (insert_tempo) {
853                         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
854                                 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
855
856                                 if (tempo) {
857                                         PositionLockStyle const ipl = insert_tempo->position_lock_style();
858                                         if ((ipl == MusicTime && tempo->beat() > insert_tempo->beat()) || (ipl == AudioTime && tempo->frame() > insert_tempo->frame())) {
859                                                 break;
860                                         }
861                                 }
862                         }
863                 }
864
865                 _metrics.insert (i, section);
866                 //dump (std::cerr);
867         }
868 }
869
870 void
871 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
872 {
873         {
874                 Glib::Threads::RWLock::WriterLock lm (lock);
875                 TempoSection& first (first_tempo());
876                 if (ts.beat() != first.beat()) {
877                         remove_tempo_locked (ts);
878                         add_tempo_locked (tempo, where, true, type);
879                 } else {
880                         first.set_type (type);
881                         {
882                                 /* cannot move the first tempo section */
883                                 *static_cast<Tempo*>(&first) = tempo;
884                                 recompute_map (_metrics);
885                         }
886                 }
887         }
888
889         PropertyChanged (PropertyChange ());
890 }
891
892 void
893 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
894 {
895         {
896                 Glib::Threads::RWLock::WriterLock lm (lock);
897                 TempoSection& first (first_tempo());
898                 if (ts.frame() != first.frame()) {
899                         remove_tempo_locked (ts);
900                         add_tempo_locked (tempo, frame, true, type);
901                 } else {
902                         first.set_type (type);
903                         {
904                                 /* cannot move the first tempo section */
905                                 *static_cast<Tempo*>(&first) = tempo;
906                                 recompute_map (_metrics);
907                         }
908                 }
909         }
910         PropertyChanged (PropertyChange ());
911 }
912
913 void
914 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
915 {
916         {
917                 Glib::Threads::RWLock::WriterLock lm (lock);
918                 add_tempo_locked (tempo, where, true, type);
919         }
920
921         PropertyChanged (PropertyChange ());
922 }
923
924 void
925 TempoMap::add_tempo (const Tempo& tempo, framepos_t frame, ARDOUR::TempoSection::Type type)
926 {
927         {
928                 Glib::Threads::RWLock::WriterLock lm (lock);
929                 add_tempo_locked (tempo, frame, true, type);
930         }
931
932
933         PropertyChanged (PropertyChange ());
934 }
935
936 void
937 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
938 {
939         TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
940
941         do_insert (ts);
942
943         if (recompute) {
944                 recompute_map (_metrics);
945         }
946 }
947
948 void
949 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
950 {
951         TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
952
953         do_insert (ts);
954
955         if (recompute) {
956                 recompute_map (_metrics);
957         }
958 }
959
960 void
961 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
962 {
963         {
964                 Glib::Threads::RWLock::WriterLock lm (lock);
965                 MeterSection& first (first_meter());
966                 if (ms.beat() != first.beat()) {
967                         remove_meter_locked (ms);
968                         add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
969                 } else {
970                         /* cannot move the first meter section */
971                         *static_cast<Meter*>(&first) = meter;
972                         recompute_map (_metrics);
973                 }
974         }
975
976         PropertyChanged (PropertyChange ());
977 }
978
979 void
980 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
981 {
982         {
983                 Glib::Threads::RWLock::WriterLock lm (lock);
984                 MeterSection& first (first_meter());
985                 if (ms.beat() != first.beat()) {
986                         remove_meter_locked (ms);
987                         add_meter_locked (meter, frame, true);
988                 } else {
989                         /* cannot move the first meter section */
990                         *static_cast<Meter*>(&first) = meter;
991                         recompute_map (_metrics);
992                 }
993         }
994
995         PropertyChanged (PropertyChange ());
996 }
997
998
999 void
1000 TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
1001 {
1002         {
1003                 Glib::Threads::RWLock::WriterLock lm (lock);
1004                 add_meter_locked (meter, beat, where, true);
1005         }
1006
1007
1008 #ifndef NDEBUG
1009         if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1010                 dump (std::cerr);
1011         }
1012 #endif
1013
1014         PropertyChanged (PropertyChange ());
1015 }
1016
1017 void
1018 TempoMap::add_meter (const Meter& meter, framepos_t frame)
1019 {
1020         {
1021                 Glib::Threads::RWLock::WriterLock lm (lock);
1022                 add_meter_locked (meter, frame, true);
1023         }
1024
1025
1026 #ifndef NDEBUG
1027         if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1028                 dump (std::cerr);
1029         }
1030 #endif
1031
1032         PropertyChanged (PropertyChange ());
1033 }
1034
1035 void
1036 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1037 {
1038         /* a new meter always starts a new bar on the first beat. so
1039            round the start time appropriately. remember that
1040            `where' is based on the existing tempo map, not
1041            the result after we insert the new meter.
1042
1043         */
1044
1045         if (where.beats != 1) {
1046                 where.beats = 1;
1047                 where.bars++;
1048         }
1049
1050         /* new meters *always* start on a beat. */
1051         where.ticks = 0;
1052
1053         do_insert (new MeterSection (beat, where, meter.divisions_per_bar(), meter.note_divisor()));
1054
1055         if (recompute) {
1056                 recompute_map (_metrics);
1057         }
1058
1059 }
1060
1061 void
1062 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, bool recompute)
1063 {
1064
1065         /* MusicTime meters always start on 1|1|0. */
1066         do_insert (new MeterSection (frame, meter.divisions_per_bar(), meter.note_divisor()));
1067
1068         if (recompute) {
1069                 recompute_map (_metrics);
1070         }
1071
1072 }
1073
1074 /**
1075 * This is for a gui that needs to know the frame of a tempo section if it were to be moved to some bbt time,
1076 * taking any possible reordering as a consequence of this into account.
1077 * @param section - the section to be altered
1078 * @param bpm - the new Tempo
1079 * @param bbt - the bbt where the altered tempo will fall
1080 * @return returns - the position in frames where the new tempo section will lie.
1081 */
1082 framepos_t
1083 TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
1084 {
1085         Glib::Threads::RWLock::ReaderLock lm (lock);
1086         Metrics future_map;
1087         framepos_t ret = 0;
1088         TempoSection* new_section = copy_metrics_and_point (future_map, section);
1089         double const beat = bbt_to_beats_locked (future_map, bbt);
1090         if (solve_map (future_map, new_section, bpm, beat)) {
1091                 ret = new_section->frame();
1092         } else {
1093                 ret = frame_at_beat_locked (_metrics, beat);
1094         }
1095
1096         Metrics::const_iterator d = future_map.begin();
1097         while (d != future_map.end()) {
1098                 delete (*d);
1099                 ++d;
1100         }
1101         return ret;
1102 }
1103
1104 void
1105 TempoMap::gui_move_tempo (TempoSection* ts,  const Tempo& bpm, const framepos_t& frame)
1106 {
1107         Metrics future_map;
1108         {
1109                 Glib::Threads::RWLock::WriterLock lm (lock);
1110                 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1111                 if (solve_map (future_map, new_section, bpm, frame)) {
1112                         solve_map (_metrics, ts, bpm, frame);
1113                 }
1114         }
1115
1116         Metrics::const_iterator d = future_map.begin();
1117         while (d != future_map.end()) {
1118                 delete (*d);
1119                 ++d;
1120         }
1121
1122         MetricPositionChanged (); // Emit Signal
1123 }
1124
1125 void
1126 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t&  frame)
1127 {
1128         {
1129                 Glib::Threads::RWLock::WriterLock lm (lock);
1130                 solve_map (_metrics, ms, mt, frame);
1131         }
1132
1133         MetricPositionChanged (); // Emit Signal
1134 }
1135
1136 void
1137 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const double&  beat)
1138 {
1139         {
1140                 Glib::Threads::RWLock::WriterLock lm (lock);
1141                 solve_map (_metrics, ms, mt, beat);
1142         }
1143
1144         MetricPositionChanged (); // Emit Signal
1145 }
1146
1147 TempoSection*
1148 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
1149 {
1150         TempoSection* t;
1151         TempoSection* ret = 0;
1152         MeterSection* m;
1153
1154         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1155                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1156                         if (t == section) {
1157                                 if (t->position_lock_style() == MusicTime) {
1158                                         ret = new TempoSection (t->beat(), t->beats_per_minute(), t->note_type(), t->type());
1159                                 } else {
1160                                         ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
1161                                 }
1162                                 copy.push_back (ret);
1163                                 continue;
1164                         }
1165                         if (t->position_lock_style() == MusicTime) {
1166                                 copy.push_back (new TempoSection (t->beat(), t->beats_per_minute(), t->note_type(), t->type()));
1167                         } else {
1168                                 copy.push_back (new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type()));
1169                         }
1170                 }
1171                 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1172                         if (m->position_lock_style() == MusicTime) {
1173                                 copy.push_back (new MeterSection (m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor()));
1174                         } else {
1175                                 copy.push_back (new MeterSection (m->frame(), m->bbt(), m->divisions_per_bar(), m->note_divisor()));
1176
1177                         }
1178                 }
1179         }
1180         recompute_map (copy);
1181         return ret;
1182 }
1183
1184 bool
1185 TempoMap::can_solve_bbt (TempoSection* ts,  const Tempo& bpm, const BBT_Time& bbt)
1186 {
1187         Metrics copy;
1188         TempoSection* new_section = 0;
1189
1190         {
1191                 Glib::Threads::RWLock::ReaderLock lm (lock);
1192                 new_section = copy_metrics_and_point (copy, ts);
1193         }
1194
1195         double const beat = bbt_to_beats_locked (copy, bbt);
1196         bool ret = solve_map (copy, new_section, bpm, beat);
1197
1198         Metrics::const_iterator d = copy.begin();
1199         while (d != copy.end()) {
1200                 delete (*d);
1201                 ++d;
1202         }
1203
1204         return ret;
1205 }
1206
1207 void
1208 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1209 {
1210         Tempo newtempo (beats_per_minute, note_type);
1211         TempoSection* t;
1212
1213         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1214                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1215                         {
1216                                 Glib::Threads::RWLock::WriterLock lm (lock);
1217                                 *((Tempo*) t) = newtempo;
1218                                 recompute_map (_metrics);
1219                         }
1220                         PropertyChanged (PropertyChange ());
1221                         break;
1222                 }
1223         }
1224 }
1225
1226 void
1227 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1228 {
1229         Tempo newtempo (beats_per_minute, note_type);
1230
1231         TempoSection* prev;
1232         TempoSection* first;
1233         Metrics::iterator i;
1234
1235         /* find the TempoSection immediately preceding "where"
1236          */
1237
1238         for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1239
1240                 if ((*i)->frame() > where) {
1241                         break;
1242                 }
1243
1244                 TempoSection* t;
1245
1246                 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1247                         if (!first) {
1248                                 first = t;
1249                         }
1250                         prev = t;
1251                 }
1252         }
1253
1254         if (!prev) {
1255                 if (!first) {
1256                         error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1257                         return;
1258                 }
1259
1260                 prev = first;
1261         }
1262
1263         /* reset */
1264
1265         {
1266                 Glib::Threads::RWLock::WriterLock lm (lock);
1267                 /* cannot move the first tempo section */
1268                 *((Tempo*)prev) = newtempo;
1269                 recompute_map (_metrics);
1270         }
1271
1272         PropertyChanged (PropertyChange ());
1273 }
1274
1275 const MeterSection&
1276 TempoMap::first_meter () const
1277 {
1278         const MeterSection *m = 0;
1279
1280         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1281                 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1282                         return *m;
1283                 }
1284         }
1285
1286         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1287         abort(); /*NOTREACHED*/
1288         return *m;
1289 }
1290
1291 MeterSection&
1292 TempoMap::first_meter ()
1293 {
1294         MeterSection *m = 0;
1295
1296         /* CALLER MUST HOLD LOCK */
1297
1298         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1299                 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1300                         return *m;
1301                 }
1302         }
1303
1304         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1305         abort(); /*NOTREACHED*/
1306         return *m;
1307 }
1308
1309 const TempoSection&
1310 TempoMap::first_tempo () const
1311 {
1312         const TempoSection *t = 0;
1313
1314         /* CALLER MUST HOLD LOCK */
1315
1316         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1317                 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1318                         return *t;
1319                 }
1320         }
1321
1322         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1323         abort(); /*NOTREACHED*/
1324         return *t;
1325 }
1326
1327 TempoSection&
1328 TempoMap::first_tempo ()
1329 {
1330         TempoSection *t = 0;
1331
1332         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1333                 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1334                         return *t;
1335                 }
1336         }
1337
1338         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1339         abort(); /*NOTREACHED*/
1340         return *t;
1341 }
1342 void
1343 TempoMap::recompute_tempos (Metrics& metrics)
1344 {
1345         TempoSection* prev_ts = 0;
1346
1347         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1348                 TempoSection* t;
1349
1350                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1351                         if (prev_ts) {
1352                                 if (t->position_lock_style() == AudioTime) {
1353                                         prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->beats_per_minute(), t->frame(), _frame_rate));
1354                                         t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1355
1356                                 } else {
1357                                         prev_ts->set_c_func (prev_ts->compute_c_func_beat (t->beats_per_minute(), t->beat(), _frame_rate));
1358                                         t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
1359
1360                                 }
1361                         }
1362                         prev_ts = t;
1363                 }
1364         }
1365 }
1366
1367 /* tempos must be positioned correctly */
1368 void
1369 TempoMap::recompute_meters (Metrics& metrics)
1370 {
1371         MeterSection* meter = 0;
1372
1373         for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1374                 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1375                         if (meter->position_lock_style() == AudioTime) {
1376                                 /* a frame based meter has to have a 1|1|0 bbt */
1377                                 pair<double, BBT_Time> pr;
1378                                 BBT_Time const where (1, 1, 0);
1379
1380                                 pr.first = beat_at_frame_locked (metrics, meter->frame());
1381                                 pr.second = where;
1382                                 meter->set_beat (pr);
1383                         } else {
1384                                 meter->set_frame (frame_at_tick_locked (metrics, meter->tick()));
1385                         }
1386                 }
1387         }
1388 }
1389
1390 void
1391 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1392 {
1393         /* CALLER MUST HOLD WRITE LOCK */
1394
1395         if (end < 0) {
1396
1397                 /* we will actually stop once we hit
1398                    the last metric.
1399                 */
1400                 end = max_framepos;
1401
1402         }
1403
1404         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1405
1406         if (end == 0) {
1407                 /* silly call from Session::process() during startup
1408                  */
1409                 return;
1410         }
1411
1412         recompute_tempos (metrics);
1413         recompute_meters (metrics);
1414 }
1415
1416
1417 TempoMetric
1418 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1419 {
1420         Glib::Threads::RWLock::ReaderLock lm (lock);
1421         TempoMetric m (first_meter(), first_tempo());
1422
1423         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1424            at something, because we insert the default tempo and meter during
1425            TempoMap construction.
1426
1427            now see if we can find better candidates.
1428         */
1429
1430         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1431
1432                 if ((*i)->frame() > frame) {
1433                         break;
1434                 }
1435
1436                 m.set_metric(*i);
1437
1438                 if (last) {
1439                         *last = i;
1440                 }
1441         }
1442
1443         return m;
1444 }
1445 /* XX meters only */
1446 TempoMetric
1447 TempoMap::metric_at (BBT_Time bbt) const
1448 {
1449         Glib::Threads::RWLock::ReaderLock lm (lock);
1450         TempoMetric m (first_meter(), first_tempo());
1451
1452         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1453            at something, because we insert the default tempo and meter during
1454            TempoMap construction.
1455
1456            now see if we can find better candidates.
1457         */
1458
1459         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1460                 MeterSection* mw;
1461                 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1462                         BBT_Time section_start (mw->bbt());
1463
1464                         if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1465                                 break;
1466                         }
1467
1468                         m.set_metric (*i);
1469                 }
1470         }
1471
1472         return m;
1473 }
1474
1475 void
1476 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1477 {
1478         Glib::Threads::RWLock::ReaderLock lm (lock);
1479
1480         if (frame < 0) {
1481                 bbt.bars = 1;
1482                 bbt.beats = 1;
1483                 bbt.ticks = 0;
1484                 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1485                 return;
1486         }
1487         bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, frame));
1488 }
1489
1490 double
1491 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1492 {
1493         Glib::Threads::RWLock::ReaderLock lm (lock);
1494         return bbt_to_beats_locked (_metrics, bbt);
1495 }
1496
1497 double
1498 TempoMap::bbt_to_beats_locked (Metrics& metrics, Timecode::BBT_Time bbt)
1499 {
1500         /* CALLER HOLDS READ LOCK */
1501
1502         double accumulated_beats = 0.0;
1503         double accumulated_bars = 0.0;
1504         double bars_offset = 0.0;
1505         MeterSection* prev_ms = 0;
1506
1507         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1508                 MeterSection* m;
1509                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1510                         double bars_to_m = 0.0;
1511                         if (prev_ms) {
1512                                 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1513                         }
1514                         if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1515                                 break;
1516                         }
1517                         if (prev_ms) {
1518                                 if (m->position_lock_style() == AudioTime) {
1519                                         accumulated_beats = 0.0;
1520                                         accumulated_bars = 0;
1521                                         bars_offset += bars_to_m;
1522                                 } else {
1523                                         accumulated_beats += m->beat() - prev_ms->beat();
1524                                         accumulated_bars += bars_to_m;
1525                                 }
1526                         }
1527                         prev_ms = m;
1528                 }
1529         }
1530
1531         double const remaining_bars = ((bbt.bars - bars_offset) - 1) - accumulated_bars;
1532         double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1533         double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1534
1535         return ret;
1536 }
1537
1538 Timecode::BBT_Time
1539 TempoMap::beats_to_bbt (double beats)
1540 {
1541         Glib::Threads::RWLock::ReaderLock lm (lock);
1542         return beats_to_bbt_locked (_metrics, beats);
1543 }
1544
1545 Timecode::BBT_Time
1546 TempoMap::beats_to_bbt_locked (Metrics& metrics, double beats)
1547 {
1548         /* CALLER HOLDS READ LOCK */
1549
1550         MeterSection* prev_ms = 0;
1551         uint32_t accumulated_bars = 0;
1552
1553         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1554                 MeterSection* m = 0;
1555
1556                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1557
1558                         if (prev_ms) {
1559                                 if (m->beat() > beats) {
1560                                         /* this is the meter after the one our beat is on*/
1561                                         break;
1562                                 }
1563                                 if(m->position_lock_style() == AudioTime) {
1564                                         accumulated_bars = 0;
1565                                 } else {
1566                                         /* we need a whole number of bars. */
1567                                         accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
1568                                 }
1569                         }
1570
1571                         prev_ms = m;
1572                 }
1573         }
1574
1575         double const beats_in_ms = beats - prev_ms->beat();
1576         uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1577         uint32_t const total_bars = bars_in_ms + accumulated_bars;
1578         double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1579         double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1580
1581         BBT_Time ret;
1582
1583         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1584         ret.beats = (uint32_t) floor (remaining_beats);
1585         ret.bars = total_bars;
1586
1587         /* 0 0 0 to 1 1 0 - based mapping*/
1588         ++ret.bars;
1589         ++ret.beats;
1590
1591         if (ret.ticks >= BBT_Time::ticks_per_beat) {
1592                 ++ret.beats;
1593                 ret.ticks -= BBT_Time::ticks_per_beat;
1594         }
1595
1596         if (ret.beats > prev_ms->divisions_per_bar()) {
1597                 ++ret.bars;
1598                 ret.beats = 1;
1599         }
1600
1601         return ret;
1602 }
1603
1604 double
1605 TempoMap::tick_at_frame_locked (const Metrics& metrics, framecnt_t frame) const
1606 {
1607         /* HOLD (at least) THE READER LOCK */
1608
1609         TempoSection* prev_ts = 0;
1610         double accumulated_ticks = 0.0;
1611
1612         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1613                 TempoSection* t;
1614                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1615                         if ((prev_ts) && frame < t->frame()) {
1616                                 /*the previous ts is the one containing the frame */
1617                                 return prev_ts->tick_at_frame (frame, _frame_rate);
1618                         }
1619
1620                         accumulated_ticks = t->tick();
1621                         prev_ts = t;
1622                 }
1623         }
1624
1625         /* treated as constant for this ts */
1626         double const ticks_in_section = ((frame - prev_ts->frame()) / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1627
1628         return ticks_in_section + accumulated_ticks;
1629
1630 }
1631
1632 framecnt_t
1633 TempoMap::frame_at_tick_locked (const Metrics& metrics, double tick) const
1634 {
1635         /* HOLD THE READER LOCK */
1636
1637         const TempoSection* prev_ts = 0;
1638         double accumulated_ticks = 0.0;
1639
1640         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1641                 TempoSection* t;
1642                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1643                         if (prev_ts && tick < t->tick()) {
1644                                 /* prev_ts is the one affecting us. */
1645                                 return prev_ts->frame_at_tick (tick, _frame_rate);
1646                         }
1647
1648                         accumulated_ticks = t->tick();
1649                         prev_ts = t;
1650                 }
1651         }
1652         /* must be treated as constant, irrespective of _type */
1653         double const ticks_in_section = tick - accumulated_ticks;
1654         double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
1655
1656         framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1657
1658         return ret;
1659 }
1660
1661 double
1662 TempoMap::beat_at_frame (framecnt_t frame) const
1663 {
1664         Glib::Threads::RWLock::ReaderLock lm (lock);
1665         return tick_at_frame_locked (_metrics, frame) / BBT_Time::ticks_per_beat;
1666 }
1667
1668 double
1669 TempoMap::beat_at_frame_locked (Metrics& metrics, framecnt_t frame) const
1670 {
1671         return tick_at_frame_locked (metrics, frame) / BBT_Time::ticks_per_beat;
1672 }
1673
1674 framecnt_t
1675 TempoMap::frame_at_beat (double beat) const
1676 {
1677         Glib::Threads::RWLock::ReaderLock lm (lock);
1678         return frame_at_tick_locked (_metrics, beat * BBT_Time::ticks_per_beat);
1679 }
1680
1681 framecnt_t
1682 TempoMap::frame_at_beat_locked (Metrics& metrics, double beat) const
1683 {
1684
1685         return frame_at_tick_locked (metrics, beat * BBT_Time::ticks_per_beat);
1686 }
1687
1688 framepos_t
1689 TempoMap::frame_time (const BBT_Time& bbt)
1690 {
1691         if (bbt.bars < 1) {
1692                 warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
1693                 return 0;
1694         }
1695
1696         if (bbt.beats < 1) {
1697                 throw std::logic_error ("beats are counted from one");
1698         }
1699         Glib::Threads::RWLock::ReaderLock lm (lock);
1700
1701         return frame_time_locked (_metrics, bbt);;
1702 }
1703
1704 framepos_t
1705 TempoMap::frame_time_locked (Metrics& metrics, const BBT_Time& bbt)
1706 {
1707         /* HOLD THE READER LOCK */
1708
1709         framepos_t const ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1710
1711         return ret;
1712 }
1713
1714 bool
1715 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1716 {
1717         TempoSection* prev_ts = 0;
1718
1719         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1720                 TempoSection* t;
1721                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1722                         if (prev_ts) {
1723                                 if ((by_frame && t->frame() < prev_ts->frame()) || (!by_frame && t->beat() < prev_ts->beat())) {
1724                                         return false;
1725                                 }
1726                                 if (by_frame && t->frame() != prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate)) {
1727                                         return false;
1728                                 }
1729                                 if (!by_frame && fabs (t->beat() - prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate)) > 0.0000005) {
1730                                         std::cerr << "beat precision too low for bpm: " << t->beats_per_minute() << std::endl <<
1731                                                 " |error          :" << t->beat() - prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate) << std::endl <<
1732                                                 "|frame at beat   :" << prev_ts->frame_at_beat (t->beat(), _frame_rate) << std::endl <<
1733                                                 " |frame at tempo : " << prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate) << std::endl;
1734                                         return false;
1735                                 }
1736                         }
1737                         prev_ts = t;
1738                 }
1739         }
1740
1741         return true;
1742 }
1743
1744 bool
1745 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const framepos_t& frame)
1746 {
1747         TempoSection* prev_ts = 0;
1748         MetricSectionFrameSorter fcmp;
1749         MetricSectionSorter cmp;
1750
1751         section->set_frame (frame);
1752
1753         imaginary.sort (fcmp);
1754         if (section->position_lock_style() == MusicTime) {
1755                 /* we're setting the frame */
1756                 section->set_position_lock_style (AudioTime);
1757                 recompute_map (imaginary);
1758                 section->set_position_lock_style (MusicTime);
1759         } else {
1760                 recompute_map (imaginary);
1761         }
1762         if (check_solved (imaginary, true)) {
1763                 return true;
1764         }
1765
1766         imaginary.sort (cmp);
1767         if (section->position_lock_style() == MusicTime) {
1768                 /* we're setting the frame */
1769                 section->set_position_lock_style (AudioTime);
1770                 recompute_map (imaginary);
1771                 section->set_position_lock_style (MusicTime);
1772         } else {
1773                 recompute_map (imaginary);
1774         }
1775         if (check_solved (imaginary, true)) {
1776                 return true;
1777         }
1778
1779         /*
1780         prev_ts = 0;
1781         std::cerr << "dumping frame order ------" << std::endl;;
1782         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1783                 TempoSection* t;
1784
1785                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1786                         if (prev_ts) {
1787
1788                                 std::cerr << "current      : " << t->beats_per_minute() << " | " << t->beat() << " | " << t->frame() << std::endl;
1789                                 std::cerr << "previous     : " << prev_ts->beats_per_minute() << " | " << prev_ts->beat() << " | " << prev_ts->frame() << std::endl;
1790                                 std::cerr << "calculated   : " << prev_ts->tempo_at_beat(t->beat()) << " | " << prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate) <<  " | " << prev_ts->frame_at_tempo(t->beats_per_minute(), t->beat(), _frame_rate) << std::endl;
1791                                 std::cerr << "------" << std::endl;
1792
1793                         }
1794                         prev_ts = t;
1795                 }
1796         }
1797         std::cerr << "end dump ------" << std::endl;
1798         */
1799
1800         return false;
1801 }
1802
1803 bool
1804 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& beat)
1805 {
1806         MetricSectionSorter cmp;
1807         MetricSectionFrameSorter fcmp;
1808
1809         section->set_beat (beat);
1810
1811         imaginary.sort (cmp);
1812         if (section->position_lock_style() == AudioTime) {
1813                 /* we're setting the beat */
1814                 section->set_position_lock_style (MusicTime);
1815                 recompute_map (imaginary);
1816                 section->set_position_lock_style (AudioTime);
1817         } else {
1818                 recompute_map (imaginary);
1819         }
1820         if (check_solved (imaginary, false)) {
1821                 return true;
1822         }
1823
1824         imaginary.sort (fcmp);
1825         if (section->position_lock_style() == AudioTime) {
1826                 /* we're setting the beat */
1827                 section->set_position_lock_style (MusicTime);
1828                 recompute_map (imaginary);
1829                 section->set_position_lock_style (AudioTime);
1830         } else {
1831                 recompute_map (imaginary);
1832         }
1833         if (check_solved (imaginary, false)) {
1834                 return true;
1835         }
1836
1837         TempoSection* prev_ts = 0;
1838         std::cerr << "dumping beat order ------" << std::endl;
1839         std::cerr << "section    : " << section->beats_per_minute() << " | " << section->beat() << " | " << section->frame() << std::endl;
1840         std::cerr << "------------------------------------" << std::endl;
1841
1842         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1843                 TempoSection* t;
1844                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1845                         if (prev_ts) {
1846
1847                                 std::cerr << "current    : " << t->beats_per_minute() << " | " << t->beat() << " | " << t->frame() << std::endl;
1848                                 std::cerr << "prev       : " << prev_ts->beats_per_minute() << " | " << prev_ts->beat() << " | " << prev_ts->frame() << std::endl;
1849                                 std::cerr << "calculated : " << prev_ts->tempo_at_beat(t->beat()) << " | " << prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate) << " | " << prev_ts->frame_at_tempo(t->beats_per_minute(), t->beat(), _frame_rate) << std::endl;
1850                                 std::cerr << "   ------" << std::endl;;
1851
1852                         }
1853                         prev_ts = t;
1854                 }
1855         }
1856         std::cerr << "end dump ------";
1857         prev_ts = 0;
1858
1859         return false;
1860 }
1861
1862 void
1863 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& beat)
1864 {
1865         MeterSection* prev_ms = 0;
1866
1867         pair<double, BBT_Time> b_bbt = make_pair (beat, BBT_Time (1, 1, 0));
1868         section->set_beat (b_bbt);
1869         MetricSectionSorter cmp;
1870         imaginary.sort (cmp);
1871
1872         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1873                 MeterSection* m;
1874                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1875                         if (prev_ms) {
1876                                 if (m->beat() > beat){
1877                                         section->set_frame (frame_at_beat_locked (imaginary, beat));
1878                                         prev_ms = section;
1879                                         continue;
1880                                 }
1881                                 if (m->position_lock_style() == MusicTime) {
1882                                         m->set_frame (frame_at_beat_locked (imaginary, m->beat()));
1883                                 } else {
1884                                         pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (imaginary, m->frame()), BBT_Time (1, 1, 0));
1885                                         m->set_beat (b_bbt);
1886                                 }
1887                         }
1888                         prev_ms = m;
1889                 }
1890         }
1891
1892         MetricSectionFrameSorter fcmp;
1893         //imaginary.sort (fcmp);
1894
1895         if (section->position_lock_style() == AudioTime) {
1896                 /* we're setting the beat */
1897                 section->set_position_lock_style (MusicTime);
1898                 recompute_meters (imaginary);
1899                 section->set_position_lock_style (AudioTime);
1900         } else {
1901                 recompute_meters (imaginary);
1902         }
1903
1904 }
1905
1906 void
1907 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
1908 {
1909         MeterSection* prev_ms = 0;
1910
1911         section->set_frame (frame);
1912         MetricSectionFrameSorter fcmp;
1913         imaginary.sort (fcmp);
1914
1915         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1916                 MeterSection* m;
1917                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1918                         if (prev_ms) {
1919                                 if (m->frame() > frame){
1920                                         pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (imaginary, frame), BBT_Time (1, 1, 0));
1921                                         section->set_beat (b_bbt);
1922                                         prev_ms = section;
1923                                         continue;
1924                                 }
1925                                 if (m->position_lock_style() == MusicTime) {
1926                                         m->set_frame (frame_at_beat_locked (imaginary, m->beat()));
1927                                 } else {
1928                                         pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (imaginary, m->frame()), BBT_Time (1, 1, 0));
1929                                         m->set_beat (b_bbt);
1930                                 }
1931                         }
1932                         prev_ms = m;
1933                 }
1934         }
1935
1936         MetricSectionSorter cmp;
1937         imaginary.sort (cmp);
1938
1939         if (section->position_lock_style() == MusicTime) {
1940                 /* we're setting the frame */
1941                 section->set_position_lock_style (AudioTime);
1942                 recompute_map (imaginary);
1943                 section->set_position_lock_style (MusicTime);
1944         } else {
1945                 recompute_map (imaginary);
1946         }
1947 }
1948
1949 framecnt_t
1950 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1951 {
1952         Glib::Threads::RWLock::ReaderLock lm (lock);
1953
1954         Metrics::const_iterator i;
1955         TempoSection* first = 0;
1956         TempoSection* second = 0;
1957
1958         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1959                 TempoSection* t;
1960
1961                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1962
1963                         if ((*i)->frame() > pos) {
1964                                 second = t;
1965                                 break;
1966                         }
1967
1968                         first = t;
1969                 }
1970         }
1971         if (first && second) {
1972                 double const tick_at_time = first->tick_at_frame (pos, _frame_rate);
1973                 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1974                 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, _frame_rate);
1975
1976                 return time_at_bbt - pos;
1977         }
1978         double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1979
1980         return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1981 }
1982
1983 framepos_t
1984 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1985 {
1986         return round_to_type (fr, dir, Bar);
1987 }
1988
1989 framepos_t
1990 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1991 {
1992         return round_to_type (fr, dir, Beat);
1993 }
1994
1995 framepos_t
1996 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1997 {
1998         Glib::Threads::RWLock::ReaderLock lm (lock);
1999
2000         uint32_t ticks = (uint32_t) floor (tick_at_frame_locked (_metrics, fr) + 0.5);
2001         uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2002         uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
2003
2004         ticks -= beats * BBT_Time::ticks_per_beat;
2005
2006         if (dir > 0) {
2007                 /* round to next (or same iff dir == RoundUpMaybe) */
2008
2009                 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2010
2011                 if (mod == 0 && dir == RoundUpMaybe) {
2012                         /* right on the subdivision, which is fine, so do nothing */
2013
2014                 } else if (mod == 0) {
2015                         /* right on the subdivision, so the difference is just the subdivision ticks */
2016                         ticks += ticks_one_subdivisions_worth;
2017
2018                 } else {
2019                         /* not on subdivision, compute distance to next subdivision */
2020
2021                         ticks += ticks_one_subdivisions_worth - mod;
2022                 }
2023
2024                 if (ticks >= BBT_Time::ticks_per_beat) {
2025                         ticks -= BBT_Time::ticks_per_beat;
2026                 }
2027         } else if (dir < 0) {
2028
2029                 /* round to previous (or same iff dir == RoundDownMaybe) */
2030
2031                 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2032
2033                 if (difference == 0 && dir == RoundDownAlways) {
2034                         /* right on the subdivision, but force-rounding down,
2035                            so the difference is just the subdivision ticks */
2036                         difference = ticks_one_subdivisions_worth;
2037                 }
2038
2039                 if (ticks < difference) {
2040                         ticks = BBT_Time::ticks_per_beat - ticks;
2041                 } else {
2042                         ticks -= difference;
2043                 }
2044
2045         } else {
2046                 /* round to nearest */
2047                 double rem;
2048
2049                 /* compute the distance to the previous and next subdivision */
2050
2051                 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2052
2053                         /* closer to the next subdivision, so shift forward */
2054
2055                         ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2056
2057                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2058
2059                         if (ticks > BBT_Time::ticks_per_beat) {
2060                                 ++beats;
2061                                 ticks -= BBT_Time::ticks_per_beat;
2062                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2063                         }
2064
2065                 } else if (rem > 0) {
2066
2067                         /* closer to previous subdivision, so shift backward */
2068
2069                         if (rem > ticks) {
2070                                 if (beats == 0) {
2071                                         /* can't go backwards past zero, so ... */
2072                                         return 0;
2073                                 }
2074                                 /* step back to previous beat */
2075                                 --beats;
2076                                 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2077                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2078                         } else {
2079                                 ticks = lrint (ticks - rem);
2080                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2081                         }
2082                 } else {
2083                         /* on the subdivision, do nothing */
2084                 }
2085         }
2086         return frame_at_tick_locked (_metrics, (beats * BBT_Time::ticks_per_beat) + ticks);
2087 }
2088
2089 framepos_t
2090 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2091 {
2092         Glib::Threads::RWLock::ReaderLock lm (lock);
2093
2094         double const beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2095
2096         BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2097
2098         switch (type) {
2099         case Bar:
2100                 if (dir < 0) {
2101                         /* find bar previous to 'frame' */
2102                         bbt.beats = 1;
2103                         bbt.ticks = 0;
2104                         return frame_time (bbt);
2105
2106                 } else if (dir > 0) {
2107                         /* find bar following 'frame' */
2108                         ++bbt.bars;
2109                         bbt.beats = 1;
2110                         bbt.ticks = 0;
2111                         return frame_time (bbt);
2112                 } else {
2113                         /* true rounding: find nearest bar */
2114                         framepos_t raw_ft = frame_time (bbt);
2115                         bbt.beats = 1;
2116                         bbt.ticks = 0;
2117                         framepos_t prev_ft = frame_time (bbt);
2118                         ++bbt.bars;
2119                         framepos_t next_ft = frame_time (bbt);
2120
2121                         if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) { 
2122                                 return next_ft;
2123                         } else {
2124                                 return prev_ft;
2125                         }
2126                 }
2127
2128                 break;
2129
2130         case Beat:
2131                 if (dir < 0) {
2132                         return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2133                 } else if (dir > 0) {
2134                         return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2135                 } else {
2136                         return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2137                 }
2138                 break;
2139         }
2140
2141         return 0;
2142 }
2143
2144 void
2145 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2146                     framepos_t lower, framepos_t upper)
2147 {
2148         Glib::Threads::RWLock::ReaderLock lm (lock);
2149         uint32_t const upper_beat = (uint32_t) floor (beat_at_frame_locked (_metrics, upper));
2150         uint32_t cnt = (uint32_t) ceil (beat_at_frame_locked (_metrics, lower));
2151
2152         while (cnt <= upper_beat) {
2153                 framecnt_t const pos = frame_at_beat_locked (_metrics, cnt);
2154                 MeterSection const meter = meter_section_at (pos);
2155
2156                 Tempo const tempo = tempo_at (pos);
2157                 BBT_Time const bbt = beats_to_bbt_locked (_metrics, (double) cnt);
2158
2159                 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
2160                 ++cnt;
2161         }
2162 }
2163
2164 const TempoSection&
2165 TempoMap::tempo_section_at (framepos_t frame) const
2166 {
2167         Glib::Threads::RWLock::ReaderLock lm (lock);
2168
2169         Metrics::const_iterator i;
2170         TempoSection* prev = 0;
2171
2172         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2173                 TempoSection* t;
2174
2175                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2176
2177                         if ((*i)->frame() > frame) {
2178                                 break;
2179                         }
2180
2181                         prev = t;
2182                 }
2183         }
2184
2185         if (prev == 0) {
2186                 fatal << endmsg;
2187                 abort(); /*NOTREACHED*/
2188         }
2189
2190         return *prev;
2191 }
2192
2193 /* don't use this to calculate length (the tempo is only correct for this frame).
2194    do that stuff based on the beat_at_frame and frame_at_beat api
2195 */
2196 double
2197 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
2198 {
2199         Glib::Threads::RWLock::ReaderLock lm (lock);
2200
2201         const TempoSection* ts_at = &tempo_section_at (frame);
2202         const TempoSection* ts_after = 0;
2203         Metrics::const_iterator i;
2204
2205         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2206                 TempoSection* t;
2207
2208                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2209
2210                         if ((*i)->frame() > frame) {
2211                                 ts_after = t;
2212                                 break;
2213                         }
2214                 }
2215         }
2216
2217         if (ts_after) {
2218                 return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2219         }
2220         /* must be treated as constant tempo */
2221         return ts_at->frames_per_beat (_frame_rate);
2222 }
2223
2224 const Tempo
2225 TempoMap::tempo_at (framepos_t frame) const
2226 {
2227         Glib::Threads::RWLock::ReaderLock lm (lock);
2228
2229         TempoMetric m (metric_at (frame));
2230         TempoSection* prev_ts = 0;
2231
2232         Metrics::const_iterator i;
2233
2234         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2235                 TempoSection* t;
2236                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2237                         if ((prev_ts) && t->frame() > frame) {
2238                                 /* this is the one past frame */
2239                                 double const ret = prev_ts->tempo_at_frame (frame, _frame_rate);
2240                                 Tempo const ret_tempo (ret, m.tempo().note_type ());
2241                                 return ret_tempo;
2242                         }
2243                         prev_ts = t;
2244                 }
2245         }
2246
2247         return m.tempo();
2248 }
2249
2250 const MeterSection&
2251 TempoMap::meter_section_at (framepos_t frame) const
2252 {
2253         Glib::Threads::RWLock::ReaderLock lm (lock);
2254
2255         Metrics::const_iterator i;
2256         MeterSection* prev = 0;
2257
2258         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2259                 MeterSection* t;
2260
2261                 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
2262
2263                         if ((*i)->frame() > frame) {
2264                                 break;
2265                         }
2266
2267                         prev = t;
2268                 }
2269         }
2270
2271         if (prev == 0) {
2272                 fatal << endmsg;
2273                 abort(); /*NOTREACHED*/
2274         }
2275
2276         return *prev;
2277 }
2278
2279 const MeterSection&
2280 TempoMap::meter_section_at (double beat) const
2281 {
2282         Glib::Threads::RWLock::ReaderLock lm (lock);
2283
2284         Metrics::const_iterator i;
2285         MeterSection* prev = 0;
2286
2287         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2288                 MeterSection* t;
2289
2290                 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
2291
2292                         if ((*i)->beat() > beat) {
2293                                 break;
2294                         }
2295
2296                         prev = t;
2297                 }
2298         }
2299
2300         if (prev == 0) {
2301                 fatal << endmsg;
2302                 abort(); /*NOTREACHED*/
2303         }
2304
2305         return *prev;
2306 }
2307
2308 const Meter&
2309 TempoMap::meter_at (framepos_t frame) const
2310 {
2311         TempoMetric m (metric_at (frame));
2312         return m.meter();
2313 }
2314
2315 XMLNode&
2316 TempoMap::get_state ()
2317 {
2318         Metrics::const_iterator i;
2319         XMLNode *root = new XMLNode ("TempoMap");
2320
2321         {
2322                 Glib::Threads::RWLock::ReaderLock lm (lock);
2323                 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2324                         root->add_child_nocopy ((*i)->get_state());
2325                 }
2326         }
2327
2328         return *root;
2329 }
2330
2331 int
2332 TempoMap::set_state (const XMLNode& node, int /*version*/)
2333 {
2334         {
2335                 Glib::Threads::RWLock::WriterLock lm (lock);
2336
2337                 XMLNodeList nlist;
2338                 XMLNodeConstIterator niter;
2339                 Metrics old_metrics (_metrics);
2340                 MeterSection* last_meter = 0;
2341                 _metrics.clear();
2342
2343                 nlist = node.children();
2344
2345                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2346                         XMLNode* child = *niter;
2347
2348                         if (child->name() == TempoSection::xml_state_node_name) {
2349
2350                                 try {
2351                                         TempoSection* ts = new TempoSection (*child);
2352                                         _metrics.push_back (ts);
2353
2354                                         if (ts->bar_offset() < 0.0) {
2355                                                 if (last_meter) {
2356                                                         //ts->update_bar_offset_from_bbt (*last_meter);
2357                                                 }
2358                                         }
2359                                 }
2360
2361                                 catch (failed_constructor& err){
2362                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2363                                         _metrics = old_metrics;
2364                                         break;
2365                                 }
2366
2367                         } else if (child->name() == MeterSection::xml_state_node_name) {
2368
2369                                 try {
2370                                         MeterSection* ms = new MeterSection (*child);
2371                                         _metrics.push_back (ms);
2372                                         last_meter = ms;
2373                                 }
2374
2375                                 catch (failed_constructor& err) {
2376                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2377                                         _metrics = old_metrics;
2378                                         break;
2379                                 }
2380                         }
2381                 }
2382
2383                 if (niter == nlist.end()) {
2384                         MetricSectionSorter cmp;
2385                         _metrics.sort (cmp);
2386                 }
2387                 /* check for legacy sessions where bbt was the base musical unit for tempo */
2388                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2389                         MeterSection* prev_ms;
2390                         TempoSection* prev_ts;
2391                         if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2392                                 if (prev_ms->beat() < 0.0) {
2393                                         /*XX we cannot possibly make this work??. */
2394                                         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());
2395                                         prev_ms->set_beat (start);
2396                                 }
2397                         } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2398                                 if (prev_ts->beat() < 0.0) {
2399                                         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);
2400                                         prev_ts->set_beat (start);
2401
2402                                 }
2403                         }
2404                 }
2405                 /* check for multiple tempo/meters at the same location, which
2406                    ardour2 somehow allowed.
2407                 */
2408
2409                 Metrics::iterator prev = _metrics.end();
2410                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2411                         if (prev != _metrics.end()) {
2412                                 MeterSection* ms;
2413                                 MeterSection* prev_ms;
2414                                 TempoSection* ts;
2415                                 TempoSection* prev_ts;
2416                                 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2417                                         if (prev_ms->beat() == ms->beat()) {
2418                                                 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2419                                                 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2420                                                 return -1;
2421                                         }
2422                                 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2423                                         if (prev_ts->beat() == ts->beat()) {
2424                                                 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2425                                                 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2426                                                 return -1;
2427                                         }
2428                                 }
2429                         }
2430                         prev = i;
2431                 }
2432
2433                 recompute_map (_metrics);
2434         }
2435
2436         PropertyChanged (PropertyChange ());
2437
2438         return 0;
2439 }
2440
2441 void
2442 TempoMap::dump (std::ostream& o) const
2443 {
2444         Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2445         const MeterSection* m;
2446         const TempoSection* t;
2447
2448         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2449
2450                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2451                         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? "
2452                           << t->movable() << ')' << endl;
2453                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2454                         o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2455                           << " (movable? " << m->movable() << ')' << endl;
2456                 }
2457         }
2458 }
2459
2460 int
2461 TempoMap::n_tempos() const
2462 {
2463         Glib::Threads::RWLock::ReaderLock lm (lock);
2464         int cnt = 0;
2465
2466         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2467                 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2468                         cnt++;
2469                 }
2470         }
2471
2472         return cnt;
2473 }
2474
2475 int
2476 TempoMap::n_meters() const
2477 {
2478         Glib::Threads::RWLock::ReaderLock lm (lock);
2479         int cnt = 0;
2480
2481         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2482                 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2483                         cnt++;
2484                 }
2485         }
2486
2487         return cnt;
2488 }
2489
2490 void
2491 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2492 {
2493         {
2494                 Glib::Threads::RWLock::WriterLock lm (lock);
2495                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2496                         if ((*i)->frame() >= where && (*i)->movable ()) {
2497                                 (*i)->set_frame ((*i)->frame() + amount);
2498                         }
2499                 }
2500
2501                 /* now reset the BBT time of all metrics, based on their new
2502                  * audio time. This is the only place where we do this reverse
2503                  * timestamp.
2504                  */
2505
2506                 Metrics::iterator i;
2507                 const MeterSection* meter;
2508                 const TempoSection* tempo;
2509                 MeterSection *m;
2510                 TempoSection *t;
2511
2512                 meter = &first_meter ();
2513                 tempo = &first_tempo ();
2514
2515                 BBT_Time start;
2516                 BBT_Time end;
2517
2518                 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2519
2520                 bool first = true;
2521                 MetricSection* prev = 0;
2522
2523                 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2524
2525                         BBT_Time bbt;
2526                         //TempoMetric metric (*meter, *tempo);
2527                         MeterSection* ms = const_cast<MeterSection*>(meter);
2528                         TempoSection* ts = const_cast<TempoSection*>(tempo);
2529                         if (prev) {
2530                                 if (ts){
2531                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2532                                                 ts->set_beat (t->beat());
2533                                         }
2534                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2535                                                 ts->set_beat (m->beat());
2536                                         }
2537                                         ts->set_frame (prev->frame());
2538
2539                                 }
2540                                 if (ms) {
2541                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2542                                                 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
2543                                                 ms->set_beat (start);
2544                                         }
2545                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2546                                                 pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_locked (_metrics, t->beat()));
2547                                                 ms->set_beat (start);
2548                                         }
2549                                         ms->set_frame (prev->frame());
2550                                 }
2551
2552                         } else {
2553                                 // metric will be at frames=0 bbt=1|1|0 by default
2554                                 // which is correct for our purpose
2555                         }
2556
2557                         // cerr << bbt << endl;
2558
2559                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2560                                 t->set_beat (beat_at_frame_locked (_metrics, m->frame()));
2561                                 tempo = t;
2562                                 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2563                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2564                                 bbt_time (m->frame(), bbt);
2565
2566                                 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2567
2568                                 if (first) {
2569                                         first = false;
2570                                 } else {
2571
2572                                         if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2573                                                 /* round up to next beat */
2574                                                 bbt.beats += 1;
2575                                         }
2576
2577                                         bbt.ticks = 0;
2578
2579                                         if (bbt.beats != 1) {
2580                                                 /* round up to next bar */
2581                                                 bbt.bars += 1;
2582                                                 bbt.beats = 1;
2583                                         }
2584                                 }
2585                                 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
2586                                 m->set_beat (start);
2587                                 meter = m;
2588                                 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2589                         } else {
2590                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2591                                 abort(); /*NOTREACHED*/
2592                         }
2593
2594                         prev = (*i);
2595                 }
2596
2597                 recompute_map (_metrics);
2598         }
2599
2600
2601         PropertyChanged (PropertyChange ());
2602 }
2603 bool
2604 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2605 {
2606         bool moved = false;
2607
2608         std::list<MetricSection*> metric_kill_list;
2609
2610         TempoSection* last_tempo = NULL;
2611         MeterSection* last_meter = NULL;
2612         bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2613         bool meter_after = false; // is there a meter marker likewise?
2614         {
2615                 Glib::Threads::RWLock::WriterLock lm (lock);
2616                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2617                         if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2618                                 metric_kill_list.push_back(*i);
2619                                 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2620                                 if (lt)
2621                                         last_tempo = lt;
2622                                 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2623                                 if (lm)
2624                                         last_meter = lm;
2625                         }
2626                         else if ((*i)->frame() >= where) {
2627                                 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2628                                 (*i)->set_frame ((*i)->frame() - amount);
2629                                 if ((*i)->frame() == where) {
2630                                         // marker was immediately after end of range
2631                                         tempo_after = dynamic_cast<TempoSection*> (*i);
2632                                         meter_after = dynamic_cast<MeterSection*> (*i);
2633                                 }
2634                                 moved = true;
2635                         }
2636                 }
2637
2638                 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2639                 if (last_tempo && !tempo_after) {
2640                         metric_kill_list.remove(last_tempo);
2641                         last_tempo->set_frame(where);
2642                         moved = true;
2643                 }
2644                 if (last_meter && !meter_after) {
2645                         metric_kill_list.remove(last_meter);
2646                         last_meter->set_frame(where);
2647                         moved = true;
2648                 }
2649
2650                 //remove all the remaining metrics
2651                 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2652                         _metrics.remove(*i);
2653                         moved = true;
2654                 }
2655
2656                 if (moved) {
2657                         recompute_map (_metrics);
2658                 }
2659         }
2660         PropertyChanged (PropertyChange ());
2661         return moved;
2662 }
2663
2664 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2665  *  pos can be -ve, if required.
2666  */
2667 framepos_t
2668 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2669 {
2670         return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2671 }
2672
2673 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2674 framepos_t
2675 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2676 {
2677         return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2678 }
2679
2680 /** Add the BBT interval op to pos and return the result */
2681 framepos_t
2682 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2683 {
2684         cerr << "framepos_plus_bbt - untested" << endl;
2685         Glib::Threads::RWLock::ReaderLock lm (lock);
2686
2687         Metrics::const_iterator i;
2688         const MeterSection* meter;
2689         const MeterSection* m;
2690         const TempoSection* tempo;
2691         const TempoSection* next_tempo = 0;
2692         const TempoSection* t;
2693         double frames_per_beat;
2694         framepos_t effective_pos = max (pos, (framepos_t) 0);
2695
2696         meter = &first_meter ();
2697         tempo = &first_tempo ();
2698
2699         assert (meter);
2700         assert (tempo);
2701
2702         /* find the starting metrics for tempo & meter */
2703
2704         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2705
2706                 if ((*i)->frame() > effective_pos) {
2707                         break;
2708                 }
2709
2710                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2711                         tempo = t;
2712                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2713                         meter = m;
2714                 }
2715         }
2716
2717         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2718                 if ((*i)->frame() > effective_pos) {
2719                         if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2720                                 next_tempo = t;
2721                         }
2722                         break;
2723                 }
2724         }
2725
2726         /* We now have:
2727
2728            meter -> the Meter for "pos"
2729            tempo -> the Tempo for "pos"
2730            next_tempo -> the Tempo after "pos" or 0
2731            i     -> for first new metric after "pos", possibly metrics.end()
2732         */
2733
2734         /* now comes the complicated part. we have to add one beat a time,
2735            checking for a new metric on every beat.
2736         */
2737
2738         uint64_t bars = 0;
2739         /* fpb is used for constant tempo */
2740         frames_per_beat = tempo->frames_per_beat (_frame_rate);
2741
2742         while (op.bars) {
2743
2744                 bars++;
2745                 op.bars--;
2746
2747                 /* check if we need to use a new metric section: has adding frames moved us
2748                    to or after the start of the next metric section? in which case, use it.
2749                 */
2750
2751                 if (i != _metrics.end()) {
2752                         if ((*i)->frame() <= pos) {
2753
2754                                 /* about to change tempo or meter, so add the
2755                                  * number of frames for the bars we've just
2756                                  * traversed before we change the
2757                                  * frames_per_beat value.
2758                                  */
2759
2760                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2761                                         next_tempo = t;
2762                                 }
2763
2764                                 if (next_tempo) {
2765                                         pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2766                                 } else {
2767                                         pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2768                                 }
2769
2770                                 bars = 0;
2771
2772                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2773                                         tempo = t;
2774                                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2775                                         meter = m;
2776                                 }
2777                                 ++i;
2778                                 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2779                         }
2780                 }
2781
2782         }
2783
2784         if (next_tempo) {
2785                 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2786         } else {
2787                 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2788         }
2789
2790         uint64_t beats = 0;
2791
2792         while (op.beats) {
2793
2794                 /* given the current meter, have we gone past the end of the bar ? */
2795
2796                 beats++;
2797                 op.beats--;
2798
2799                 /* check if we need to use a new metric section: has adding frames moved us
2800                    to or after the start of the next metric section? in which case, use it.
2801                 */
2802
2803                 if (i != _metrics.end()) {
2804                         if ((*i)->frame() <= pos) {
2805
2806                                 /* about to change tempo or meter, so add the
2807                                  * number of frames for the beats we've just
2808                                  * traversed before we change the
2809                                  * frames_per_beat value.
2810                                  */
2811
2812                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2813                                         next_tempo = t;
2814                                 }
2815
2816                                 if (next_tempo) {
2817                                         pos += tempo->frame_at_beat (beats, _frame_rate);
2818                                 } else {
2819                                         pos += llrint (beats * frames_per_beat);
2820                                 }
2821
2822                                 beats = 0;
2823
2824                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2825                                         tempo = t;
2826                                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2827                                         meter = m;
2828                                 }
2829                                 ++i;
2830                                 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2831                         }
2832                 }
2833         }
2834
2835         if (next_tempo) {
2836                 pos += tempo->frame_at_beat (beats, _frame_rate);
2837         } else {
2838                 pos += llrint (beats * frames_per_beat);
2839         }
2840
2841         if (op.ticks) {
2842                 pos += tempo->frame_at_tick (op.ticks, _frame_rate);
2843         }
2844
2845         return pos;
2846
2847 }
2848
2849 /** Count the number of beats that are equivalent to distance when going forward,
2850     starting at pos.
2851 */
2852 Evoral::Beats
2853 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2854 {
2855         return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2856 }
2857
2858 struct bbtcmp {
2859     bool operator() (const BBT_Time& a, const BBT_Time& b) {
2860             return a < b;
2861     }
2862 };
2863
2864 std::ostream&
2865 operator<< (std::ostream& o, const Meter& m) {
2866         return o << m.divisions_per_bar() << '/' << m.note_divisor();
2867 }
2868
2869 std::ostream&
2870 operator<< (std::ostream& o, const Tempo& t) {
2871         return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2872 }
2873
2874 std::ostream&
2875 operator<< (std::ostream& o, const MetricSection& section) {
2876
2877         o << "MetricSection @ " << section.frame() << ' ';
2878
2879         const TempoSection* ts;
2880         const MeterSection* ms;
2881
2882         if ((ts = dynamic_cast<const TempoSection*> (&section)) != 0) {
2883                 o << *((const Tempo*) ts);
2884         } else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
2885                 //o << *((const Meter*) ms);
2886         }
2887
2888         return o;
2889 }