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