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