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