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