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