Tempo ramps - fix ordering glitches when dragging tempos.
[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 /* set this ramp's function constant using the end tempo and duration (beats into global start) of some later tempo section*/
378 void
379 TempoSection::set_c_func_from_tempo_and_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         _c_func = 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 (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 (true);
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 (true);
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->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 (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 /**
871 * This is for a gui that needs to know the frame of a beat if a tempo section were to be moved or altered.
872 * It actually alters tha ramps up to the beat parameter, so calling this commits you to replacing the section immediately.
873 * It will not emit a signal or recompute the map, as you probably want to replace the tempo or do somethig else before that happens.
874 * @param section - the section you want to alter
875 * @param bpm - the new tempo
876 * @param beat - the beat where the altered tempo will fall
877 * @return returns - the position in frames where the new tempo section will lie
878 */
879 framepos_t
880 TempoMap::compute_replacement_tempo_section (TempoSection* section, const double& bpm, const double& beat)
881 {
882         TempoSection* prev_ts = 0;
883         TempoSection* t;
884         framepos_t ret = 0;
885         MetricSectionSorter cmp;
886         {
887         Glib::Threads::RWLock::WriterLock lm (lock);
888
889         for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
890                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
891                         if (prev_ts) {
892                                 if (section->beat() == t->beat()) {
893                                         continue;
894                                 }
895                                 if (beat < t->beat()){
896                                         prev_ts->set_c_func_from_tempo_and_beat (bpm, beat, _frame_rate);
897                                         section->set_beat (beat);
898                                         section->set_frame (prev_ts->frame_at_beat (beat, _frame_rate));
899                                         break;
900                                 }
901
902                                 if (t->position_lock_style() == MusicTime) {
903                                         prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
904                                         t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
905                                 } else {
906                                         prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
907                                         t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
908                                 }
909                         }
910                         prev_ts = t;
911                 }
912         }
913         /* now we do the whole thing again because audio-locked sections will have caused a re-order */
914         prev_ts = 0;
915         metrics.sort (cmp);
916
917         for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
918                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
919                         if (prev_ts) {
920                                 if (section->beat() == t->beat()) {
921                                         continue;
922                                 }
923                                 if (beat < t->beat()){
924                                         prev_ts->set_c_func_from_tempo_and_beat (bpm, beat, _frame_rate);
925                                         ret = prev_ts->frame_at_beat (beat, _frame_rate);
926                                         section->set_frame (ret);
927                                         prev_ts = section;
928                                         break;
929                                 }
930                                 if (t->position_lock_style() == MusicTime) {
931                                         prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
932                                         t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
933                                 } else {
934                                         prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
935                                         t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
936                                 }
937                         }
938                         prev_ts = t;
939                 }
940         }
941
942         if (!ret) {
943                 prev_ts->set_c_func_from_tempo_and_beat (bpm, beat, _frame_rate);
944                 section->set_beat (beat);
945                 section->set_frame (prev_ts->frame_at_beat (beat, _frame_rate));
946
947                 ret = prev_ts->frame_at_beat (beat, _frame_rate);
948         }
949         }
950         return ret;
951 }
952
953 void
954 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
955 {
956         {
957                 Glib::Threads::RWLock::WriterLock lm (lock);
958                 TempoSection& first (first_tempo());
959                 if (ts.beat() != first.beat()) {
960                         remove_tempo_locked (ts);
961                         add_tempo_locked (tempo, where, true, type);
962                 } else {
963                         first.set_type (type);
964                         {
965                                 /* cannot move the first tempo section */
966                                 *static_cast<Tempo*>(&first) = tempo;
967                                 recompute_map (false);
968                         }
969                 }
970         }
971
972         PropertyChanged (PropertyChange ());
973 }
974
975 void
976 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
977 {
978         {
979                 Glib::Threads::RWLock::WriterLock lm (lock);
980                 TempoSection& first (first_tempo());
981                 if (ts.beat() != first.beat()) {
982                         remove_tempo_locked (ts);
983                         add_tempo_locked (tempo, frame, true, type);
984                 } else {
985                         first.set_type (type);
986                         {
987                                 /* cannot move the first tempo section */
988                                 *static_cast<Tempo*>(&first) = tempo;
989                                 recompute_map (false);
990                         }
991                 }
992         }
993
994         PropertyChanged (PropertyChange ());
995 }
996
997 Metrics
998 TempoMap::imagine_new_order (TempoSection* section, const Tempo& bpm, const framepos_t& frame, const double& beat)
999 {
1000         Metrics imaginary (metrics);
1001
1002         TempoSection* prev_ts = 0;
1003         TempoSection* t;
1004
1005         /*set frame and sort */
1006         section->set_frame (frame);
1007         MetricSectionFrameSorter fcmp;
1008         imaginary.sort (fcmp);
1009
1010         /* recompute */
1011         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1012                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1013                         if (prev_ts) {
1014                                 if (t == section) {
1015                                         /* we have already set the frame - set the beat */
1016                                         prev_ts->set_c_func (prev_ts->compute_c_func (bpm.beats_per_minute(), frame, _frame_rate));
1017                                         //section->set_beat (prev_ts->beat_at_frame (frame, _frame_rate));
1018                                         section->set_beat (prev_ts->beat_at_tempo (bpm.beats_per_minute(), frame, _frame_rate));
1019                                         prev_ts = t;
1020                                         continue;
1021                                 }
1022                                 if (t->position_lock_style() == MusicTime) {
1023                                         prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
1024                                         //t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
1025                                         t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
1026                                 } else {
1027                                         prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
1028                                         //t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
1029                                         t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1030                                 }
1031                         }
1032                         prev_ts = t;
1033                 }
1034         }
1035
1036         MetricSectionSorter cmp;
1037         imaginary.sort (cmp);
1038
1039         /* to do - check precision using _at_tempo() methods */
1040 /*
1041         prev_ts = 0;
1042         std::cerr << "dumping imaginary order ------" << std::endl;;
1043         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1044                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1045                         if (prev_ts) {
1046
1047                                 std::cerr << t->beats_per_minute() << " | " << t->beat() << " | " << t->frame() << std::endl;
1048                                 std::cerr << prev_ts->beats_per_minute() << " | " << prev_ts->beat() << " | " << prev_ts->frame() << std::endl;
1049                                 std::cerr << t->beats_per_minute() << " | " << prev_ts->beat_at_tempo (t->beats_per_minute()) << " | " << prev_ts->tempo_at_beat(t->beat()) << " | " << prev_ts->frame_at_tempo(t->beats_per_minute(), _frame_rate) << std::endl;
1050                                 std::cerr << "   ------" << std::endl;;
1051
1052                         }
1053                         prev_ts = t;
1054                 }
1055         }
1056         std::cerr << "end dump ------";
1057 */
1058         return imaginary;
1059 }
1060
1061 Metrics
1062 TempoMap::imagine_new_order(MeterSection* section, const Meter& mt, const framepos_t& frame, const double& beat)
1063 {
1064         /* incomplete */
1065         Metrics imaginary (metrics);
1066
1067         MeterSection* prev_ms = 0;
1068         MeterSection* m;
1069         MeterSection* our_section = 0;
1070         framepos_t ret = 0;
1071         MetricSectionSorter cmp;
1072
1073         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1074                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1075                         if (prev_ms) {
1076                                 if (section->beat() == m->beat()) {
1077                                         our_section = m;
1078                                         continue;
1079                                 }
1080                                 if (beat < m->beat()){
1081                                         pair<double, BBT_Time> b_bbt = make_pair (beat, BBT_Time (1, 1, 0));
1082                                         our_section->set_beat (b_bbt);
1083                                         our_section->set_frame (frame_at_beat_locked (beat));
1084                                         break;
1085                                 }
1086
1087                                 if (m->position_lock_style() == MusicTime) {
1088                                         m->set_frame (frame_at_beat_locked (m->beat()));
1089                                 } else {
1090                                         pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (m->frame()), BBT_Time (1, 1, 0));
1091                                         m->set_beat (b_bbt);
1092                                 }
1093                         }
1094                         prev_ms = m;
1095                 }
1096         }
1097         /* now we do the whole thing again because audio-locked sections will have caused a re-order */
1098         prev_ms = 0;
1099         metrics.sort (cmp);
1100
1101         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1102                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1103                         if (prev_ms) {
1104                                 if (section->beat() == m->beat()) {
1105                                         continue;
1106                                 }
1107                                 if (beat < m->beat()){
1108                                         ret = frame_at_beat (beat);
1109                                         section->set_frame (ret);
1110                                         prev_ms = section;
1111                                         break;
1112                                 }
1113                                 if (m->position_lock_style() == MusicTime) {
1114                                         m->set_frame (frame_at_beat (m->beat()));
1115                                 } else {
1116                                         pair<double, BBT_Time> b_bbt = make_pair (beat_at_frame_locked (m->frame()), BBT_Time (1, 1, 0));
1117                                         m->set_beat (b_bbt);
1118                                 }
1119                         }
1120                         prev_ms = m;
1121                 }
1122         }
1123
1124         if (!ret) {
1125                 pair<double, BBT_Time> b_bbt = make_pair (beat, BBT_Time (1, 1, 0));
1126                 section->set_beat (b_bbt);
1127                 section->set_frame (frame_at_beat_locked (beat));
1128         }
1129         return imaginary;
1130 }
1131
1132 void
1133 TempoMap::gui_move_tempo (TempoSection* ts,  const Tempo& bpm, const framepos_t& frame, const double& beat_where)
1134 {
1135         {
1136                 Glib::Threads::RWLock::WriterLock lm (lock);
1137                 Metrics new_order = imagine_new_order (ts, bpm, frame, beat_where);
1138                 metrics.clear();
1139                 metrics = new_order;
1140
1141                 recompute_map (false);
1142         }
1143
1144         MetricPositionChanged (); // Emit Signal
1145 }
1146
1147 void
1148 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t& frame, const double&  beat_where)
1149 {
1150         Glib::Threads::RWLock::WriterLock lm (lock);
1151         Metrics imaginary = imagine_new_order (ms, mt, frame, beat_where);
1152 }
1153
1154 void
1155 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
1156 {
1157         {
1158                 Glib::Threads::RWLock::WriterLock lm (lock);
1159                 add_tempo_locked (tempo, where, true, type);
1160         }
1161
1162
1163         PropertyChanged (PropertyChange ());
1164 }
1165
1166 void
1167 TempoMap::add_tempo (const Tempo& tempo, framepos_t frame, ARDOUR::TempoSection::Type type)
1168 {
1169         {
1170                 Glib::Threads::RWLock::WriterLock lm (lock);
1171                 add_tempo_locked (tempo, frame, true, type);
1172         }
1173
1174
1175         PropertyChanged (PropertyChange ());
1176 }
1177
1178 void
1179 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
1180 {
1181         TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
1182
1183         do_insert (ts);
1184
1185         if (recompute) {
1186                 recompute_map (false);
1187         }
1188 }
1189
1190 void
1191 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
1192 {
1193         TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
1194
1195         do_insert (ts);
1196
1197         if (recompute) {
1198                 recompute_map (false);
1199         }
1200 }
1201
1202 void
1203 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
1204 {
1205         {
1206                 Glib::Threads::RWLock::WriterLock lm (lock);
1207                 MeterSection& first (first_meter());
1208                 if (ms.beat() != first.beat()) {
1209                         remove_meter_locked (ms);
1210                         add_meter_locked (meter, bbt_to_beats_locked (where), where, true);
1211                 } else {
1212                         /* cannot move the first meter section */
1213                         *static_cast<Meter*>(&first) = meter;
1214                         recompute_map (true);
1215                 }
1216         }
1217
1218         PropertyChanged (PropertyChange ());
1219 }
1220
1221 void
1222 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
1223 {
1224         {
1225                 Glib::Threads::RWLock::WriterLock lm (lock);
1226                 MeterSection& first (first_meter());
1227                 if (ms.beat() != first.beat()) {
1228                         remove_meter_locked (ms);
1229                         add_meter_locked (meter, frame, true);
1230                 } else {
1231                         /* cannot move the first meter section */
1232                         *static_cast<Meter*>(&first) = meter;
1233                         recompute_map (true);
1234                 }
1235         }
1236
1237         PropertyChanged (PropertyChange ());
1238 }
1239
1240
1241 void
1242 TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
1243 {
1244         {
1245                 Glib::Threads::RWLock::WriterLock lm (lock);
1246                 add_meter_locked (meter, beat, where, true);
1247         }
1248
1249
1250 #ifndef NDEBUG
1251         if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1252                 dump (std::cerr);
1253         }
1254 #endif
1255
1256         PropertyChanged (PropertyChange ());
1257 }
1258
1259 void
1260 TempoMap::add_meter (const Meter& meter, framepos_t frame)
1261 {
1262         {
1263                 Glib::Threads::RWLock::WriterLock lm (lock);
1264                 add_meter_locked (meter, frame, true);
1265         }
1266
1267
1268 #ifndef NDEBUG
1269         if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1270                 dump (std::cerr);
1271         }
1272 #endif
1273
1274         PropertyChanged (PropertyChange ());
1275 }
1276
1277 void
1278 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1279 {
1280         /* a new meter always starts a new bar on the first beat. so
1281            round the start time appropriately. remember that
1282            `where' is based on the existing tempo map, not
1283            the result after we insert the new meter.
1284
1285         */
1286
1287         if (where.beats != 1) {
1288                 where.beats = 1;
1289                 where.bars++;
1290         }
1291
1292         /* new meters *always* start on a beat. */
1293         where.ticks = 0;
1294
1295         do_insert (new MeterSection (beat, where, meter.divisions_per_bar(), meter.note_divisor()));
1296
1297         if (recompute) {
1298                 recompute_map (true);
1299         }
1300
1301 }
1302
1303 void
1304 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, bool recompute)
1305 {
1306
1307         /* MusicTime meters always start on 1|1|0. */
1308         do_insert (new MeterSection (frame, meter.divisions_per_bar(), meter.note_divisor()));
1309
1310         if (recompute) {
1311                 recompute_map (true);
1312         }
1313
1314 }
1315
1316 void
1317 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1318 {
1319         Tempo newtempo (beats_per_minute, note_type);
1320         TempoSection* t;
1321
1322         for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1323                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1324                         {
1325                                 Glib::Threads::RWLock::WriterLock lm (lock);
1326                                 *((Tempo*) t) = newtempo;
1327                                 recompute_map (false);
1328                         }
1329                         PropertyChanged (PropertyChange ());
1330                         break;
1331                 }
1332         }
1333 }
1334
1335 void
1336 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1337 {
1338         Tempo newtempo (beats_per_minute, note_type);
1339
1340         TempoSection* prev;
1341         TempoSection* first;
1342         Metrics::iterator i;
1343
1344         /* find the TempoSection immediately preceding "where"
1345          */
1346
1347         for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
1348
1349                 if ((*i)->frame() > where) {
1350                         break;
1351                 }
1352
1353                 TempoSection* t;
1354
1355                 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1356                         if (!first) {
1357                                 first = t;
1358                         }
1359                         prev = t;
1360                 }
1361         }
1362
1363         if (!prev) {
1364                 if (!first) {
1365                         error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1366                         return;
1367                 }
1368
1369                 prev = first;
1370         }
1371
1372         /* reset */
1373
1374         {
1375                 Glib::Threads::RWLock::WriterLock lm (lock);
1376                 /* cannot move the first tempo section */
1377                 *((Tempo*)prev) = newtempo;
1378                 recompute_map (false);
1379         }
1380
1381         PropertyChanged (PropertyChange ());
1382 }
1383
1384 const MeterSection&
1385 TempoMap::first_meter () const
1386 {
1387         const MeterSection *m = 0;
1388
1389         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1390                 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1391                         return *m;
1392                 }
1393         }
1394
1395         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1396         abort(); /*NOTREACHED*/
1397         return *m;
1398 }
1399
1400 MeterSection&
1401 TempoMap::first_meter ()
1402 {
1403         MeterSection *m = 0;
1404
1405         /* CALLER MUST HOLD LOCK */
1406
1407         for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1408                 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1409                         return *m;
1410                 }
1411         }
1412
1413         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1414         abort(); /*NOTREACHED*/
1415         return *m;
1416 }
1417
1418 const TempoSection&
1419 TempoMap::first_tempo () const
1420 {
1421         const TempoSection *t = 0;
1422
1423         /* CALLER MUST HOLD LOCK */
1424
1425         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1426                 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1427                         return *t;
1428                 }
1429         }
1430
1431         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1432         abort(); /*NOTREACHED*/
1433         return *t;
1434 }
1435
1436 TempoSection&
1437 TempoMap::first_tempo ()
1438 {
1439         TempoSection *t = 0;
1440
1441         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1442                 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1443                         return *t;
1444                 }
1445         }
1446
1447         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1448         abort(); /*NOTREACHED*/
1449         return *t;
1450 }
1451
1452 void
1453 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
1454 {
1455         /* CALLER MUST HOLD WRITE LOCK */
1456
1457         if (end < 0) {
1458
1459                 /* we will actually stop once we hit
1460                    the last metric.
1461                 */
1462                 end = max_framepos;
1463
1464         }
1465
1466         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1467
1468         if (end == 0) {
1469                 /* silly call from Session::process() during startup
1470                  */
1471                 return;
1472         }
1473
1474         Metrics::const_iterator i;
1475         TempoSection* prev_ts = 0;
1476
1477         for (i = metrics.begin(); i != metrics.end(); ++i) {
1478                 TempoSection* t;
1479
1480                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1481                         if (prev_ts) {
1482                                 if (t->position_lock_style() == AudioTime) {
1483                                         if (prev_ts->type() == TempoSection::Ramp) {
1484                                                 prev_ts->set_c_func (prev_ts->compute_c_func (t->beats_per_minute(), t->frame(), _frame_rate));
1485                                                 //t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
1486                                                 t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1487                                         } else {
1488                                                 prev_ts->set_c_func (0.0);
1489                                                 //t->set_beat (prev_ts->beat_at_frame (t->frame(), _frame_rate));
1490                                                 t->set_beat (prev_ts->beat_at_tempo (t->beats_per_minute(), t->frame(), _frame_rate));
1491                                         }
1492                                 } else {
1493                                         if (prev_ts->type() == TempoSection::Ramp) {
1494                                                 prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat(), _frame_rate);
1495                                                 //t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
1496                                                 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
1497                                         } else {
1498                                                 prev_ts->set_c_func (0.0);
1499                                                 //t->set_frame (prev_ts->frame_at_beat (t->beat(), _frame_rate));
1500                                                 t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), t->beat(), _frame_rate));
1501                                         }
1502                                 }
1503                         }
1504                         prev_ts = t;
1505                 }
1506         }
1507
1508         MeterSection* meter = 0;
1509
1510         for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1511                 /* Now we have the tempos mapped to position, set meter positions.*/
1512                 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1513                         if (meter->position_lock_style() == AudioTime) {
1514                                 /* a frame based meter has to have a 1|1|0 bbt */
1515                                 pair<double, BBT_Time> pr;
1516                                 BBT_Time where;
1517
1518                                 where.bars = 1;
1519                                 where.beats = 1;
1520                                 where.ticks = 0;
1521
1522                                 pr.first = beat_at_frame_locked (meter->frame());
1523                                 pr.second = where;
1524                                 meter->set_beat (pr);
1525                         } else {
1526                                 meter->set_frame (frame_at_tick_locked (meter->tick()));
1527                         }
1528                 }
1529         }
1530 }
1531
1532
1533 TempoMetric
1534 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1535 {
1536         Glib::Threads::RWLock::ReaderLock lm (lock);
1537         TempoMetric m (first_meter(), first_tempo());
1538
1539         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1540            at something, because we insert the default tempo and meter during
1541            TempoMap construction.
1542
1543            now see if we can find better candidates.
1544         */
1545
1546         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1547
1548                 if ((*i)->frame() > frame) {
1549                         break;
1550                 }
1551
1552                 m.set_metric(*i);
1553
1554                 if (last) {
1555                         *last = i;
1556                 }
1557         }
1558
1559         return m;
1560 }
1561 /* XX meters only */
1562 TempoMetric
1563 TempoMap::metric_at (BBT_Time bbt) const
1564 {
1565         Glib::Threads::RWLock::ReaderLock lm (lock);
1566         TempoMetric m (first_meter(), first_tempo());
1567
1568         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1569            at something, because we insert the default tempo and meter during
1570            TempoMap construction.
1571
1572            now see if we can find better candidates.
1573         */
1574
1575         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1576                 MeterSection* mw;
1577                 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1578                         BBT_Time section_start (mw->bbt());
1579
1580                         if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1581                                 break;
1582                         }
1583
1584                         m.set_metric (*i);
1585                 }
1586         }
1587
1588         return m;
1589 }
1590
1591 void
1592 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1593 {
1594         Glib::Threads::RWLock::ReaderLock lm (lock);
1595
1596         if (frame < 0) {
1597                 bbt.bars = 1;
1598                 bbt.beats = 1;
1599                 bbt.ticks = 0;
1600                 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1601                 return;
1602         }
1603         bbt = beats_to_bbt_locked (beat_at_frame_locked (frame));
1604 }
1605
1606 double
1607 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1608 {
1609         Glib::Threads::RWLock::ReaderLock lm (lock);
1610         return bbt_to_beats_locked (bbt);
1611 }
1612
1613 double
1614 TempoMap::bbt_to_beats_locked (Timecode::BBT_Time bbt)
1615 {
1616         /* CALLER HOLDS READ LOCK */
1617
1618         double accumulated_beats = 0.0;
1619         double accumulated_bars = 0.0;
1620         MeterSection* prev_ms = 0;
1621
1622         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1623                 MeterSection* m;
1624                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1625                         double bars_to_m = 0.0;
1626                         if (prev_ms) {
1627                                 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1628                         }
1629                         if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1630                                 break;
1631                         }
1632                         if (prev_ms) {
1633                                 accumulated_beats += m->beat() - prev_ms->beat();
1634                                 accumulated_bars += bars_to_m;
1635                         }
1636                         prev_ms = m;
1637                 }
1638         }
1639
1640         double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1641         double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1642         double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1643         return ret;
1644 }
1645
1646 Timecode::BBT_Time
1647 TempoMap::beats_to_bbt (double beats)
1648 {
1649         Glib::Threads::RWLock::ReaderLock lm (lock);
1650         return beats_to_bbt_locked (beats);
1651 }
1652
1653 Timecode::BBT_Time
1654 TempoMap::beats_to_bbt_locked (double beats)
1655 {
1656         /* CALLER HOLDS READ LOCK */
1657
1658         MeterSection* prev_ms = 0;
1659         uint32_t accumulated_bars = 0;
1660
1661         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1662                 MeterSection* m = 0;
1663
1664                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1665
1666                         if (beats < m->beat()) {
1667                                 /* this is the meter after the one our beat is on*/
1668                                 break;
1669                         }
1670
1671                         if (prev_ms) {
1672                                 /* we need a whole number of bars. */
1673                                 accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
1674                         }
1675
1676                         prev_ms = m;
1677                 }
1678         }
1679
1680         double const beats_in_ms = beats - prev_ms->beat();
1681         uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1682         uint32_t const total_bars = bars_in_ms + accumulated_bars;
1683         double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1684         double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1685
1686         BBT_Time ret;
1687
1688         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1689         ret.beats = (uint32_t) floor (remaining_beats);
1690         ret.bars = total_bars;
1691
1692         /* 0 0 0 to 1 1 0 - based mapping*/
1693         ++ret.bars;
1694         ++ret.beats;
1695
1696         if (ret.ticks >= BBT_Time::ticks_per_beat) {
1697                 ++ret.beats;
1698                 ret.ticks -= BBT_Time::ticks_per_beat;
1699         }
1700
1701         if (ret.beats > prev_ms->divisions_per_bar()) {
1702                 ++ret.bars;
1703                 ret.beats = 1;
1704         }
1705
1706         return ret;
1707 }
1708
1709 double
1710 TempoMap::tick_at_frame_locked (framecnt_t frame) const
1711 {
1712         /* HOLD (at least) THE READER LOCK */
1713
1714         TempoSection* prev_ts = 0;
1715         double accumulated_ticks = 0.0;
1716
1717         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1718                 TempoSection* t;
1719                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1720                         if ((prev_ts) && frame < t->frame()) {
1721                                 /*the previous ts is the one containing the frame */
1722                                 return prev_ts->tick_at_frame (frame, _frame_rate);
1723                         }
1724
1725                         accumulated_ticks = t->tick();
1726                         prev_ts = t;
1727                 }
1728         }
1729
1730         /* treated as constant for this ts */
1731         double const ticks_in_section = ((frame - prev_ts->frame()) / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1732
1733         return ticks_in_section + accumulated_ticks;
1734
1735 }
1736
1737 framecnt_t
1738 TempoMap::frame_at_tick_locked (double tick) const
1739 {
1740         /* HOLD THE READER LOCK */
1741
1742         const TempoSection* prev_ts = 0;
1743         double accumulated_ticks = 0.0;
1744
1745         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1746                 TempoSection* t;
1747                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1748                         if (prev_ts && tick < t->tick()) {
1749                                 /* prev_ts is the one affecting us. */
1750                                 return prev_ts->frame_at_tick (tick, _frame_rate);
1751                         }
1752
1753                         accumulated_ticks = t->tick();
1754                         prev_ts = t;
1755                 }
1756         }
1757         /* must be treated as constant, irrespective of _type */
1758         double const ticks_in_section = tick - accumulated_ticks;
1759         double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
1760
1761         framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1762
1763         return ret;
1764 }
1765
1766 double
1767 TempoMap::beat_at_frame (framecnt_t frame) const
1768 {
1769         Glib::Threads::RWLock::ReaderLock lm (lock);
1770         return tick_at_frame_locked (frame) / BBT_Time::ticks_per_beat;
1771 }
1772
1773 double
1774 TempoMap::beat_at_frame_locked (framecnt_t frame) const
1775 {
1776         return tick_at_frame_locked (frame) / BBT_Time::ticks_per_beat;
1777 }
1778
1779 framecnt_t
1780 TempoMap::frame_at_beat (double beat) const
1781 {
1782         Glib::Threads::RWLock::ReaderLock lm (lock);
1783         return frame_at_tick_locked (beat * BBT_Time::ticks_per_beat);
1784 }
1785
1786 framecnt_t
1787 TempoMap::frame_at_beat_locked (double beat) const
1788 {
1789
1790         return frame_at_tick_locked (beat * BBT_Time::ticks_per_beat);
1791 }
1792
1793 framepos_t
1794 TempoMap::frame_time (const BBT_Time& bbt)
1795 {
1796         if (bbt.bars < 1) {
1797                 warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
1798                 return 0;
1799         }
1800
1801         if (bbt.beats < 1) {
1802                 throw std::logic_error ("beats are counted from one");
1803         }
1804         Glib::Threads::RWLock::ReaderLock lm (lock);
1805
1806         return frame_time_locked (bbt);;
1807 }
1808
1809 framepos_t
1810 TempoMap::frame_time_locked (const BBT_Time& bbt)
1811 {
1812         /* HOLD THE READER LOCK */
1813
1814         framepos_t const ret = frame_at_beat_locked (bbt_to_beats_locked (bbt));
1815
1816         return ret;
1817 }
1818
1819
1820 framecnt_t
1821 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1822 {
1823         Glib::Threads::RWLock::ReaderLock lm (lock);
1824
1825         Metrics::const_iterator i;
1826         TempoSection* first = 0;
1827         TempoSection* second = 0;
1828
1829         for (i = metrics.begin(); i != metrics.end(); ++i) {
1830                 TempoSection* t;
1831
1832                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1833
1834                         if ((*i)->frame() > pos) {
1835                                 second = t;
1836                                 break;
1837                         }
1838
1839                         first = t;
1840                 }
1841         }
1842         if (first && second) {
1843                 double const tick_at_time = first->tick_at_frame (pos, _frame_rate);
1844                 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1845                 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, _frame_rate);
1846
1847                 return time_at_bbt - pos;
1848         }
1849         double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1850
1851         return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1852 }
1853
1854 framepos_t
1855 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1856 {
1857         return round_to_type (fr, dir, Bar);
1858 }
1859
1860 framepos_t
1861 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1862 {
1863         return round_to_type (fr, dir, Beat);
1864 }
1865
1866 framepos_t
1867 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1868 {
1869         Glib::Threads::RWLock::ReaderLock lm (lock);
1870
1871         uint32_t ticks = (uint32_t) floor (tick_at_frame_locked (fr) + 0.5);
1872         uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1873         uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1874
1875         ticks -= beats * BBT_Time::ticks_per_beat;
1876
1877         if (dir > 0) {
1878                 /* round to next (or same iff dir == RoundUpMaybe) */
1879
1880                 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1881
1882                 if (mod == 0 && dir == RoundUpMaybe) {
1883                         /* right on the subdivision, which is fine, so do nothing */
1884
1885                 } else if (mod == 0) {
1886                         /* right on the subdivision, so the difference is just the subdivision ticks */
1887                         ticks += ticks_one_subdivisions_worth;
1888
1889                 } else {
1890                         /* not on subdivision, compute distance to next subdivision */
1891
1892                         ticks += ticks_one_subdivisions_worth - mod;
1893                 }
1894
1895                 if (ticks >= BBT_Time::ticks_per_beat) {
1896                         ticks -= BBT_Time::ticks_per_beat;
1897                 }
1898         } else if (dir < 0) {
1899
1900                 /* round to previous (or same iff dir == RoundDownMaybe) */
1901
1902                 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1903
1904                 if (difference == 0 && dir == RoundDownAlways) {
1905                         /* right on the subdivision, but force-rounding down,
1906                            so the difference is just the subdivision ticks */
1907                         difference = ticks_one_subdivisions_worth;
1908                 }
1909
1910                 if (ticks < difference) {
1911                         ticks = BBT_Time::ticks_per_beat - ticks;
1912                 } else {
1913                         ticks -= difference;
1914                 }
1915
1916         } else {
1917                 /* round to nearest */
1918                 double rem;
1919
1920                 /* compute the distance to the previous and next subdivision */
1921
1922                 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1923
1924                         /* closer to the next subdivision, so shift forward */
1925
1926                         ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1927
1928                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1929
1930                         if (ticks > BBT_Time::ticks_per_beat) {
1931                                 ++beats;
1932                                 ticks -= BBT_Time::ticks_per_beat;
1933                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1934                         }
1935
1936                 } else if (rem > 0) {
1937
1938                         /* closer to previous subdivision, so shift backward */
1939
1940                         if (rem > ticks) {
1941                                 if (beats == 0) {
1942                                         /* can't go backwards past zero, so ... */
1943                                         return 0;
1944                                 }
1945                                 /* step back to previous beat */
1946                                 --beats;
1947                                 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1948                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1949                         } else {
1950                                 ticks = lrint (ticks - rem);
1951                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1952                         }
1953                 } else {
1954                         /* on the subdivision, do nothing */
1955                 }
1956         }
1957         return frame_at_tick_locked ((beats * BBT_Time::ticks_per_beat) + ticks);
1958 }
1959
1960 framepos_t
1961 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1962 {
1963         Glib::Threads::RWLock::ReaderLock lm (lock);
1964
1965         double const beat_at_framepos = beat_at_frame_locked (frame);
1966
1967         BBT_Time bbt (beats_to_bbt_locked (beat_at_framepos));
1968
1969         switch (type) {
1970         case Bar:
1971                 if (dir < 0) {
1972                         /* find bar previous to 'frame' */
1973                         bbt.beats = 1;
1974                         bbt.ticks = 0;
1975                         return frame_time (bbt);
1976
1977                 } else if (dir > 0) {
1978                         /* find bar following 'frame' */
1979                         ++bbt.bars;
1980                         bbt.beats = 1;
1981                         bbt.ticks = 0;
1982                         return frame_time (bbt);
1983                 } else {
1984                         /* true rounding: find nearest bar */
1985                         framepos_t raw_ft = frame_time (bbt);
1986                         bbt.beats = 1;
1987                         bbt.ticks = 0;
1988                         framepos_t prev_ft = frame_time (bbt);
1989                         ++bbt.bars;
1990                         framepos_t next_ft = frame_time (bbt);
1991
1992                         if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) { 
1993                                 return next_ft;
1994                         } else {
1995                                 return prev_ft;
1996                         }
1997                 }
1998
1999                 break;
2000
2001         case Beat:
2002                 if (dir < 0) {
2003                         return frame_at_beat_locked (floor (beat_at_framepos));
2004                 } else if (dir > 0) {
2005                         return frame_at_beat_locked (ceil (beat_at_framepos));
2006                 } else {
2007                         return frame_at_beat_locked (floor (beat_at_framepos + 0.5));
2008                 }
2009                 break;
2010         }
2011
2012         return 0;
2013 }
2014
2015 void
2016 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2017                     framepos_t lower, framepos_t upper)
2018 {
2019         Glib::Threads::RWLock::ReaderLock lm (lock);
2020         uint32_t const upper_beat = (uint32_t) floor (beat_at_frame_locked (upper));
2021         uint32_t cnt = (uint32_t) ceil (beat_at_frame_locked (lower));
2022
2023         while (cnt <= upper_beat) {
2024                 framecnt_t const pos = frame_at_beat (cnt);
2025                 MeterSection const meter = meter_section_at (pos);
2026                 Tempo const tempo = tempo_at (pos);
2027                 BBT_Time const bbt = beats_to_bbt_locked ((double) cnt);
2028
2029                 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
2030                 ++cnt;
2031         }
2032 }
2033
2034 const TempoSection&
2035 TempoMap::tempo_section_at (framepos_t frame) const
2036 {
2037         Glib::Threads::RWLock::ReaderLock lm (lock);
2038
2039         Metrics::const_iterator i;
2040         TempoSection* prev = 0;
2041
2042         for (i = metrics.begin(); i != metrics.end(); ++i) {
2043                 TempoSection* t;
2044
2045                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2046
2047                         if ((*i)->frame() > frame) {
2048                                 break;
2049                         }
2050
2051                         prev = t;
2052                 }
2053         }
2054
2055         if (prev == 0) {
2056                 fatal << endmsg;
2057                 abort(); /*NOTREACHED*/
2058         }
2059
2060         return *prev;
2061 }
2062
2063 /* don't use this to calculate length (the tempo is only correct for this frame).
2064    do that stuff based on the beat_at_frame and frame_at_beat api
2065 */
2066 double
2067 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
2068 {
2069         Glib::Threads::RWLock::ReaderLock lm (lock);
2070
2071         const TempoSection* ts_at = &tempo_section_at (frame);
2072         const TempoSection* ts_after = 0;
2073         Metrics::const_iterator i;
2074
2075         for (i = metrics.begin(); i != metrics.end(); ++i) {
2076                 TempoSection* t;
2077
2078                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2079
2080                         if ((*i)->frame() > frame) {
2081                                 ts_after = t;
2082                                 break;
2083                         }
2084                 }
2085         }
2086
2087         if (ts_after) {
2088                 return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2089         }
2090         /* must be treated as constant tempo */
2091         return ts_at->frames_per_beat (_frame_rate);
2092 }
2093
2094 const Tempo
2095 TempoMap::tempo_at (framepos_t frame) const
2096 {
2097         Glib::Threads::RWLock::ReaderLock lm (lock);
2098
2099         TempoMetric m (metric_at (frame));
2100         TempoSection* prev_ts = 0;
2101
2102         Metrics::const_iterator i;
2103
2104         for (i = metrics.begin(); i != metrics.end(); ++i) {
2105                 TempoSection* t;
2106                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2107                         if ((prev_ts) && t->frame() > frame) {
2108                                 /* this is the one past frame */
2109                                 double const ret = prev_ts->tempo_at_frame (frame, _frame_rate);
2110                                 Tempo const ret_tempo (ret, m.tempo().note_type ());
2111                                 return ret_tempo;
2112                         }
2113                         prev_ts = t;
2114                 }
2115         }
2116
2117         return m.tempo();
2118 }
2119
2120 const MeterSection&
2121 TempoMap::meter_section_at (framepos_t frame) const
2122 {
2123         Glib::Threads::RWLock::ReaderLock lm (lock);
2124
2125         Metrics::const_iterator i;
2126         MeterSection* prev = 0;
2127
2128         for (i = metrics.begin(); i != metrics.end(); ++i) {
2129                 MeterSection* t;
2130
2131                 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
2132
2133                         if ((*i)->frame() > frame) {
2134                                 break;
2135                         }
2136
2137                         prev = t;
2138                 }
2139         }
2140
2141         if (prev == 0) {
2142                 fatal << endmsg;
2143                 abort(); /*NOTREACHED*/
2144         }
2145
2146         return *prev;
2147 }
2148
2149 const Meter&
2150 TempoMap::meter_at (framepos_t frame) const
2151 {
2152         TempoMetric m (metric_at (frame));
2153         return m.meter();
2154 }
2155
2156 XMLNode&
2157 TempoMap::get_state ()
2158 {
2159         Metrics::const_iterator i;
2160         XMLNode *root = new XMLNode ("TempoMap");
2161
2162         {
2163                 Glib::Threads::RWLock::ReaderLock lm (lock);
2164                 for (i = metrics.begin(); i != metrics.end(); ++i) {
2165                         root->add_child_nocopy ((*i)->get_state());
2166                 }
2167         }
2168
2169         return *root;
2170 }
2171
2172 int
2173 TempoMap::set_state (const XMLNode& node, int /*version*/)
2174 {
2175         {
2176                 Glib::Threads::RWLock::WriterLock lm (lock);
2177
2178                 XMLNodeList nlist;
2179                 XMLNodeConstIterator niter;
2180                 Metrics old_metrics (metrics);
2181                 MeterSection* last_meter = 0;
2182                 metrics.clear();
2183
2184                 nlist = node.children();
2185
2186                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2187                         XMLNode* child = *niter;
2188
2189                         if (child->name() == TempoSection::xml_state_node_name) {
2190
2191                                 try {
2192                                         TempoSection* ts = new TempoSection (*child);
2193                                         metrics.push_back (ts);
2194
2195                                         if (ts->bar_offset() < 0.0) {
2196                                                 if (last_meter) {
2197                                                         //ts->update_bar_offset_from_bbt (*last_meter);
2198                                                 }
2199                                         }
2200                                 }
2201
2202                                 catch (failed_constructor& err){
2203                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2204                                         metrics = old_metrics;
2205                                         break;
2206                                 }
2207
2208                         } else if (child->name() == MeterSection::xml_state_node_name) {
2209
2210                                 try {
2211                                         MeterSection* ms = new MeterSection (*child);
2212                                         metrics.push_back (ms);
2213                                         last_meter = ms;
2214                                 }
2215
2216                                 catch (failed_constructor& err) {
2217                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2218                                         metrics = old_metrics;
2219                                         break;
2220                                 }
2221                         }
2222                 }
2223
2224                 if (niter == nlist.end()) {
2225                         MetricSectionSorter cmp;
2226                         metrics.sort (cmp);
2227                 }
2228                 /* check for legacy sessions where bbt was the base musical unit for tempo */
2229                 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2230                         MeterSection* prev_ms;
2231                         TempoSection* prev_ts;
2232                         if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2233                                 if (prev_ms->beat() < 0.0) {
2234                                         /*XX we cannot possibly make this work??. */
2235                                         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());
2236                                         prev_ms->set_beat (start);
2237                                 }
2238                         } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2239                                 if (prev_ts->beat() < 0.0) {
2240                                         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);
2241                                         prev_ts->set_beat (start);
2242
2243                                 }
2244                         }
2245                 }
2246                 /* check for multiple tempo/meters at the same location, which
2247                    ardour2 somehow allowed.
2248                 */
2249
2250                 Metrics::iterator prev = metrics.end();
2251                 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2252                         if (prev != metrics.end()) {
2253                                 MeterSection* ms;
2254                                 MeterSection* prev_ms;
2255                                 TempoSection* ts;
2256                                 TempoSection* prev_ts;
2257                                 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2258                                         if (prev_ms->beat() == ms->beat()) {
2259                                                 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2260                                                 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
2261                                                 return -1;
2262                                         }
2263                                 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2264                                         if (prev_ts->beat() == ts->beat()) {
2265                                                 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2266                                                 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
2267                                                 return -1;
2268                                         }
2269                                 }
2270                         }
2271                         prev = i;
2272                 }
2273
2274                 recompute_map (true, -1);
2275         }
2276
2277         PropertyChanged (PropertyChange ());
2278
2279         return 0;
2280 }
2281
2282 void
2283 TempoMap::dump (std::ostream& o) const
2284 {
2285         Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2286         const MeterSection* m;
2287         const TempoSection* t;
2288
2289         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2290
2291                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2292                         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? "
2293                           << t->movable() << ')' << endl;
2294                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2295                         o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2296                           << " (movable? " << m->movable() << ')' << endl;
2297                 }
2298         }
2299 }
2300
2301 int
2302 TempoMap::n_tempos() const
2303 {
2304         Glib::Threads::RWLock::ReaderLock lm (lock);
2305         int cnt = 0;
2306
2307         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2308                 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2309                         cnt++;
2310                 }
2311         }
2312
2313         return cnt;
2314 }
2315
2316 int
2317 TempoMap::n_meters() const
2318 {
2319         Glib::Threads::RWLock::ReaderLock lm (lock);
2320         int cnt = 0;
2321
2322         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2323                 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2324                         cnt++;
2325                 }
2326         }
2327
2328         return cnt;
2329 }
2330
2331 void
2332 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2333 {
2334         {
2335                 Glib::Threads::RWLock::WriterLock lm (lock);
2336                 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2337                         if ((*i)->frame() >= where && (*i)->movable ()) {
2338                                 (*i)->set_frame ((*i)->frame() + amount);
2339                         }
2340                 }
2341
2342                 /* now reset the BBT time of all metrics, based on their new
2343                  * audio time. This is the only place where we do this reverse
2344                  * timestamp.
2345                  */
2346
2347                 Metrics::iterator i;
2348                 const MeterSection* meter;
2349                 const TempoSection* tempo;
2350                 MeterSection *m;
2351                 TempoSection *t;
2352
2353                 meter = &first_meter ();
2354                 tempo = &first_tempo ();
2355
2356                 BBT_Time start;
2357                 BBT_Time end;
2358
2359                 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2360
2361                 bool first = true;
2362                 MetricSection* prev = 0;
2363
2364                 for (i = metrics.begin(); i != metrics.end(); ++i) {
2365
2366                         BBT_Time bbt;
2367                         //TempoMetric metric (*meter, *tempo);
2368                         MeterSection* ms = const_cast<MeterSection*>(meter);
2369                         TempoSection* ts = const_cast<TempoSection*>(tempo);
2370                         if (prev) {
2371                                 if (ts){
2372                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2373                                                 ts->set_beat (t->beat());
2374                                         }
2375                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2376                                                 ts->set_beat (m->beat());
2377                                         }
2378                                         ts->set_frame (prev->frame());
2379
2380                                 }
2381                                 if (ms) {
2382                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2383                                                 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
2384                                                 ms->set_beat (start);
2385                                         }
2386                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2387                                                 pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_locked (t->beat()));
2388                                                 ms->set_beat (start);
2389                                         }
2390                                         ms->set_frame (prev->frame());
2391                                 }
2392
2393                         } else {
2394                                 // metric will be at frames=0 bbt=1|1|0 by default
2395                                 // which is correct for our purpose
2396                         }
2397
2398                         // cerr << bbt << endl;
2399
2400                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2401                                 t->set_beat (beat_at_frame_locked (m->frame()));
2402                                 tempo = t;
2403                                 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2404                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2405                                 bbt_time (m->frame(), bbt);
2406
2407                                 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2408
2409                                 if (first) {
2410                                         first = false;
2411                                 } else {
2412
2413                                         if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2414                                                 /* round up to next beat */
2415                                                 bbt.beats += 1;
2416                                         }
2417
2418                                         bbt.ticks = 0;
2419
2420                                         if (bbt.beats != 1) {
2421                                                 /* round up to next bar */
2422                                                 bbt.bars += 1;
2423                                                 bbt.beats = 1;
2424                                         }
2425                                 }
2426                                 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (m->frame()), bbt);
2427                                 m->set_beat (start);
2428                                 meter = m;
2429                                 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2430                         } else {
2431                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2432                                 abort(); /*NOTREACHED*/
2433                         }
2434
2435                         prev = (*i);
2436                 }
2437
2438                 recompute_map (true);
2439         }
2440
2441
2442         PropertyChanged (PropertyChange ());
2443 }
2444 bool
2445 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2446 {
2447         bool moved = false;
2448
2449         std::list<MetricSection*> metric_kill_list;
2450
2451         TempoSection* last_tempo = NULL;
2452         MeterSection* last_meter = NULL;
2453         bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2454         bool meter_after = false; // is there a meter marker likewise?
2455         {
2456                 Glib::Threads::RWLock::WriterLock lm (lock);
2457                 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2458                         if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2459                                 metric_kill_list.push_back(*i);
2460                                 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2461                                 if (lt)
2462                                         last_tempo = lt;
2463                                 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2464                                 if (lm)
2465                                         last_meter = lm;
2466                         }
2467                         else if ((*i)->frame() >= where) {
2468                                 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2469                                 (*i)->set_frame ((*i)->frame() - amount);
2470                                 if ((*i)->frame() == where) {
2471                                         // marker was immediately after end of range
2472                                         tempo_after = dynamic_cast<TempoSection*> (*i);
2473                                         meter_after = dynamic_cast<MeterSection*> (*i);
2474                                 }
2475                                 moved = true;
2476                         }
2477                 }
2478
2479                 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2480                 if (last_tempo && !tempo_after) {
2481                         metric_kill_list.remove(last_tempo);
2482                         last_tempo->set_frame(where);
2483                         moved = true;
2484                 }
2485                 if (last_meter && !meter_after) {
2486                         metric_kill_list.remove(last_meter);
2487                         last_meter->set_frame(where);
2488                         moved = true;
2489                 }
2490
2491                 //remove all the remaining metrics
2492                 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2493                         metrics.remove(*i);
2494                         moved = true;
2495                 }
2496
2497                 if (moved) {
2498                         recompute_map (true);
2499                 }
2500         }
2501         PropertyChanged (PropertyChange ());
2502         return moved;
2503 }
2504
2505 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2506  *  pos can be -ve, if required.
2507  */
2508 framepos_t
2509 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2510 {
2511         return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2512 }
2513
2514 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2515 framepos_t
2516 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2517 {
2518         return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2519 }
2520
2521 /** Add the BBT interval op to pos and return the result */
2522 framepos_t
2523 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2524 {
2525         cerr << "framepos_plus_bbt - untested" << endl;
2526         Glib::Threads::RWLock::ReaderLock lm (lock);
2527
2528         Metrics::const_iterator i;
2529         const MeterSection* meter;
2530         const MeterSection* m;
2531         const TempoSection* tempo;
2532         const TempoSection* next_tempo = 0;
2533         const TempoSection* t;
2534         double frames_per_beat;
2535         framepos_t effective_pos = max (pos, (framepos_t) 0);
2536
2537         meter = &first_meter ();
2538         tempo = &first_tempo ();
2539
2540         assert (meter);
2541         assert (tempo);
2542
2543         /* find the starting metrics for tempo & meter */
2544
2545         for (i = metrics.begin(); i != metrics.end(); ++i) {
2546
2547                 if ((*i)->frame() > effective_pos) {
2548                         break;
2549                 }
2550
2551                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2552                         tempo = t;
2553                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2554                         meter = m;
2555                 }
2556         }
2557
2558         for (i = metrics.begin(); i != metrics.end(); ++i) {
2559                 if ((*i)->frame() > effective_pos) {
2560                         if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2561                                 next_tempo = t;
2562                         }
2563                         break;
2564                 }
2565         }
2566
2567         /* We now have:
2568
2569            meter -> the Meter for "pos"
2570            tempo -> the Tempo for "pos"
2571            next_tempo -> the Tempo after "pos" or 0
2572            i     -> for first new metric after "pos", possibly metrics.end()
2573         */
2574
2575         /* now comes the complicated part. we have to add one beat a time,
2576            checking for a new metric on every beat.
2577         */
2578
2579         uint64_t bars = 0;
2580         /* fpb is used for constant tempo */
2581         frames_per_beat = tempo->frames_per_beat (_frame_rate);
2582
2583         while (op.bars) {
2584
2585                 bars++;
2586                 op.bars--;
2587
2588                 /* check if we need to use a new metric section: has adding frames moved us
2589                    to or after the start of the next metric section? in which case, use it.
2590                 */
2591
2592                 if (i != metrics.end()) {
2593                         if ((*i)->frame() <= pos) {
2594
2595                                 /* about to change tempo or meter, so add the
2596                                  * number of frames for the bars we've just
2597                                  * traversed before we change the
2598                                  * frames_per_beat value.
2599                                  */
2600
2601                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2602                                         next_tempo = t;
2603                                 }
2604
2605                                 if (next_tempo) {
2606                                         pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2607                                 } else {
2608                                         pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2609                                 }
2610
2611                                 bars = 0;
2612
2613                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2614                                         tempo = t;
2615                                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2616                                         meter = m;
2617                                 }
2618                                 ++i;
2619                                 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2620                         }
2621                 }
2622
2623         }
2624
2625         if (next_tempo) {
2626                 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2627         } else {
2628                 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2629         }
2630
2631         uint64_t beats = 0;
2632
2633         while (op.beats) {
2634
2635                 /* given the current meter, have we gone past the end of the bar ? */
2636
2637                 beats++;
2638                 op.beats--;
2639
2640                 /* check if we need to use a new metric section: has adding frames moved us
2641                    to or after the start of the next metric section? in which case, use it.
2642                 */
2643
2644                 if (i != metrics.end()) {
2645                         if ((*i)->frame() <= pos) {
2646
2647                                 /* about to change tempo or meter, so add the
2648                                  * number of frames for the beats we've just
2649                                  * traversed before we change the
2650                                  * frames_per_beat value.
2651                                  */
2652
2653                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2654                                         next_tempo = t;
2655                                 }
2656
2657                                 if (next_tempo) {
2658                                         pos += tempo->frame_at_beat (beats, _frame_rate);
2659                                 } else {
2660                                         pos += llrint (beats * frames_per_beat);
2661                                 }
2662
2663                                 beats = 0;
2664
2665                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2666                                         tempo = t;
2667                                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2668                                         meter = m;
2669                                 }
2670                                 ++i;
2671                                 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2672                         }
2673                 }
2674         }
2675
2676         if (next_tempo) {
2677                 pos += tempo->frame_at_beat (beats, _frame_rate);
2678         } else {
2679                 pos += llrint (beats * frames_per_beat);
2680         }
2681
2682         if (op.ticks) {
2683                 pos += tempo->frame_at_tick (op.ticks, _frame_rate);
2684         }
2685
2686         return pos;
2687
2688 }
2689
2690 /** Count the number of beats that are equivalent to distance when going forward,
2691     starting at pos.
2692 */
2693 Evoral::Beats
2694 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2695 {
2696         return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2697 }
2698
2699 struct bbtcmp {
2700     bool operator() (const BBT_Time& a, const BBT_Time& b) {
2701             return a < b;
2702     }
2703 };
2704
2705 std::ostream&
2706 operator<< (std::ostream& o, const Meter& m) {
2707         return o << m.divisions_per_bar() << '/' << m.note_divisor();
2708 }
2709
2710 std::ostream&
2711 operator<< (std::ostream& o, const Tempo& t) {
2712         return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2713 }
2714
2715 std::ostream&
2716 operator<< (std::ostream& o, const MetricSection& section) {
2717
2718         o << "MetricSection @ " << section.frame() << ' ';
2719
2720         const TempoSection* ts;
2721         const MeterSection* ms;
2722
2723         if ((ts = dynamic_cast<const TempoSection*> (&section)) != 0) {
2724                 o << *((const Tempo*) ts);
2725         } else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
2726                 //o << *((const Meter*) ms);
2727         }
2728
2729         return o;
2730 }