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