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