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