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