Tempo ramps - remove commented code
[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 void
885 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
886 {
887         const bool locked_to_meter = ts.locked_to_meter();
888
889         {
890                 Glib::Threads::RWLock::WriterLock lm (lock);
891                 TempoSection& first (first_tempo());
892                 if (ts.frame() != first.frame()) {
893                         remove_tempo_locked (ts);
894                         add_tempo_locked (tempo, pulse, frame, type, pls, true, locked_to_meter);
895                 } else {
896                         first.set_type (type);
897                         first.set_pulse (0.0);
898                         first.set_frame (frame);
899                         first.set_position_lock_style (AudioTime);
900                         {
901                                 /* cannot move the first tempo section */
902                                 *static_cast<Tempo*>(&first) = tempo;
903                                 recompute_map (_metrics);
904                         }
905                 }
906         }
907
908         PropertyChanged (PropertyChange ());
909 }
910
911 TempoSection*
912 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
913 {
914         TempoSection* ts = 0;
915         {
916                 Glib::Threads::RWLock::WriterLock lm (lock);
917                 ts = add_tempo_locked (tempo, pulse, frame, type, pls, true);
918         }
919
920
921         PropertyChanged (PropertyChange ());
922
923         return ts;
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 void
948 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
949 {
950         {
951                 Glib::Threads::RWLock::WriterLock lm (lock);
952                 const double beat = bbt_to_beats_locked (_metrics, where);
953
954                 if (ms.movable()) {
955                         remove_meter_locked (ms);
956                         add_meter_locked (meter, beat, where, frame, pls, true);
957                 } else {
958                         MeterSection& first (first_meter());
959                         TempoSection& first_t (first_tempo());
960                         /* cannot move the first meter section */
961                         *static_cast<Meter*>(&first) = meter;
962                         first.set_position_lock_style (AudioTime);
963                         first.set_pulse (0.0);
964                         first.set_frame (frame);
965                         pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
966                         first.set_beat (beat);
967                         first_t.set_frame (first.frame());
968                         first_t.set_pulse (0.0);
969                         first_t.set_position_lock_style (AudioTime);
970                 }
971                 recompute_map (_metrics);
972         }
973         PropertyChanged (PropertyChange ());
974 }
975
976 MeterSection*
977 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
978 {
979         MeterSection* m = 0;
980         {
981                 Glib::Threads::RWLock::WriterLock lm (lock);
982                 m = add_meter_locked (meter, beat, where, frame, pls, true);
983         }
984
985
986 #ifndef NDEBUG
987         if (DEBUG_ENABLED(DEBUG::TempoMap)) {
988                 dump (_metrics, std::cerr);
989         }
990 #endif
991
992         PropertyChanged (PropertyChange ());
993         return m;
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_locked (const Metrics& metrics, const double& beat) const
1378 {
1379         MeterSection* prev_m = 0;
1380
1381         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1382                 MeterSection* m;
1383                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1384                         if (prev_m && m->beat() > beat) {
1385                                 break;
1386                         }
1387                         prev_m = m;
1388                 }
1389
1390         }
1391         double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1392         return ret;
1393 }
1394
1395 double
1396 TempoMap::pulse_at_beat (const double& beat) const
1397 {
1398         Glib::Threads::RWLock::ReaderLock lm (lock);
1399         return pulse_at_beat_locked (_metrics, beat);
1400 }
1401
1402 double
1403 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1404 {
1405         MeterSection* prev_m = 0;
1406
1407         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1408                 MeterSection* m;
1409                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1410                         if (prev_m && m->pulse() > pulse) {
1411                                 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1412                                         break;
1413                                 }
1414                         }
1415                         prev_m = m;
1416                 }
1417         }
1418
1419         double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1420         return ret;
1421 }
1422
1423 double
1424 TempoMap::beat_at_pulse (const double& pulse) const
1425 {
1426         Glib::Threads::RWLock::ReaderLock lm (lock);
1427         return beat_at_pulse_locked (_metrics, pulse);
1428 }
1429
1430 /* tempo section based */
1431 double
1432 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1433 {
1434         /* HOLD (at least) THE READER LOCK */
1435         TempoSection* prev_t = 0;
1436
1437         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1438                 TempoSection* t;
1439                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1440                         if (!t->active()) {
1441                                 continue;
1442                         }
1443                         if (prev_t && t->frame() > frame) {
1444                                 /*the previous ts is the one containing the frame */
1445                                 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1446                                 return ret;
1447                         }
1448                         prev_t = t;
1449                 }
1450         }
1451
1452         /* treated as constant for this ts */
1453         const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1454
1455         return pulses_in_section + prev_t->pulse();
1456 }
1457
1458 double
1459 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1460 {
1461         Glib::Threads::RWLock::ReaderLock lm (lock);
1462         return pulse_at_frame_locked (_metrics, frame);
1463 }
1464
1465 /* tempo section based */
1466 framecnt_t
1467 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1468 {
1469         /* HOLD THE READER LOCK */
1470
1471         const TempoSection* prev_t = 0;
1472
1473         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1474                 TempoSection* t;
1475
1476                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1477                         if (!t->active()) {
1478                                 continue;
1479                         }
1480                         if (prev_t && t->pulse() > pulse) {
1481                                 return prev_t->frame_at_pulse (pulse, _frame_rate);
1482                         }
1483
1484                         prev_t = t;
1485                 }
1486         }
1487         /* must be treated as constant, irrespective of _type */
1488         double const pulses_in_section = pulse - prev_t->pulse();
1489         double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1490
1491         framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1492
1493         return ret;
1494 }
1495
1496 framecnt_t
1497 TempoMap::frame_at_pulse (const double& pulse) const
1498 {
1499         Glib::Threads::RWLock::ReaderLock lm (lock);
1500         return frame_at_pulse_locked (_metrics, pulse);
1501 }
1502
1503 /* meter section based */
1504 double
1505 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1506 {
1507         const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1508         MeterSection* prev_m = 0;
1509         MeterSection* next_m = 0;
1510
1511         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1512                 MeterSection* m;
1513                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1514                         if (prev_m && m->frame() > frame) {
1515                                 next_m = m;
1516                                 break;
1517                         }
1518                         prev_m = m;
1519                 }
1520         }
1521         if (frame < prev_m->frame()) {
1522                 return 0.0;
1523         }
1524         const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1525
1526         if (next_m && next_m->beat() < beat) {
1527                 return next_m->beat();
1528         }
1529
1530         return beat;
1531 }
1532
1533 double
1534 TempoMap::beat_at_frame (const framecnt_t& frame) const
1535 {
1536         Glib::Threads::RWLock::ReaderLock lm (lock);
1537         return beat_at_frame_locked (_metrics, frame);
1538 }
1539
1540 /* meter section based */
1541 framecnt_t
1542 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1543 {
1544         const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1545         MeterSection* prev_m = 0;
1546
1547         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1548                 MeterSection* m;
1549                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1550                         if (prev_m && m->beat() > beat) {
1551                                 break;
1552                         }
1553                         prev_m = m;
1554                 }
1555         }
1556
1557         return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1558 }
1559
1560 framecnt_t
1561 TempoMap::frame_at_beat (const double& beat) const
1562 {
1563         Glib::Threads::RWLock::ReaderLock lm (lock);
1564         return frame_at_beat_locked (_metrics, beat);
1565 }
1566
1567 double
1568 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1569 {
1570         /* CALLER HOLDS READ LOCK */
1571
1572         MeterSection* prev_m = 0;
1573
1574         /* because audio-locked meters have 'fake' integral beats,
1575            there is no pulse offset here.
1576         */
1577         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1578                 MeterSection* m;
1579                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1580                         if (prev_m) {
1581                                 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1582                                 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1583                                         break;
1584                                 }
1585                         }
1586                         prev_m = m;
1587                 }
1588         }
1589
1590         const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1591         const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1592         const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1593
1594         return ret;
1595 }
1596
1597 double
1598 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1599 {
1600         Glib::Threads::RWLock::ReaderLock lm (lock);
1601         return bbt_to_beats_locked (_metrics, bbt);
1602 }
1603
1604 Timecode::BBT_Time
1605 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1606 {
1607         /* CALLER HOLDS READ LOCK */
1608         MeterSection* prev_m = 0;
1609         const double beats = max (0.0, b);
1610
1611         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1612                 MeterSection* m = 0;
1613
1614                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1615                         if (prev_m) {
1616                                 if (m->beat() > beats) {
1617                                         /* this is the meter after the one our beat is on*/
1618                                         break;
1619                                 }
1620                         }
1621
1622                         prev_m = m;
1623                 }
1624         }
1625
1626         const double beats_in_ms = beats - prev_m->beat();
1627         const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1628         const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1629         const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1630         const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1631
1632         BBT_Time ret;
1633
1634         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1635         ret.beats = (uint32_t) floor (remaining_beats);
1636         ret.bars = total_bars;
1637
1638         /* 0 0 0 to 1 1 0 - based mapping*/
1639         ++ret.bars;
1640         ++ret.beats;
1641
1642         if (ret.ticks >= BBT_Time::ticks_per_beat) {
1643                 ++ret.beats;
1644                 ret.ticks -= BBT_Time::ticks_per_beat;
1645         }
1646
1647         if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1648                 ++ret.bars;
1649                 ret.beats = 1;
1650         }
1651
1652         return ret;
1653 }
1654
1655 Timecode::BBT_Time
1656 TempoMap::beats_to_bbt (const double& beats)
1657 {
1658         Glib::Threads::RWLock::ReaderLock lm (lock);
1659         return beats_to_bbt_locked (_metrics, beats);
1660 }
1661
1662 Timecode::BBT_Time
1663 TempoMap::pulse_to_bbt (const double& pulse)
1664 {
1665         Glib::Threads::RWLock::ReaderLock lm (lock);
1666         MeterSection* prev_m = 0;
1667
1668         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1669                 MeterSection* m = 0;
1670
1671                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1672
1673                         if (prev_m) {
1674                                 double const pulses_to_m = m->pulse() - prev_m->pulse();
1675                                 if (prev_m->pulse() + pulses_to_m > pulse) {
1676                                         /* this is the meter after the one our beat is on*/
1677                                         break;
1678                                 }
1679                         }
1680
1681                         prev_m = m;
1682                 }
1683         }
1684
1685         const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1686         const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1687         const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1688         const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1689         const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1690
1691         BBT_Time ret;
1692
1693         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1694         ret.beats = (uint32_t) floor (remaining_beats);
1695         ret.bars = total_bars;
1696
1697         /* 0 0 0 to 1 1 0 mapping*/
1698         ++ret.bars;
1699         ++ret.beats;
1700
1701         if (ret.ticks >= BBT_Time::ticks_per_beat) {
1702                 ++ret.beats;
1703                 ret.ticks -= BBT_Time::ticks_per_beat;
1704         }
1705
1706         if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1707                 ++ret.bars;
1708                 ret.beats = 1;
1709         }
1710
1711         return ret;
1712 }
1713
1714 void
1715 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1716 {
1717
1718         if (frame < 0) {
1719                 bbt.bars = 1;
1720                 bbt.beats = 1;
1721                 bbt.ticks = 0;
1722                 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1723                 return;
1724         }
1725         Glib::Threads::RWLock::ReaderLock lm (lock);
1726         const double beat = beat_at_frame_locked (_metrics, frame);
1727
1728         bbt = beats_to_bbt_locked (_metrics, beat);
1729 }
1730
1731 /* meter section based */
1732 framepos_t
1733 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1734 {
1735         /* HOLD THE READER LOCK */
1736
1737         const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1738         return ret;
1739 }
1740
1741 framepos_t
1742 TempoMap::frame_time (const BBT_Time& bbt)
1743 {
1744         if (bbt.bars < 1) {
1745                 warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
1746                 return 0;
1747         }
1748
1749         if (bbt.beats < 1) {
1750                 throw std::logic_error ("beats are counted from one");
1751         }
1752         Glib::Threads::RWLock::ReaderLock lm (lock);
1753
1754         return frame_time_locked (_metrics, bbt);
1755 }
1756
1757 bool
1758 TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
1759 {
1760         TempoSection* prev_t = 0;
1761         MeterSection* prev_m = 0;
1762
1763         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1764                 TempoSection* t;
1765                 MeterSection* m;
1766                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1767                         if (!t->active()) {
1768                                 continue;
1769                         }
1770                         if (prev_t) {
1771                                 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1772                                         return false;
1773                                 }
1774
1775                                 if (t->frame() == prev_t->frame()) {
1776                                         return false;
1777                                 }
1778
1779                                 /* precision check ensures pulses and frames align.*/
1780                                 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1781                                         if (!t->locked_to_meter()) {
1782                                                 return false;
1783                                         }
1784                                 }
1785                         }
1786                         prev_t = t;
1787                 }
1788
1789                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1790                         if (prev_m && m->position_lock_style() == AudioTime) {
1791                                 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_locked (metrics, m->frame() - 1));
1792                                 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
1793                                 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
1794
1795                                 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
1796                                         return false;
1797                                 }
1798                         }
1799
1800                         prev_m = m;
1801                 }
1802
1803         }
1804
1805         return true;
1806 }
1807
1808 bool
1809 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1810 {
1811         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1812                 TempoSection* t;
1813                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1814                         if (!t->movable()) {
1815                                 t->set_active (true);
1816                                 continue;
1817                         }
1818                         if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1819                                 t->set_active (false);
1820                                 t->set_pulse (0.0);
1821                         } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1822                                 t->set_active (true);
1823                         } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1824                                 return false;
1825                         }
1826                 }
1827         }
1828         return true;
1829 }
1830
1831 bool
1832 TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1833 {
1834         TempoSection* prev_t = 0;
1835         TempoSection* section_prev = 0;
1836         framepos_t first_m_frame = 0;
1837
1838         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1839                 MeterSection* m;
1840                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1841                         if (!m->movable()) {
1842                                 first_m_frame = m->frame();
1843                                 break;
1844                         }
1845                 }
1846         }
1847         if (section->movable() && frame <= first_m_frame) {
1848                 return false;
1849         }
1850
1851         section->set_active (true);
1852         section->set_frame (frame);
1853
1854         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1855                 TempoSection* t;
1856                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1857
1858                         if (!t->active()) {
1859                                 continue;
1860                         }
1861                         if (prev_t) {
1862                                 if (t == section) {
1863                                         section_prev = prev_t;
1864                                         continue;
1865                                 }
1866                                 if (t->position_lock_style() == MusicTime) {
1867                                         prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1868                                         t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1869                                 } else {
1870                                         prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1871                                         if (!t->locked_to_meter()) {
1872                                                 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1873                                         }
1874                                 }
1875                         }
1876                         prev_t = t;
1877                 }
1878         }
1879
1880         if (section_prev) {
1881                 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1882                 if (!section->locked_to_meter()) {
1883                         section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1884                 }
1885         }
1886
1887         if (section->position_lock_style() == MusicTime) {
1888                 /* we're setting the frame */
1889                 section->set_position_lock_style (AudioTime);
1890                 recompute_tempos (imaginary);
1891                 section->set_position_lock_style (MusicTime);
1892         } else {
1893                 recompute_tempos (imaginary);
1894         }
1895
1896         if (check_solved (imaginary, true)) {
1897                 return true;
1898         }
1899
1900         MetricSectionFrameSorter fcmp;
1901         imaginary.sort (fcmp);
1902         if (section->position_lock_style() == MusicTime) {
1903                 /* we're setting the frame */
1904                 section->set_position_lock_style (AudioTime);
1905                 recompute_tempos (imaginary);
1906                 section->set_position_lock_style (MusicTime);
1907         } else {
1908                 recompute_tempos (imaginary);
1909         }
1910
1911         if (check_solved (imaginary, true)) {
1912                 return true;
1913         }
1914
1915         return false;
1916 }
1917
1918 bool
1919 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
1920 {
1921         TempoSection* prev_t = 0;
1922         TempoSection* section_prev = 0;
1923
1924         section->set_pulse (pulse);
1925
1926         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1927                 TempoSection* t;
1928                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1929                         if (!t->active()) {
1930                                 continue;
1931                         }
1932                         if (!t->movable()) {
1933                                 t->set_pulse (0.0);
1934                                 prev_t = t;
1935                                 continue;
1936                         }
1937                         if (prev_t) {
1938                                 if (t == section) {
1939                                         section_prev = prev_t;
1940                                         continue;
1941                                 }
1942                                 if (t->position_lock_style() == MusicTime) {
1943                                         prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1944                                         t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1945                                 } else {
1946                                         prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1947                                         if (!t->locked_to_meter()) {
1948                                                 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1949                                         }
1950                                 }
1951                         }
1952                         prev_t = t;
1953                 }
1954         }
1955         if (section_prev) {
1956                 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1957                 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1958         }
1959
1960         if (section->position_lock_style() == AudioTime) {
1961                 /* we're setting the pulse */
1962                 section->set_position_lock_style (MusicTime);
1963                 recompute_tempos (imaginary);
1964                 section->set_position_lock_style (AudioTime);
1965         } else {
1966                 recompute_tempos (imaginary);
1967         }
1968
1969         if (check_solved (imaginary, false)) {
1970                 return true;
1971         }
1972
1973         MetricSectionSorter cmp;
1974         imaginary.sort (cmp);
1975         if (section->position_lock_style() == AudioTime) {
1976                 /* we're setting the pulse */
1977                 section->set_position_lock_style (MusicTime);
1978                 recompute_tempos (imaginary);
1979                 section->set_position_lock_style (AudioTime);
1980         } else {
1981                 recompute_tempos (imaginary);
1982         }
1983
1984         if (check_solved (imaginary, false)) {
1985                 return true;
1986         }
1987
1988         return false;
1989 }
1990
1991 bool
1992 TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
1993 {
1994         /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
1995         const MeterSection* other =  &meter_section_at_locked (imaginary, frame);
1996         if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
1997                 return false;
1998         }
1999
2000         if (!section->movable()) {
2001                 /* lock the first tempo to our first meter */
2002                 if (!set_active_tempos (imaginary, frame)) {
2003                         return false;
2004                 }
2005         }
2006
2007         /* it would make sense to bail out if there is no audio-locked meter,
2008            however it may be desirable to move a music-locked meter by frame at some point.
2009         */
2010         TempoSection* meter_locked_tempo = 0;
2011         for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2012                 TempoSection* t;
2013                 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2014                         if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2015                                 meter_locked_tempo = t;
2016                                 break;
2017                         }
2018                 }
2019         }
2020
2021         MeterSection* prev_m = 0;
2022
2023         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2024                 MeterSection* m;
2025                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2026                         if (m == section){
2027                                 if (prev_m && section->movable()) {
2028                                         const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2029                                         if (beats + prev_m->beat() < section->beat()) {
2030                                                 /* disallow position change if it will alter our beat
2031                                                    we allow tempo changes to do this in recompute_meters().
2032                                                    blocking this is an option, but i'm not convinced that
2033                                                    this is what the user would actually want.
2034                                                    here we set the frame/pulse corresponding to its musical position.
2035                                                 */
2036
2037                                                 if (meter_locked_tempo) {
2038                                                         Metrics future_map;
2039                                                         bool solved = false;
2040                                                         TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2041                                                         const double new_pulse = ((section->beat() - prev_m->beat())
2042                                                                                   / prev_m->note_divisor()) + prev_m->pulse();
2043                                                         const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2044                                                         if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
2045                                                                 meter_locked_tempo->set_pulse (new_pulse);
2046                                                                 solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
2047                                                                 section->set_frame (smallest_frame);
2048                                                                 section->set_pulse (new_pulse);
2049                                                         } else {
2050                                                                 solved = false;
2051                                                         }
2052
2053                                                         Metrics::const_iterator d = future_map.begin();
2054                                                         while (d != future_map.end()) {
2055                                                                 delete (*d);
2056                                                                 ++d;
2057                                                         }
2058
2059                                                         if (!solved) {
2060                                                                 return false;
2061                                                         }
2062                                                 }
2063                                                 return false;
2064                                         } else {
2065                                                 if (meter_locked_tempo) {
2066                                                         Metrics future_map;
2067                                                         bool solved = false;
2068
2069                                                         TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2070                                                         MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
2071                                                         meter_copy->set_frame (frame);
2072
2073                                                         if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2074                                                                 section->set_frame (frame);
2075                                                                 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2076                                                                                                 / prev_m->note_divisor()) + prev_m->pulse());
2077                                                                 solve_map_frame (imaginary, meter_locked_tempo, frame);
2078                                                         } else {
2079                                                                 solved = false;
2080                                                         }
2081
2082                                                         Metrics::const_iterator d = future_map.begin();
2083                                                         while (d != future_map.end()) {
2084                                                                 delete (*d);
2085                                                                 ++d;
2086                                                         }
2087
2088                                                         if (!solved) {
2089                                                                 return false;
2090                                                         }
2091                                                 }
2092                                         }
2093                                 } else {
2094                                         /* not movable (first meter atm) */
2095                                         if (meter_locked_tempo) {
2096                                                 Metrics future_map;
2097                                                 bool solved = false;
2098                                                 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2099
2100                                                 tempo_copy->set_frame (frame);
2101                                                 tempo_copy->set_pulse (0.0);
2102
2103                                                 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2104                                                         section->set_frame (frame);
2105                                                         meter_locked_tempo->set_frame (frame);
2106                                                         meter_locked_tempo->set_pulse (0.0);
2107                                                         solve_map_frame (imaginary, meter_locked_tempo, frame);
2108                                                 } else {
2109                                                         solved = false;
2110                                                 }
2111
2112                                                 Metrics::const_iterator d = future_map.begin();
2113                                                 while (d != future_map.end()) {
2114                                                         delete (*d);
2115                                                         ++d;
2116                                                 }
2117
2118                                                 if (!solved) {
2119                                                         return false;
2120                                                 }
2121
2122                                         } else {
2123                                                 return false;
2124                                         }
2125                                         pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2126                                         section->set_beat (b_bbt);
2127                                         section->set_pulse (0.0);
2128
2129                                 }
2130                                 break;
2131                         }
2132
2133                         prev_m = m;
2134                 }
2135         }
2136
2137         MetricSectionFrameSorter fcmp;
2138         imaginary.sort (fcmp);
2139         if (section->position_lock_style() == MusicTime) {
2140                 /* we're setting the frame */
2141                 section->set_position_lock_style (AudioTime);
2142                 recompute_meters (imaginary);
2143                 section->set_position_lock_style (MusicTime);
2144         } else {
2145                 recompute_meters (imaginary);
2146         }
2147
2148         return true;
2149 }
2150
2151 bool
2152 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2153 {
2154         /* disallow setting section to an existing meter's bbt */
2155         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2156                 MeterSection* m;
2157                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2158                         if (m != section && m->bbt().bars == when.bars) {
2159                                 return false;
2160                         }
2161                 }
2162         }
2163
2164         MeterSection* prev_m = 0;
2165         MeterSection* section_prev = 0;
2166
2167         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2168                 MeterSection* m;
2169                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2170                         pair<double, BBT_Time> b_bbt;
2171                         double new_pulse = 0.0;
2172
2173                         if (prev_m && m->bbt().bars > when.bars && !section_prev){
2174                                 section_prev = prev_m;
2175                                 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2176                                 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2177                                 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2178
2179                                 section->set_beat (b_bbt);
2180                                 section->set_pulse (pulse);
2181                                 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2182                                 prev_m = section;
2183                                 continue;
2184                         }
2185
2186                         if (m->position_lock_style() == AudioTime) {
2187                                 TempoSection* meter_locked_tempo = 0;
2188                                 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2189                                         TempoSection* t;
2190                                         if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2191                                                 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2192                                                         meter_locked_tempo = t;
2193                                                         break;
2194                                                 }
2195                                         }
2196                                 }
2197
2198                                 if (prev_m) {
2199                                         const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2200
2201                                         if (beats + prev_m->beat() != m->beat()) {
2202                                                 /* tempo/ meter change caused a change in beat (bar). */
2203                                                 b_bbt = make_pair (beats + prev_m->beat()
2204                                                                    , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2205                                                 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2206                                         } else if (m->movable()) {
2207                                                 b_bbt = make_pair (m->beat(), m->bbt());
2208                                                 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2209                                         }
2210                                 } else {
2211                                         b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2212                                 }
2213                                 if (meter_locked_tempo) {
2214                                         meter_locked_tempo->set_pulse (new_pulse);
2215                                         recompute_tempos (imaginary);
2216                                 }
2217                                 m->set_beat (b_bbt);
2218                                 m->set_pulse (new_pulse);
2219
2220                         } else {
2221                                 /* MusicTime */
2222                                 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2223                                 if (beats + prev_m->beat() != m->beat()) {
2224                                         /* tempo/ meter change caused a change in beat (bar). */
2225                                         b_bbt = make_pair (beats + prev_m->beat()
2226                                                            , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2227                                 } else {
2228                                         b_bbt = make_pair (beats + prev_m->beat()
2229                                                            , m->bbt());
2230                                 }
2231                                 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2232                                 m->set_beat (b_bbt);
2233                                 m->set_pulse (new_pulse);
2234                                 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2235                         }
2236
2237                         prev_m = m;
2238                 }
2239         }
2240
2241         if (!section_prev) {
2242
2243                 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2244                 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2245                 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2246
2247                 section->set_beat (b_bbt);
2248                 section->set_pulse (pulse);
2249                 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2250         }
2251
2252         MetricSectionSorter cmp;
2253         imaginary.sort (cmp);
2254
2255         if (section->position_lock_style() == AudioTime) {
2256                 /* we're setting the pulse */
2257                 section->set_position_lock_style (MusicTime);
2258                 recompute_meters (imaginary);
2259                 section->set_position_lock_style (AudioTime);
2260         } else {
2261                 recompute_meters (imaginary);
2262         }
2263
2264         return true;
2265 }
2266
2267 /** places a copy of _metrics into copy and returns a pointer
2268  *  to section's equivalent in copy.
2269  */
2270 TempoSection*
2271 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2272 {
2273         TempoSection* ret = 0;
2274
2275         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2276                 TempoSection* t;
2277                 MeterSection* m;
2278                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2279                         if (t == section) {
2280                                 ret = new TempoSection (*t);
2281                                 copy.push_back (ret);
2282                                 continue;
2283                         }
2284
2285                         TempoSection* cp = new TempoSection (*t);
2286                         copy.push_back (cp);
2287                 }
2288                 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2289                         MeterSection* cp = new MeterSection (*m);
2290                         copy.push_back (cp);
2291                 }
2292         }
2293
2294         return ret;
2295 }
2296
2297 MeterSection*
2298 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2299 {
2300         MeterSection* ret = 0;
2301
2302         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2303                 TempoSection* t;
2304                 MeterSection* m;
2305                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2306                         TempoSection* cp = new TempoSection (*t);
2307                         copy.push_back (cp);
2308                 }
2309
2310                 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2311                         if (m == section) {
2312                                 ret = new MeterSection (*m);
2313                                 copy.push_back (ret);
2314                                 continue;
2315                         }
2316                         MeterSection* cp = new MeterSection (*m);
2317                         copy.push_back (cp);
2318                 }
2319         }
2320
2321         return ret;
2322 }
2323
2324 bool
2325 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2326 {
2327         Metrics copy;
2328         TempoSection* tempo_copy = 0;
2329
2330         {
2331                 Glib::Threads::RWLock::ReaderLock lm (lock);
2332                 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2333                 if (!tempo_copy) {
2334                         return false;
2335                 }
2336         }
2337
2338         const double beat = bbt_to_beats_locked (copy, bbt);
2339         const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
2340
2341         Metrics::const_iterator d = copy.begin();
2342         while (d != copy.end()) {
2343                 delete (*d);
2344                 ++d;
2345         }
2346
2347         return ret;
2348 }
2349
2350 /**
2351 * 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,
2352 * taking any possible reordering as a consequence of this into account.
2353 * @param section - the section to be altered
2354 * @param bpm - the new Tempo
2355 * @param bbt - the bbt where the altered tempo will fall
2356 * @return returns - the position in frames where the new tempo section will lie.
2357 */
2358 framepos_t
2359 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2360 {
2361         Glib::Threads::RWLock::ReaderLock lm (lock);
2362         Metrics future_map;
2363         framepos_t ret = 0;
2364         TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2365         if (!tempo_copy) {
2366                 return 0;
2367         }
2368         const double beat = bbt_to_beats_locked (future_map, bbt);
2369
2370         if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2371                 ret = tempo_copy->frame();
2372         } else {
2373                 ret = section->frame();
2374         }
2375
2376         Metrics::const_iterator d = future_map.begin();
2377         while (d != future_map.end()) {
2378                 delete (*d);
2379                 ++d;
2380         }
2381         return ret;
2382 }
2383
2384 double
2385 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2386 {
2387         Glib::Threads::RWLock::ReaderLock lm (lock);
2388         Metrics future_map;
2389         double ret = 0.0;
2390         TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2391
2392         if (solve_map_frame (future_map, tempo_copy, frame)) {
2393                 ret = tempo_copy->pulse();
2394         } else {
2395                 ret = section->pulse();
2396         }
2397
2398         Metrics::const_iterator d = future_map.begin();
2399         while (d != future_map.end()) {
2400                 delete (*d);
2401                 ++d;
2402         }
2403         return ret;
2404 }
2405
2406 void
2407 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2408 {
2409         Metrics future_map;
2410         {
2411                 Glib::Threads::RWLock::WriterLock lm (lock);
2412                 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2413                 if (solve_map_frame (future_map, tempo_copy, frame)) {
2414                         solve_map_frame (_metrics, ts, frame);
2415                         recompute_meters (_metrics);
2416                 }
2417         }
2418
2419         Metrics::const_iterator d = future_map.begin();
2420         while (d != future_map.end()) {
2421                 delete (*d);
2422                 ++d;
2423         }
2424
2425         MetricPositionChanged (); // Emit Signal
2426 }
2427
2428 void
2429 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2430 {
2431         Metrics future_map;
2432         {
2433                 Glib::Threads::RWLock::WriterLock lm (lock);
2434                 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2435                 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2436                         solve_map_pulse (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2437                         recompute_meters (_metrics);
2438                 }
2439         }
2440
2441         Metrics::const_iterator d = future_map.begin();
2442         while (d != future_map.end()) {
2443                 delete (*d);
2444                 ++d;
2445         }
2446
2447         MetricPositionChanged (); // Emit Signal
2448 }
2449
2450 void
2451 TempoMap::gui_move_meter_frame (MeterSection* ms, const framepos_t&  frame)
2452 {
2453         Metrics future_map;
2454         {
2455                 Glib::Threads::RWLock::WriterLock lm (lock);
2456                 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2457                 if (solve_map_frame (future_map, copy, frame)) {
2458                         solve_map_frame (_metrics, ms, frame);
2459                         recompute_tempos (_metrics);
2460                 }
2461         }
2462
2463         Metrics::const_iterator d = future_map.begin();
2464         while (d != future_map.end()) {
2465                 delete (*d);
2466                 ++d;
2467         }
2468
2469         MetricPositionChanged (); // Emit Signal
2470 }
2471
2472 void
2473 TempoMap::gui_move_meter_bbt (MeterSection* ms, const Timecode::BBT_Time& bbt)
2474 {
2475         Metrics future_map;
2476         {
2477                 Glib::Threads::RWLock::WriterLock lm (lock);
2478                 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2479                 if (solve_map_bbt (future_map, copy, bbt)) {
2480                         solve_map_bbt (_metrics, ms, bbt);
2481                         recompute_tempos (_metrics);
2482                 }
2483         }
2484
2485         Metrics::const_iterator d = future_map.begin();
2486         while (d != future_map.end()) {
2487                 delete (*d);
2488                 ++d;
2489         }
2490
2491         MetricPositionChanged (); // Emit Signal
2492 }
2493
2494 bool
2495 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2496 {
2497         Metrics future_map;
2498         bool can_solve = false;
2499         {
2500                 Glib::Threads::RWLock::WriterLock lm (lock);
2501                 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2502                 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2503                 recompute_tempos (future_map);
2504
2505                 if (check_solved (future_map, true)) {
2506                         ts->set_beats_per_minute (bpm.beats_per_minute());
2507                         recompute_map (_metrics);
2508                         can_solve = true;
2509                 }
2510         }
2511
2512         Metrics::const_iterator d = future_map.begin();
2513         while (d != future_map.end()) {
2514                 delete (*d);
2515                 ++d;
2516         }
2517         if (can_solve) {
2518                 MetricPositionChanged (); // Emit Signal
2519         }
2520         return can_solve;
2521 }
2522
2523 void
2524 TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
2525 {
2526         Metrics future_map;
2527         TempoSection* ts = 0;
2528
2529         if (frame <= first_meter().frame()) {
2530                 return;
2531         }
2532
2533         if (ms->position_lock_style() == AudioTime) {
2534                 /* disabled for now due to faked tempo locked to meter pulse */
2535                 return;
2536         }
2537
2538         {
2539                 Glib::Threads::RWLock::WriterLock lm (lock);
2540                 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2541                 if (!ts) {
2542                         return;
2543                 }
2544                 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2545                 TempoSection* prev_to_prev_t = 0;
2546                 const frameoffset_t fr_off = frame - ms->frame();
2547                 double new_bpm = 0.0;
2548
2549                 if (prev_t && prev_t->pulse() > 0.0) {
2550                         prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2551                 }
2552
2553                 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2554                    constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2555                 */
2556                 double contribution = 0.0;
2557                 frameoffset_t frame_contribution = 0;
2558                 frameoffset_t prev_t_frame_contribution = fr_off;
2559
2560                 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2561                         /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2562                         contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2563                         frame_contribution = contribution * (double) fr_off;
2564                         prev_t_frame_contribution = fr_off - frame_contribution;
2565                 }
2566
2567                 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2568
2569                         if (prev_t->position_lock_style() == MusicTime) {
2570                                 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2571                                         new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2572                                                                                 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2573
2574                                 } else {
2575                                         /* prev to prev is irrelevant */
2576                                         const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
2577                                         const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2578
2579                                         if (frame_pulse != prev_t->pulse()) {
2580                                                 new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
2581                                         } else {
2582                                                 new_bpm = prev_t->beats_per_minute();
2583                                         }
2584                                 }
2585                         } else {
2586                                 /* AudioTime */
2587                                 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2588                                         new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2589                                                                                 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2590                                 } else {
2591                                         /* prev_to_prev_t is irrelevant */
2592
2593                                         if (frame != prev_t->frame()) {
2594                                                 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2595                                         } else {
2596                                                 new_bpm = prev_t->beats_per_minute();
2597                                         }
2598                                 }
2599                         }
2600                 } else if (prev_t->c_func() < 0.0) {
2601                         if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2602                                 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
2603                         } else {
2604                                 /* prev_to_prev_t is irrelevant */
2605                                 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
2606                         }
2607
2608                 } else if (prev_t->c_func() > 0.0) {
2609                         if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2610                                 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
2611                         } else {
2612                                 /* prev_to_prev_t is irrelevant */
2613                                 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
2614                         }
2615                 }
2616
2617                 /* limits - a bit clunky, but meh */
2618                 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2619                 if (diff > -1.0 && diff  < 1.0) {
2620                         new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2621                                                                 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2622                 }
2623
2624                 prev_t->set_beats_per_minute (new_bpm);
2625                 recompute_tempos (future_map);
2626                 recompute_meters (future_map);
2627
2628                 if (check_solved (future_map, true)) {
2629
2630                         prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2631                         prev_t->set_beats_per_minute (new_bpm);
2632                         recompute_tempos (_metrics);
2633
2634                         if (ms->position_lock_style() == AudioTime) {
2635                                 ms->set_frame (frame);
2636                         }
2637
2638                         recompute_meters (_metrics);
2639                 }
2640         }
2641
2642         Metrics::const_iterator d = future_map.begin();
2643         while (d != future_map.end()) {
2644                 delete (*d);
2645                 ++d;
2646         }
2647
2648         MetricPositionChanged (); // Emit Signal
2649 }
2650
2651 void
2652 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
2653 {
2654         /*
2655           Ts (future prev_t)   Tnext
2656           |                    |
2657           |     [drag^]        |
2658           |----------|----------
2659                 e_f  pulse(frame)
2660         */
2661
2662         Metrics future_map;
2663
2664         {
2665                 Glib::Threads::RWLock::WriterLock lm (lock);
2666
2667                 if (!ts) {
2668                         return;
2669                 }
2670
2671                 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2672                 TempoSection* prev_to_prev_t = 0;
2673                 const frameoffset_t fr_off = end_frame - frame;
2674
2675                 if (prev_t && prev_t->pulse() > 0.0) {
2676                         prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2677                 }
2678
2679                 TempoSection* next_t = 0;
2680                 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2681                         TempoSection* t = 0;
2682                         if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2683                                 if (t->frame() > ts->frame()) {
2684                                         next_t = t;
2685                                         break;
2686                                 }
2687                         }
2688                 }
2689
2690                 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2691                    constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2692                 */
2693                 double contribution = 0.0;
2694                 double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2695
2696                 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2697                         contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
2698                 }
2699
2700                 frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
2701                 double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2702                 double new_bpm;
2703
2704                 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2705
2706                         if (prev_t->position_lock_style() == MusicTime) {
2707                                 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2708
2709                                         new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2710                                                                                 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
2711
2712                                 } else {
2713                                         /* prev to prev is irrelevant */
2714
2715                                         if (start_pulse != prev_t->pulse()) {
2716                                                 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2717                                         } else {
2718                                                 new_bpm = prev_t->beats_per_minute();
2719                                         }
2720                                 }
2721                         } else {
2722                                 /* AudioTime */
2723                                 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2724                                         new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
2725                                                                                 / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
2726                                 } else {
2727                                         /* prev_to_prev_t is irrelevant */
2728
2729                                         if (end_frame != prev_t->frame()) {
2730                                                 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2731                                         } else {
2732                                                 new_bpm = prev_t->beats_per_minute();
2733                                         }
2734                                 }
2735                         }
2736                 } else {
2737
2738                         double frame_ratio;
2739                         double pulse_ratio;
2740                         const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
2741
2742                         if (prev_to_prev_t) {
2743
2744                                 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
2745                                 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
2746                         } else {
2747
2748                                 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
2749                                 pulse_ratio = (start_pulse / end_pulse);
2750                         }
2751                         new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
2752                 }
2753
2754                 prev_t->set_beats_per_minute (new_bpm);
2755                 recompute_tempos (future_map);
2756                 recompute_meters (future_map);
2757
2758                 if (check_solved (future_map, true)) {
2759                         ts->set_beats_per_minute (new_bpm);
2760                         recompute_tempos (_metrics);
2761                         recompute_meters (_metrics);
2762                 }
2763         }
2764
2765         Metrics::const_iterator d = future_map.begin();
2766         while (d != future_map.end()) {
2767                 delete (*d);
2768                 ++d;
2769         }
2770
2771         MetricPositionChanged (); // Emit Signal
2772 }
2773
2774 framecnt_t
2775 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2776 {
2777         Glib::Threads::RWLock::ReaderLock lm (lock);
2778
2779         const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2780         const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2781         const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2782
2783         return frame_at_beat_locked (_metrics, total_beats);
2784 }
2785
2786 framepos_t
2787 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2788 {
2789         return round_to_type (fr, dir, Bar);
2790 }
2791
2792 framepos_t
2793 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2794 {
2795         return round_to_type (fr, dir, Beat);
2796 }
2797
2798 framepos_t
2799 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2800 {
2801         Glib::Threads::RWLock::ReaderLock lm (lock);
2802         uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2803         uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2804         uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2805
2806         ticks -= beats * BBT_Time::ticks_per_beat;
2807
2808         if (dir > 0) {
2809                 /* round to next (or same iff dir == RoundUpMaybe) */
2810
2811                 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2812
2813                 if (mod == 0 && dir == RoundUpMaybe) {
2814                         /* right on the subdivision, which is fine, so do nothing */
2815
2816                 } else if (mod == 0) {
2817                         /* right on the subdivision, so the difference is just the subdivision ticks */
2818                         ticks += ticks_one_subdivisions_worth;
2819
2820                 } else {
2821                         /* not on subdivision, compute distance to next subdivision */
2822
2823                         ticks += ticks_one_subdivisions_worth - mod;
2824                 }
2825
2826                 if (ticks >= BBT_Time::ticks_per_beat) {
2827                         ticks -= BBT_Time::ticks_per_beat;
2828                 }
2829         } else if (dir < 0) {
2830
2831                 /* round to previous (or same iff dir == RoundDownMaybe) */
2832
2833                 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2834
2835                 if (difference == 0 && dir == RoundDownAlways) {
2836                         /* right on the subdivision, but force-rounding down,
2837                            so the difference is just the subdivision ticks */
2838                         difference = ticks_one_subdivisions_worth;
2839                 }
2840
2841                 if (ticks < difference) {
2842                         ticks = BBT_Time::ticks_per_beat - ticks;
2843                 } else {
2844                         ticks -= difference;
2845                 }
2846
2847         } else {
2848                 /* round to nearest */
2849                 double rem;
2850
2851                 /* compute the distance to the previous and next subdivision */
2852
2853                 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2854
2855                         /* closer to the next subdivision, so shift forward */
2856
2857                         ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2858
2859                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2860
2861                         if (ticks > BBT_Time::ticks_per_beat) {
2862                                 ++beats;
2863                                 ticks -= BBT_Time::ticks_per_beat;
2864                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2865                         }
2866
2867                 } else if (rem > 0) {
2868
2869                         /* closer to previous subdivision, so shift backward */
2870
2871                         if (rem > ticks) {
2872                                 if (beats == 0) {
2873                                         /* can't go backwards past zero, so ... */
2874                                         return 0;
2875                                 }
2876                                 /* step back to previous beat */
2877                                 --beats;
2878                                 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2879                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2880                         } else {
2881                                 ticks = lrint (ticks - rem);
2882                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2883                         }
2884                 } else {
2885                         /* on the subdivision, do nothing */
2886                 }
2887         }
2888
2889         const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2890
2891         return ret_frame;
2892 }
2893
2894 void
2895 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num, RoundMode dir)
2896 {
2897         if (sub_num == -1) {
2898                 if (dir > 0) {
2899                         ++when.bars;
2900                         when.beats = 1;
2901                         when.ticks = 0;
2902                 } else if (dir < 0) {
2903                         when.beats = 1;
2904                         when.ticks = 0;
2905                 } else {
2906                         const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2907                         if ((double) when.beats > bpb / 2.0) {
2908                                 ++when.bars;
2909                         }
2910                         when.beats = 1;
2911                         when.ticks = 0;
2912                 }
2913
2914                 return;
2915
2916         } else if (sub_num == 0) {
2917                 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2918                 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2919                         ++when.beats;
2920                         while ((double) when.beats > bpb) {
2921                                 ++when.bars;
2922                                 when.beats -= (uint32_t) floor (bpb);
2923                         }
2924                 }
2925                 when.ticks = 0;
2926
2927                 return;
2928         }
2929
2930         const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2931
2932         if (dir > 0) {
2933                 /* round to next (or same iff dir == RoundUpMaybe) */
2934
2935                 uint32_t mod = when.ticks % ticks_one_subdivisions_worth;
2936
2937                 if (mod == 0 && dir == RoundUpMaybe) {
2938                         /* right on the subdivision, which is fine, so do nothing */
2939
2940                 } else if (mod == 0) {
2941                         /* right on the subdivision, so the difference is just the subdivision ticks */
2942                         when.ticks += ticks_one_subdivisions_worth;
2943
2944                 } else {
2945                         /* not on subdivision, compute distance to next subdivision */
2946
2947                         when.ticks += ticks_one_subdivisions_worth - mod;
2948                 }
2949
2950                 if (when.ticks >= BBT_Time::ticks_per_beat) {
2951                         when.ticks -= BBT_Time::ticks_per_beat;
2952                 }
2953
2954         } else if (dir < 0) {
2955                 /* round to previous (or same iff dir == RoundDownMaybe) */
2956
2957                 uint32_t difference = when.ticks % ticks_one_subdivisions_worth;
2958
2959                 if (difference == 0 && dir == RoundDownAlways) {
2960                         /* right on the subdivision, but force-rounding down,
2961                            so the difference is just the subdivision ticks */
2962                         difference = ticks_one_subdivisions_worth;
2963                 }
2964
2965                 if (when.ticks < difference) {
2966                         when.ticks = BBT_Time::ticks_per_beat - when.ticks;
2967                 } else {
2968                         when.ticks -= difference;
2969                 }
2970
2971         } else {
2972                 /* round to nearest */  double rem;
2973                 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2974                         /* closer to the next subdivision, so shift forward */
2975
2976                         when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2977
2978                         if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2979                                 ++when.beats;
2980                                 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2981                         }
2982
2983                 } else if (rem > 0) {
2984                         /* closer to previous subdivision, so shift backward */
2985
2986                         if (rem > when.ticks) {
2987                                 if (when.beats == 0) {
2988                                         /* can't go backwards past zero, so ... */
2989                                 }
2990                                 /* step back to previous beat */
2991                                 --when.beats;
2992                                 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2993                         } else {
2994                                 when.ticks = when.ticks - rem;
2995                         }
2996                 }
2997         }
2998 }
2999
3000 framepos_t
3001 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3002 {
3003         Glib::Threads::RWLock::ReaderLock lm (lock);
3004
3005         const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
3006         BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
3007
3008         switch (type) {
3009         case Bar:
3010                 if (dir < 0) {
3011                         /* find bar previous to 'frame' */
3012                         bbt.beats = 1;
3013                         bbt.ticks = 0;
3014                         return frame_time_locked (_metrics, bbt);
3015
3016                 } else if (dir > 0) {
3017                         /* find bar following 'frame' */
3018                         ++bbt.bars;
3019                         bbt.beats = 1;
3020                         bbt.ticks = 0;
3021                         return frame_time_locked (_metrics, bbt);
3022                 } else {
3023                         /* true rounding: find nearest bar */
3024                         framepos_t raw_ft = frame_time_locked (_metrics, bbt);
3025                         bbt.beats = 1;
3026                         bbt.ticks = 0;
3027                         framepos_t prev_ft = frame_time_locked (_metrics, bbt);
3028                         ++bbt.bars;
3029                         framepos_t next_ft = frame_time_locked (_metrics, bbt);
3030
3031                         if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) { 
3032                                 return next_ft;
3033                         } else {
3034                                 return prev_ft;
3035                         }
3036                 }
3037
3038                 break;
3039
3040         case Beat:
3041                 if (dir < 0) {
3042                         return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
3043                 } else if (dir > 0) {
3044                         return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
3045                 } else {
3046                         return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
3047                 }
3048                 break;
3049         }
3050
3051         return 0;
3052 }
3053
3054 void
3055 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3056                     framepos_t lower, framepos_t upper)
3057 {
3058         Glib::Threads::RWLock::ReaderLock lm (lock);
3059         int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3060         framecnt_t pos = 0;
3061         /* although the map handles negative beats, bbt doesn't. */
3062         if (cnt < 0.0) {
3063                 cnt = 0.0;
3064         }
3065         while (pos < upper) {
3066                 pos = frame_at_beat_locked (_metrics, cnt);
3067                 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
3068                 const MeterSection meter = meter_section_at_locked (_metrics, pos);
3069                 const BBT_Time bbt = beats_to_bbt (cnt);
3070                 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3071                 ++cnt;
3072         }
3073 }
3074
3075 const TempoSection&
3076 TempoMap::tempo_section_at (framepos_t frame) const
3077 {
3078         Glib::Threads::RWLock::ReaderLock lm (lock);
3079         return tempo_section_at_locked (_metrics, frame);
3080 }
3081
3082 const TempoSection&
3083 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
3084 {
3085         Metrics::const_iterator i;
3086         TempoSection* prev = 0;
3087
3088         for (i = metrics.begin(); i != metrics.end(); ++i) {
3089                 TempoSection* t;
3090
3091                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3092                         if (!t->active()) {
3093                                 continue;
3094                         }
3095                         if (prev && t->frame() > frame) {
3096                                 break;
3097                         }
3098
3099                         prev = t;
3100                 }
3101         }
3102
3103         if (prev == 0) {
3104                 fatal << endmsg;
3105                 abort(); /*NOTREACHED*/
3106         }
3107
3108         return *prev;
3109 }
3110
3111 const TempoSection&
3112 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3113 {
3114         TempoSection* prev_t = 0;
3115         const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3116
3117         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3118                 TempoSection* t;
3119                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3120                         if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3121                                 break;
3122                         }
3123                         prev_t = t;
3124                 }
3125
3126         }
3127         return *prev_t;
3128 }
3129
3130 const TempoSection&
3131 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
3132 {
3133         TempoSection* prev_t = 0;
3134
3135         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3136                 TempoSection* t;
3137                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3138                         if (prev_t && t->pulse() > pulse) {
3139                                 break;
3140                         }
3141                         prev_t = t;
3142                 }
3143
3144         }
3145         return *prev_t;
3146 }
3147
3148 /* don't use this to calculate length (the tempo is only correct for this frame).
3149    do that stuff based on the beat_at_frame and frame_at_beat api
3150 */
3151 double
3152 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3153 {
3154         Glib::Threads::RWLock::ReaderLock lm (lock);
3155
3156         const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
3157         const TempoSection* ts_after = 0;
3158         Metrics::const_iterator i;
3159
3160         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3161                 TempoSection* t;
3162
3163                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3164                         if (!t->active()) {
3165                                 continue;
3166                         }
3167                         if ((*i)->frame() > frame) {
3168                                 ts_after = t;
3169                                 break;
3170                         }
3171                 }
3172         }
3173
3174         if (ts_after) {
3175                 return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
3176         }
3177         /* must be treated as constant tempo */
3178         return ts_at->frames_per_beat (_frame_rate);
3179 }
3180
3181 const Tempo
3182 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
3183 {
3184         TempoSection* prev_t = 0;
3185
3186         Metrics::const_iterator i;
3187
3188         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3189                 TempoSection* t;
3190                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3191                         if (!t->active()) {
3192                                 continue;
3193                         }
3194                         if ((prev_t) && t->frame() > frame) {
3195                                 /* t is the section past frame */
3196                                 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
3197                                 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
3198                                 return ret_tempo;
3199                         }
3200                         prev_t = t;
3201                 }
3202         }
3203
3204         const double ret = prev_t->beats_per_minute();
3205         const Tempo ret_tempo (ret, prev_t->note_type ());
3206
3207         return ret_tempo;
3208 }
3209
3210 const Tempo
3211 TempoMap::tempo_at (const framepos_t& frame) const
3212 {
3213         Glib::Threads::RWLock::ReaderLock lm (lock);
3214         return tempo_at_locked (_metrics, frame);
3215 }
3216
3217 const MeterSection&
3218 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
3219 {
3220         Metrics::const_iterator i;
3221         MeterSection* prev = 0;
3222
3223         for (i = metrics.begin(); i != metrics.end(); ++i) {
3224                 MeterSection* m;
3225
3226                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3227
3228                         if (prev && (*i)->frame() > frame) {
3229                                 break;
3230                         }
3231
3232                         prev = m;
3233                 }
3234         }
3235
3236         if (prev == 0) {
3237                 fatal << endmsg;
3238                 abort(); /*NOTREACHED*/
3239         }
3240
3241         return *prev;
3242 }
3243
3244
3245 const MeterSection&
3246 TempoMap::meter_section_at (framepos_t frame) const
3247 {
3248         Glib::Threads::RWLock::ReaderLock lm (lock);
3249         return meter_section_at_locked (_metrics, frame);
3250 }
3251
3252 const MeterSection&
3253 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3254 {
3255         MeterSection* prev_m = 0;
3256
3257         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3258                 MeterSection* m;
3259                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3260                         if (prev_m && m->beat() > beat) {
3261                                 break;
3262                         }
3263                         prev_m = m;
3264                 }
3265
3266         }
3267         return *prev_m;
3268 }
3269
3270 const MeterSection&
3271 TempoMap::meter_section_at_beat (double beat) const
3272 {
3273         Glib::Threads::RWLock::ReaderLock lm (lock);
3274         return meter_section_at_beat_locked (_metrics, beat);
3275 }
3276
3277 const Meter&
3278 TempoMap::meter_at (framepos_t frame) const
3279 {
3280         TempoMetric m (metric_at (frame));
3281         return m.meter();
3282 }
3283
3284 void
3285 TempoMap::fix_legacy_session ()
3286 {
3287         MeterSection* prev_m = 0;
3288         TempoSection* prev_t = 0;
3289
3290         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3291                 MeterSection* m;
3292                 TempoSection* t;
3293
3294                 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3295                         if (!m->movable()) {
3296                                 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3297                                 m->set_beat (bbt);
3298                                 m->set_pulse (0.0);
3299                                 m->set_frame (0);
3300                                 m->set_position_lock_style (AudioTime);
3301                                 prev_m = m;
3302                                 continue;
3303                         }
3304                         if (prev_m) {
3305                                 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3306                                                                           + (m->bbt().beats - 1)
3307                                                                           + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3308                                                                           , m->bbt());
3309                                 m->set_beat (start);
3310                                 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3311                                         + (m->bbt().beats - 1)
3312                                         + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3313                                 m->set_pulse (start_beat / prev_m->note_divisor());
3314                         }
3315                         prev_m = m;
3316                 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3317
3318                         if (!t->active()) {
3319                                 continue;
3320                         }
3321
3322                         if (!t->movable()) {
3323                                 t->set_pulse (0.0);
3324                                 t->set_frame (0);
3325                                 t->set_position_lock_style (AudioTime);
3326                                 prev_t = t;
3327                                 continue;
3328                         }
3329
3330                         if (prev_t) {
3331                                 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3332                                         + (t->legacy_bbt().beats - 1)
3333                                         + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3334                                 if (prev_m) {
3335                                         t->set_pulse (beat / prev_m->note_divisor());
3336                                 } else {
3337                                         /* really shouldn't happen but.. */
3338                                         t->set_pulse (beat / 4.0);
3339                                 }
3340                         }
3341                         prev_t = t;
3342                 }
3343         }
3344 }
3345
3346 XMLNode&
3347 TempoMap::get_state ()
3348 {
3349         Metrics::const_iterator i;
3350         XMLNode *root = new XMLNode ("TempoMap");
3351
3352         {
3353                 Glib::Threads::RWLock::ReaderLock lm (lock);
3354                 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3355                         root->add_child_nocopy ((*i)->get_state());
3356                 }
3357         }
3358
3359         return *root;
3360 }
3361
3362 int
3363 TempoMap::set_state (const XMLNode& node, int /*version*/)
3364 {
3365         {
3366                 Glib::Threads::RWLock::WriterLock lm (lock);
3367
3368                 XMLNodeList nlist;
3369                 XMLNodeConstIterator niter;
3370                 Metrics old_metrics (_metrics);
3371                 _metrics.clear();
3372
3373                 nlist = node.children();
3374
3375                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3376                         XMLNode* child = *niter;
3377
3378                         if (child->name() == TempoSection::xml_state_node_name) {
3379
3380                                 try {
3381                                         TempoSection* ts = new TempoSection (*child);
3382                                         _metrics.push_back (ts);
3383                                 }
3384
3385                                 catch (failed_constructor& err){
3386                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3387                                         _metrics = old_metrics;
3388                                         break;
3389                                 }
3390
3391                         } else if (child->name() == MeterSection::xml_state_node_name) {
3392
3393                                 try {
3394                                         MeterSection* ms = new MeterSection (*child);
3395                                         _metrics.push_back (ms);
3396                                 }
3397
3398                                 catch (failed_constructor& err) {
3399                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3400                                         _metrics = old_metrics;
3401                                         break;
3402                                 }
3403                         }
3404                 }
3405
3406                 if (niter == nlist.end()) {
3407                         MetricSectionSorter cmp;
3408                         _metrics.sort (cmp);
3409                 }
3410
3411                 /* check for legacy sessions where bbt was the base musical unit for tempo */
3412                 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3413                         TempoSection* t;
3414                         if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3415                                 if (t->legacy_bbt().bars != 0) {
3416                                         fix_legacy_session();
3417                                         break;
3418                                 }
3419                                 break;
3420                         }
3421                 }
3422
3423                 /* check for multiple tempo/meters at the same location, which
3424                    ardour2 somehow allowed.
3425                 */
3426
3427                 Metrics::iterator prev = _metrics.end();
3428                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3429                         if (prev != _metrics.end()) {
3430                                 MeterSection* ms;
3431                                 MeterSection* prev_m;
3432                                 TempoSection* ts;
3433                                 TempoSection* prev_t;
3434                                 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3435                                         if (prev_m->pulse() == ms->pulse()) {
3436                                                 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3437                                                 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3438                                                 return -1;
3439                                         }
3440                                 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3441                                         if (prev_t->pulse() == ts->pulse()) {
3442                                                 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3443                                                 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3444                                                 return -1;
3445                                         }
3446                                 }
3447                         }
3448                         prev = i;
3449                 }
3450
3451                 recompute_map (_metrics);
3452         }
3453
3454         PropertyChanged (PropertyChange ());
3455
3456         return 0;
3457 }
3458
3459 void
3460 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3461 {
3462         Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3463         const MeterSection* m;
3464         const TempoSection* t;
3465         const TempoSection* prev_t = 0;
3466
3467         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3468
3469                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3470                         o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3471                           << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3472                         o << "current      : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3473                         if (prev_t) {
3474                                 o << "previous     : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3475                                 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;
3476                         }
3477                         prev_t = t;
3478                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3479                         o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3480                           << " pulse: " << m->pulse() <<  " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3481                 }
3482         }
3483         o << "------" << std::endl;
3484 }
3485
3486 int
3487 TempoMap::n_tempos() const
3488 {
3489         Glib::Threads::RWLock::ReaderLock lm (lock);
3490         int cnt = 0;
3491
3492         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3493                 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3494                         cnt++;
3495                 }
3496         }
3497
3498         return cnt;
3499 }
3500
3501 int
3502 TempoMap::n_meters() const
3503 {
3504         Glib::Threads::RWLock::ReaderLock lm (lock);
3505         int cnt = 0;
3506
3507         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3508                 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3509                         cnt++;
3510                 }
3511         }
3512
3513         return cnt;
3514 }
3515
3516 void
3517 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3518 {
3519         {
3520                 Glib::Threads::RWLock::WriterLock lm (lock);
3521                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3522                         if ((*i)->frame() >= where && (*i)->movable ()) {
3523                                 (*i)->set_frame ((*i)->frame() + amount);
3524                         }
3525                 }
3526
3527                 /* now reset the BBT time of all metrics, based on their new
3528                  * audio time. This is the only place where we do this reverse
3529                  * timestamp.
3530                  */
3531
3532                 Metrics::iterator i;
3533                 const MeterSection* meter;
3534                 const TempoSection* tempo;
3535                 MeterSection *m;
3536                 TempoSection *t;
3537
3538                 meter = &first_meter ();
3539                 tempo = &first_tempo ();
3540
3541                 BBT_Time start;
3542                 BBT_Time end;
3543
3544                 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3545
3546                 bool first = true;
3547                 MetricSection* prev = 0;
3548
3549                 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3550
3551                         BBT_Time bbt;
3552                         //TempoMetric metric (*meter, *tempo);
3553                         MeterSection* ms = const_cast<MeterSection*>(meter);
3554                         TempoSection* ts = const_cast<TempoSection*>(tempo);
3555                         if (prev) {
3556                                 if (ts){
3557                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3558                                                 if (!t->active()) {
3559                                                         continue;
3560                                                 }
3561                                                 ts->set_pulse (t->pulse());
3562                                         }
3563                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3564                                                 ts->set_pulse (m->pulse());
3565                                         }
3566                                         ts->set_frame (prev->frame());
3567
3568                                 }
3569                                 if (ms) {
3570                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3571                                                 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3572                                                 ms->set_beat (start);
3573                                                 ms->set_pulse (m->pulse());
3574                                         }
3575                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3576                                                 if (!t->active()) {
3577                                                         continue;
3578                                                 }
3579                                                 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3580                                                 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3581                                                 ms->set_beat (start);
3582                                                 ms->set_pulse (t->pulse());
3583                                         }
3584                                         ms->set_frame (prev->frame());
3585                                 }
3586
3587                         } else {
3588                                 // metric will be at frames=0 bbt=1|1|0 by default
3589                                 // which is correct for our purpose
3590                         }
3591
3592                         // cerr << bbt << endl;
3593
3594                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3595                                 if (!t->active()) {
3596                                         continue;
3597                                 }
3598                                 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3599                                 tempo = t;
3600                                 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3601                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3602                                 bbt_time (m->frame(), bbt);
3603
3604                                 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3605
3606                                 if (first) {
3607                                         first = false;
3608                                 } else {
3609
3610                                         if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3611                                                 /* round up to next beat */
3612                                                 bbt.beats += 1;
3613                                         }
3614
3615                                         bbt.ticks = 0;
3616
3617                                         if (bbt.beats != 1) {
3618                                                 /* round up to next bar */
3619                                                 bbt.bars += 1;
3620                                                 bbt.beats = 1;
3621                                         }
3622                                 }
3623                                 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3624                                 m->set_beat (start);
3625                                 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3626                                 meter = m;
3627                                 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3628                         } else {
3629                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3630                                 abort(); /*NOTREACHED*/
3631                         }
3632
3633                         prev = (*i);
3634                 }
3635
3636                 recompute_map (_metrics);
3637         }
3638
3639
3640         PropertyChanged (PropertyChange ());
3641 }
3642 bool
3643 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3644 {
3645         bool moved = false;
3646
3647         std::list<MetricSection*> metric_kill_list;
3648
3649         TempoSection* last_tempo = NULL;
3650         MeterSection* last_meter = NULL;
3651         bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3652         bool meter_after = false; // is there a meter marker likewise?
3653         {
3654                 Glib::Threads::RWLock::WriterLock lm (lock);
3655                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3656                         if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3657                                 metric_kill_list.push_back(*i);
3658                                 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3659                                 if (lt)
3660                                         last_tempo = lt;
3661                                 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3662                                 if (lm)
3663                                         last_meter = lm;
3664                         }
3665                         else if ((*i)->frame() >= where) {
3666                                 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3667                                 (*i)->set_frame ((*i)->frame() - amount);
3668                                 if ((*i)->frame() == where) {
3669                                         // marker was immediately after end of range
3670                                         tempo_after = dynamic_cast<TempoSection*> (*i);
3671                                         meter_after = dynamic_cast<MeterSection*> (*i);
3672                                 }
3673                                 moved = true;
3674                         }
3675                 }
3676
3677                 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3678                 if (last_tempo && !tempo_after) {
3679                         metric_kill_list.remove(last_tempo);
3680                         last_tempo->set_frame(where);
3681                         moved = true;
3682                 }
3683                 if (last_meter && !meter_after) {
3684                         metric_kill_list.remove(last_meter);
3685                         last_meter->set_frame(where);
3686                         moved = true;
3687                 }
3688
3689                 //remove all the remaining metrics
3690                 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3691                         _metrics.remove(*i);
3692                         moved = true;
3693                 }
3694
3695                 if (moved) {
3696                         recompute_map (_metrics);
3697                 }
3698         }
3699         PropertyChanged (PropertyChange ());
3700         return moved;
3701 }
3702
3703 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3704  *  pos can be -ve, if required.
3705  */
3706 framepos_t
3707 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3708 {
3709         return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3710 }
3711
3712 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3713 framepos_t
3714 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3715 {
3716         return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3717 }
3718
3719 /** Add the BBT interval op to pos and return the result */
3720 framepos_t
3721 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3722 {
3723         Glib::Threads::RWLock::ReaderLock lm (lock);
3724
3725         BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3726         pos_bbt.ticks += op.ticks;
3727         if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3728                 ++pos_bbt.beats;
3729                 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3730         }
3731         pos_bbt.beats += op.beats;
3732         /* the meter in effect will start on the bar */
3733         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();
3734         while (pos_bbt.beats >= divisions_per_bar + 1) {
3735                 ++pos_bbt.bars;
3736                 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3737                 pos_bbt.beats -= divisions_per_bar;
3738         }
3739         pos_bbt.bars += op.bars;
3740
3741         return frame_time_locked (_metrics, pos_bbt);
3742 }
3743
3744 /** Count the number of beats that are equivalent to distance when going forward,
3745     starting at pos.
3746 */
3747 Evoral::Beats
3748 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3749 {
3750         return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3751 }
3752
3753 struct bbtcmp {
3754     bool operator() (const BBT_Time& a, const BBT_Time& b) {
3755             return a < b;
3756     }
3757 };
3758
3759 std::ostream&
3760 operator<< (std::ostream& o, const Meter& m) {
3761         return o << m.divisions_per_bar() << '/' << m.note_divisor();
3762 }
3763
3764 std::ostream&
3765 operator<< (std::ostream& o, const Tempo& t) {
3766         return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3767 }
3768
3769 std::ostream&
3770 operator<< (std::ostream& o, const MetricSection& section) {
3771
3772         o << "MetricSection @ " << section.frame() << ' ';
3773
3774         const TempoSection* ts;
3775         const MeterSection* ms;
3776
3777         if ((ts = dynamic_cast<const TempoSection*> (&section)) != 0) {
3778                 o << *((const Tempo*) ts);
3779         } else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
3780                 o << *((const Meter*) ms);
3781         }
3782
3783         return o;
3784 }