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