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