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