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