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