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