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