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