Tempo ramps - improve constraint mod + bbt dragging begavior.
[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 framepos_t& f, const framecnt_t& frame_rate) const
211 {
212
213         if (_type == Constant || _c_func == 0.0) {
214                 return pulses_per_minute();
215         }
216
217         return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
218 }
219
220 /** returns the zero-based frame (relative to session)
221    where the tempo in whole pulses per minute occurs in this section.
222    beat b is only used for constant tempos.
223    note that the tempo map may have multiple such values.
224 */
225 framepos_t
226 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
227 {
228         if (_type == Constant || _c_func == 0.0) {
229                 return ((b - pulse())  * frames_per_pulse (frame_rate))  + frame();
230         }
231
232         return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
233 }
234 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
235 */
236 double
237 TempoSection::tempo_at_pulse (const double& p) const
238 {
239
240         if (_type == Constant || _c_func == 0.0) {
241                 return pulses_per_minute();
242         }
243         double const ppm = pulse_tempo_at_pulse (p - pulse());
244         return ppm;
245 }
246
247 /** returns the zero-based beat (relative to session)
248    where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
249    note that the session tempo map may have multiple beats at a given tempo.
250 */
251 double
252 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
253 {
254         if (_type == Constant || _c_func == 0.0) {
255                 double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
256                 return  pulses;
257         }
258         return pulse_at_pulse_tempo (ppm) + pulse();
259 }
260
261 /** returns the zero-based pulse (relative to session origin)
262    where the zero-based frame (relative to session)
263    lies.
264 */
265 double
266 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
267 {
268         if (_type == Constant || _c_func == 0.0) {
269                 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
270         }
271
272         return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
273 }
274
275 /** returns the zero-based frame (relative to session start frame)
276    where the zero-based pulse (relative to session start)
277    falls.
278 */
279
280 framepos_t
281 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
282 {
283         if (_type == Constant || _c_func == 0.0) {
284                 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
285         }
286
287         return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
288 }
289
290 /*
291 Ramp Overview
292
293       |                     *
294 Tempo |                   *
295 Tt----|-----------------*|
296 Ta----|--------------|*  |
297       |            * |   |
298       |         *    |   |
299       |     *        |   |
300 T0----|*             |   |
301   *   |              |   |
302       _______________|___|____
303       time           a   t (next tempo)
304       [        c         ] defines c
305
306 Duration in beats at time a is the integral of some Tempo function.
307 In our case, the Tempo function (Tempo at time t) is
308 T(t) = T0(e^(ct))
309
310 with function constant
311 c = log(Ta/T0)/a
312 so
313 a = log(Ta/T0)/c
314
315 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
316 b(t) = T0(e^(ct) - 1) / c
317
318 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
319 t(b) = log((c.b / T0) + 1) / c
320
321 The time t at which Tempo T occurs is a as above:
322 t(T) = log(T / T0) / c
323
324 The beat at which a Tempo T occurs is:
325 b(T) = (T - T0) / c
326
327 The Tempo at which beat b occurs is:
328 T(b) = b.c + T0
329
330 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
331 Our problem is that we usually don't know t.
332 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
333 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
334 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
335
336 By substituting our expanded t as a in the c function above, our problem is reduced to:
337 c = T0 (e^(log (Ta / T0)) - 1) / b
338
339 Of course the word 'beat' has been left loosely defined above.
340 In music, a beat is defined by the musical pulse (which comes from the tempo)
341 and the meter in use at a particular time (how many  pulse divisions there are in one bar).
342 It would be more accurate to substitute the work 'pulse' for 'beat' above.
343
344 Anyway ...
345
346 We can now store c for future time calculations.
347 If the following tempo section (the one that defines c in conjunction with this one)
348 is changed or moved, c is no longer valid.
349
350 The public methods are session-relative.
351
352 Most of this stuff is taken from this paper:
353
354 WHERE’S THE BEAT?
355 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
356 Jan C. Schacher
357 Martin Neukom
358 Zurich University of Arts
359 Institute for Computer Music and Sound Technology
360
361 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
362
363 */
364
365 /*
366   compute this ramp's function constant using the end tempo (in whole pulses per minute)
367   and duration (pulses into global start) of some later tempo section.
368 */
369 double
370 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
371 {
372         double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
373         return pulses_per_minute() *  (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
374 }
375
376 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
377 double
378 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
379 {
380         return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
381 }
382
383 framepos_t
384 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
385 {
386         return (framepos_t) floor ((time * 60.0 * frame_rate) + 0.5);
387 }
388
389 double
390 TempoSection::frame_to_minute (const framepos_t& frame, const framecnt_t& frame_rate) const
391 {
392         return (frame / (double) frame_rate) / 60.0;
393 }
394
395 /* position function */
396 double
397 TempoSection::a_func (double end_ppm, double c_func) const
398 {
399         return log (end_ppm / pulses_per_minute()) /  c_func;
400 }
401
402 /*function constant*/
403 double
404 TempoSection::c_func (double end_ppm, double end_time) const
405 {
406         return log (end_ppm / pulses_per_minute()) /  end_time;
407 }
408
409 /* tempo in ppm at time in minutes */
410 double
411 TempoSection::pulse_tempo_at_time (const double& time) const
412 {
413         return exp (_c_func * time) * pulses_per_minute();
414 }
415
416 /* time in minutes at tempo in ppm */
417 double
418 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
419 {
420         return log (pulse_tempo / pulses_per_minute()) / _c_func;
421 }
422
423 /* tick at tempo in ppm */
424 double
425 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
426 {
427         return (pulse_tempo - pulses_per_minute()) / _c_func;
428 }
429
430 /* tempo in ppm at tick */
431 double
432 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
433 {
434         return (pulse * _c_func) + pulses_per_minute();
435 }
436
437 /* pulse at time in minutes */
438 double
439 TempoSection::pulse_at_time (const double& time) const
440 {
441         return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
442 }
443
444 /* time in minutes at pulse */
445 double
446 TempoSection::time_at_pulse (const double& pulse) const
447 {
448         return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
449 }
450
451 /***********************************************************************/
452
453 const string MeterSection::xml_state_node_name = "Meter";
454
455 MeterSection::MeterSection (const XMLNode& node)
456         : MetricSection (0.0), 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         Metrics::const_iterator d = _metrics.begin();
658         while (d != _metrics.end()) {
659                 delete (*d);
660                 ++d;
661         }
662         _metrics.clear();
663 }
664
665 void
666 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
667 {
668         bool removed = false;
669
670         {
671                 Glib::Threads::RWLock::WriterLock lm (lock);
672                 if ((removed = remove_tempo_locked (tempo))) {
673                         if (complete_operation) {
674                                 recompute_map (_metrics);
675                         }
676                 }
677         }
678
679         if (removed && complete_operation) {
680                 PropertyChanged (PropertyChange ());
681         }
682 }
683
684 bool
685 TempoMap::remove_tempo_locked (const TempoSection& tempo)
686 {
687         Metrics::iterator i;
688
689         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
690                 if (dynamic_cast<TempoSection*> (*i) != 0) {
691                         if (tempo.frame() == (*i)->frame()) {
692                                 if ((*i)->movable()) {
693                                         delete (*i);
694                                         _metrics.erase (i);
695                                         return true;
696                                 }
697                         }
698                 }
699         }
700
701         return false;
702 }
703
704 void
705 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
706 {
707         bool removed = false;
708
709         {
710                 Glib::Threads::RWLock::WriterLock lm (lock);
711                 if ((removed = remove_meter_locked (tempo))) {
712                         if (complete_operation) {
713                                 recompute_map (_metrics);
714                         }
715                 }
716         }
717
718         if (removed && complete_operation) {
719                 PropertyChanged (PropertyChange ());
720         }
721 }
722
723 bool
724 TempoMap::remove_meter_locked (const MeterSection& meter)
725 {
726         Metrics::iterator i;
727
728         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
729                 TempoSection* t = 0;
730                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
731                         if (meter.frame() == (*i)->frame()) {
732                                 if (t->locked_to_meter()) {
733                                         delete (*i);
734                                         _metrics.erase (i);
735                                         break;
736                                 }
737                         }
738                 }
739         }
740
741         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
742                 if (dynamic_cast<MeterSection*> (*i) != 0) {
743                         if (meter.frame() == (*i)->frame()) {
744                                 if ((*i)->movable()) {
745                                         delete (*i);
746                                         _metrics.erase (i);
747                                         return true;
748                                 }
749                         }
750                 }
751         }
752
753         return false;
754 }
755
756 void
757 TempoMap::do_insert (MetricSection* section)
758 {
759         bool need_add = true;
760         /* we only allow new meters to be inserted on beat 1 of an existing
761          * measure.
762          */
763         MeterSection* m = 0;
764         if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
765
766                 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
767
768                         pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
769                         corrected.second.beats = 1;
770                         corrected.second.ticks = 0;
771                         corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
772                         warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
773                                                    m->bbt(), corrected.second) << endmsg;
774                         //m->set_pulse (corrected);
775                 }
776         }
777
778         /* Look for any existing MetricSection that is of the same type and
779            in the same bar as the new one, and remove it before adding
780            the new one. Note that this means that if we find a matching,
781            existing section, we can break out of the loop since we're
782            guaranteed that there is only one such match.
783         */
784
785         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
786
787                 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
788                 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
789                 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
790                 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
791
792                 if (tempo && insert_tempo) {
793
794                         /* Tempo sections */
795                         bool const ipm = insert_tempo->position_lock_style() == MusicTime;
796                         if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
797
798                                 if (!tempo->movable()) {
799
800                                         /* can't (re)move this section, so overwrite
801                                          * its data content (but not its properties as
802                                          * a section).
803                                          */
804
805                                         *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
806                                         (*i)->set_position_lock_style (AudioTime);
807                                         TempoSection* t;
808                                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
809                                                 t->set_type (insert_tempo->type());
810                                         }
811                                         need_add = false;
812                                 } else {
813                                         delete (*i);
814                                         _metrics.erase (i);
815                                 }
816                                 break;
817                         }
818
819                 } else if (meter && insert_meter) {
820
821                         /* Meter Sections */
822
823                         bool const ipm = insert_meter->position_lock_style() == MusicTime;
824
825                         if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
826
827                                 if (!meter->movable()) {
828
829                                         /* can't (re)move this section, so overwrite
830                                          * its data content (but not its properties as
831                                          * a section
832                                          */
833
834                                         *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
835                                         (*i)->set_position_lock_style (AudioTime);
836                                         need_add = false;
837                                 } else {
838                                         delete (*i);
839                                         _metrics.erase (i);
840                                 }
841
842                                 break;
843                         }
844                 } else {
845                         /* non-matching types, so we don't care */
846                 }
847         }
848
849         /* Add the given MetricSection, if we didn't just reset an existing
850          * one above
851          */
852
853         if (need_add) {
854                 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
855                 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
856                 Metrics::iterator i;
857                 if (insert_meter) {
858                         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
859                                 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
860
861                                 if (meter) {
862                                         bool const ipm = insert_meter->position_lock_style() == MusicTime;
863                                         if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
864                                                 break;
865                                         }
866                                 }
867                         }
868                 } else if (insert_tempo) {
869                         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
870                                 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
871
872                                 if (tempo) {
873                                         bool const ipm = insert_tempo->position_lock_style() == MusicTime;
874                                         if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
875                                                 break;
876                                         }
877                                 }
878                         }
879                 }
880
881                 _metrics.insert (i, section);
882                 //dump (_metrics, std::cout);
883         }
884 }
885
886 void
887 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
888 {
889         {
890                 Glib::Threads::RWLock::WriterLock lm (lock);
891                 TempoSection& first (first_tempo());
892                 if (ts.pulse() != first.pulse()) {
893                         remove_tempo_locked (ts);
894                         add_tempo_locked (tempo, pulse, true, type);
895                 } else {
896                         first.set_type (type);
897                         {
898                                 /* cannot move the first tempo section */
899                                 *static_cast<Tempo*>(&first) = tempo;
900                                 recompute_map (_metrics);
901                         }
902                 }
903         }
904
905         PropertyChanged (PropertyChange ());
906 }
907
908 void
909 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
910 {
911         {
912                 Glib::Threads::RWLock::WriterLock lm (lock);
913                 TempoSection& first (first_tempo());
914                 if (ts.frame() != first.frame()) {
915                         remove_tempo_locked (ts);
916                         add_tempo_locked (tempo, frame, true, type);
917                 } else {
918                         first.set_type (type);
919                         first.set_pulse (0.0);
920                         first.set_position_lock_style (AudioTime);
921                         {
922                                 /* cannot move the first tempo section */
923                                 *static_cast<Tempo*>(&first) = tempo;
924                                 recompute_map (_metrics);
925                         }
926                 }
927         }
928
929         PropertyChanged (PropertyChange ());
930 }
931
932 TempoSection*
933 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
934 {
935         TempoSection* ts = 0;
936         {
937                 Glib::Threads::RWLock::WriterLock lm (lock);
938                 ts = add_tempo_locked (tempo, pulse, true, type);
939         }
940
941         PropertyChanged (PropertyChange ());
942
943         return ts;
944 }
945
946 TempoSection*
947 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
948 {
949         TempoSection* ts = 0;
950         {
951                 Glib::Threads::RWLock::WriterLock lm (lock);
952                 ts = add_tempo_locked (tempo, frame, true, type);
953         }
954
955
956         PropertyChanged (PropertyChange ());
957
958         return ts;
959 }
960
961 TempoSection*
962 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
963 {
964         TempoSection* t = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
965
966         do_insert (t);
967
968         if (recompute) {
969                 solve_map (_metrics, t, t->pulse());
970                 recompute_meters (_metrics);
971         }
972
973         return t;
974 }
975
976 TempoSection*
977 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
978 {
979         TempoSection* t = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
980
981         do_insert (t);
982
983         if (recompute) {
984                 solve_map (_metrics, t, t->frame());
985                 recompute_meters (_metrics);
986         }
987
988         return t;
989 }
990
991 void
992 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
993 {
994         {
995                 Glib::Threads::RWLock::WriterLock lm (lock);
996
997                 if (ms.movable()) {
998                         remove_meter_locked (ms);
999                         add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
1000                 } else {
1001                         MeterSection& first (first_meter());
1002                         /* cannot move the first meter section */
1003                         *static_cast<Meter*>(&first) = meter;
1004                         first.set_position_lock_style (AudioTime);
1005                 }
1006                 recompute_map (_metrics);
1007         }
1008
1009         PropertyChanged (PropertyChange ());
1010 }
1011
1012 void
1013 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
1014 {
1015         {
1016                 Glib::Threads::RWLock::WriterLock lm (lock);
1017
1018                 const double beat = ms.beat();
1019                 const BBT_Time bbt = ms.bbt();
1020
1021                 if (ms.movable()) {
1022                         remove_meter_locked (ms);
1023                         add_meter_locked (meter, frame, beat, bbt, true);
1024                 } else {
1025                         MeterSection& first (first_meter());
1026                         TempoSection& first_t (first_tempo());
1027                         /* cannot move the first meter section */
1028                         *static_cast<Meter*>(&first) = meter;
1029                         first.set_position_lock_style (AudioTime);
1030                         first.set_pulse (0.0);
1031                         first.set_frame (frame);
1032                         pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1033                         first.set_beat (beat);
1034                         first_t.set_frame (first.frame());
1035                         first_t.set_pulse (0.0);
1036                         first_t.set_position_lock_style (AudioTime);
1037                 }
1038                 recompute_map (_metrics);
1039         }
1040         PropertyChanged (PropertyChange ());
1041 }
1042
1043
1044 MeterSection*
1045 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
1046 {
1047         MeterSection* m = 0;
1048         {
1049                 Glib::Threads::RWLock::WriterLock lm (lock);
1050                 m = add_meter_locked (meter, beat, where, true);
1051         }
1052
1053
1054 #ifndef NDEBUG
1055         if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1056                 dump (_metrics, std::cerr);
1057         }
1058 #endif
1059
1060         PropertyChanged (PropertyChange ());
1061
1062         return m;
1063 }
1064
1065 MeterSection*
1066 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1067 {
1068         MeterSection* m = 0;
1069         {
1070                 Glib::Threads::RWLock::WriterLock lm (lock);
1071                 m = add_meter_locked (meter, frame, beat, where, true);
1072         }
1073
1074
1075 #ifndef NDEBUG
1076         if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1077                 dump (_metrics, std::cerr);
1078         }
1079 #endif
1080
1081         PropertyChanged (PropertyChange ());
1082
1083         return m;
1084 }
1085
1086 MeterSection*
1087 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, bool recompute)
1088 {
1089         /* a new meter always starts a new bar on the first beat. so
1090            round the start time appropriately. remember that
1091            `where' is based on the existing tempo map, not
1092            the result after we insert the new meter.
1093
1094         */
1095
1096         const double pulse = pulse_at_beat_locked (_metrics, beat);
1097         MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1098
1099         do_insert (new_meter);
1100
1101         if (recompute) {
1102                 solve_map (_metrics, new_meter, where);
1103         }
1104
1105         return new_meter;
1106 }
1107
1108 MeterSection*
1109 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, const Timecode::BBT_Time& where, bool recompute)
1110 {
1111         /* add meter-locked tempo */
1112         TempoSection* t = add_tempo_locked (tempo_at_locked (_metrics, frame), frame, true, TempoSection::Ramp);
1113         if (t) {
1114                 t->set_locked_to_meter (true);
1115         }
1116
1117         MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1118         new_meter->set_pulse (pulse_at_frame_locked (_metrics, frame));
1119
1120         do_insert (new_meter);
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    the current approach is to use a meter's bbt time as its base position unit.
1320    this means that a meter's beat may change, but its bbt may not.
1321    an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1322    while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1323 */
1324 void
1325 TempoMap::recompute_meters (Metrics& metrics)
1326 {
1327         MeterSection* meter = 0;
1328         MeterSection* prev_m = 0;
1329
1330         for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1331                 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1332                         if (meter->position_lock_style() == AudioTime) {
1333                                 double pulse = 0.0;
1334                                 pair<double, BBT_Time> b_bbt;
1335                                 TempoSection* meter_locked_tempo = 0;
1336                                 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1337                                         TempoSection* t;
1338                                         if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
1339                                                 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1340                                                         meter_locked_tempo = t;
1341                                                         break;
1342                                                 }
1343                                         }
1344                                 }
1345
1346                                 if (prev_m) {
1347                                         const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1348                                         if (beats + prev_m->beat() != meter->beat()) {
1349                                                 /* reordering caused a bbt change */
1350                                                 b_bbt = make_pair (beats + prev_m->beat()
1351                                                                    , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1352                                                 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1353
1354                                         } else if (meter->movable()) {
1355                                                 b_bbt = make_pair (meter->beat(), meter->bbt());
1356                                                 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1357                                         }
1358                                 } else {
1359                                         b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1360                                 }
1361                                 if (meter_locked_tempo) {
1362                                         meter_locked_tempo->set_pulse (pulse);
1363                                 }
1364                                 meter->set_beat (b_bbt);
1365                                 meter->set_pulse (pulse);
1366
1367                         } else {
1368                                 /* MusicTime */
1369                                 double pulse = 0.0;
1370                                 pair<double, BBT_Time> new_beat;
1371                                 if (prev_m) {
1372                                         const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1373                                         if (beats + prev_m->beat() != meter->beat()) {
1374                                                 /* reordering caused a bbt change */
1375                                                 new_beat = make_pair (beats + prev_m->beat()
1376                                                                    , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1377                                         } else {
1378                                                 new_beat = make_pair (beats + prev_m->beat(), meter->bbt());
1379                                         }
1380                                         pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1381                                 } else {
1382                                         /* shouldn't happen - the first is audio-locked */
1383                                         pulse = pulse_at_beat_locked (metrics, meter->beat());
1384                                         new_beat = make_pair (meter->beat(), meter->bbt());
1385                                 }
1386
1387                                 meter->set_beat (new_beat);
1388                                 meter->set_pulse (pulse);
1389                                 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1390                         }
1391
1392                         prev_m = meter;
1393                 }
1394         }
1395 }
1396
1397 void
1398 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1399 {
1400         /* CALLER MUST HOLD WRITE LOCK */
1401
1402         if (end < 0) {
1403
1404                 /* we will actually stop once we hit
1405                    the last metric.
1406                 */
1407                 end = max_framepos;
1408
1409         }
1410
1411         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1412
1413         if (end == 0) {
1414                 /* silly call from Session::process() during startup
1415                  */
1416                 return;
1417         }
1418
1419         recompute_tempos (metrics);
1420         recompute_meters (metrics);
1421 }
1422
1423 TempoMetric
1424 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1425 {
1426         Glib::Threads::RWLock::ReaderLock lm (lock);
1427         TempoMetric m (first_meter(), first_tempo());
1428
1429         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1430            at something, because we insert the default tempo and meter during
1431            TempoMap construction.
1432
1433            now see if we can find better candidates.
1434         */
1435
1436         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1437
1438                 if ((*i)->frame() > frame) {
1439                         break;
1440                 }
1441
1442                 m.set_metric(*i);
1443
1444                 if (last) {
1445                         *last = i;
1446                 }
1447         }
1448
1449         return m;
1450 }
1451
1452 /* XX meters only */
1453 TempoMetric
1454 TempoMap::metric_at (BBT_Time bbt) const
1455 {
1456         Glib::Threads::RWLock::ReaderLock lm (lock);
1457         TempoMetric m (first_meter(), first_tempo());
1458
1459         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1460            at something, because we insert the default tempo and meter during
1461            TempoMap construction.
1462
1463            now see if we can find better candidates.
1464         */
1465
1466         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1467                 MeterSection* mw;
1468                 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1469                         BBT_Time section_start (mw->bbt());
1470
1471                         if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1472                                 break;
1473                         }
1474
1475                         m.set_metric (*i);
1476                 }
1477         }
1478
1479         return m;
1480 }
1481
1482 double
1483 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1484 {
1485         MeterSection* prev_m = 0;
1486
1487         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1488                 MeterSection* m;
1489                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1490                         if (prev_m && m->beat() > beat) {
1491                                 break;
1492                         }
1493                         prev_m = m;
1494                 }
1495
1496         }
1497         double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1498         return ret;
1499 }
1500
1501 double
1502 TempoMap::pulse_at_beat (const double& beat) const
1503 {
1504         Glib::Threads::RWLock::ReaderLock lm (lock);
1505         return pulse_at_beat_locked (_metrics, beat);
1506 }
1507
1508 double
1509 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1510 {
1511         MeterSection* prev_m = 0;
1512
1513         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1514                 MeterSection* m;
1515                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1516                         if (prev_m && m->pulse() > pulse) {
1517                                 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1518                                         break;
1519                                 }
1520                         }
1521                         prev_m = m;
1522                 }
1523         }
1524
1525         double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1526         return ret;
1527 }
1528
1529 double
1530 TempoMap::beat_at_pulse (const double& pulse) const
1531 {
1532         Glib::Threads::RWLock::ReaderLock lm (lock);
1533         return beat_at_pulse_locked (_metrics, pulse);
1534 }
1535
1536 /* tempo section based */
1537 double
1538 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1539 {
1540         /* HOLD (at least) THE READER LOCK */
1541         TempoSection* prev_t = 0;
1542
1543         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1544                 TempoSection* t;
1545                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1546                         if (!t->active()) {
1547                                 continue;
1548                         }
1549                         if (prev_t && t->frame() > frame) {
1550                                 /*the previous ts is the one containing the frame */
1551                                 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1552                                 return ret;
1553                         }
1554                         prev_t = t;
1555                 }
1556         }
1557
1558         /* treated as constant for this ts */
1559         const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1560
1561         return pulses_in_section + prev_t->pulse();
1562 }
1563
1564 double
1565 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1566 {
1567         Glib::Threads::RWLock::ReaderLock lm (lock);
1568         return pulse_at_frame_locked (_metrics, frame);
1569 }
1570
1571 /* tempo section based */
1572 framecnt_t
1573 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1574 {
1575         /* HOLD THE READER LOCK */
1576
1577         const TempoSection* prev_t = 0;
1578
1579         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1580                 TempoSection* t;
1581
1582                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1583                         if (!t->active()) {
1584                                 continue;
1585                         }
1586                         if (prev_t && t->pulse() > pulse) {
1587                                 return prev_t->frame_at_pulse (pulse, _frame_rate);
1588                         }
1589
1590                         prev_t = t;
1591                 }
1592         }
1593         /* must be treated as constant, irrespective of _type */
1594         double const pulses_in_section = pulse - prev_t->pulse();
1595         double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1596
1597         framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1598
1599         return ret;
1600 }
1601
1602 framecnt_t
1603 TempoMap::frame_at_pulse (const double& pulse) const
1604 {
1605         Glib::Threads::RWLock::ReaderLock lm (lock);
1606         return frame_at_pulse_locked (_metrics, pulse);
1607 }
1608
1609 /* meter section based */
1610 double
1611 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1612 {
1613         const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1614         MeterSection* prev_m = 0;
1615         MeterSection* next_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->frame() > frame) {
1621                                 next_m = m;
1622                                 break;
1623                         }
1624                         prev_m = m;
1625                 }
1626         }
1627         if (frame < prev_m->frame()) {
1628                 return 0.0;
1629         }
1630         const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1631
1632         if (next_m && next_m->beat() < beat) {
1633                 return next_m->beat();
1634         }
1635
1636         return beat;
1637 }
1638
1639 double
1640 TempoMap::beat_at_frame (const framecnt_t& frame) const
1641 {
1642         Glib::Threads::RWLock::ReaderLock lm (lock);
1643         return beat_at_frame_locked (_metrics, frame);
1644 }
1645
1646 /* meter section based */
1647 framecnt_t
1648 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1649 {
1650         const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1651         MeterSection* prev_m = 0;
1652
1653         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1654                 MeterSection* m;
1655                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1656                         if (prev_m && m->beat() > beat) {
1657                                 break;
1658                         }
1659                         prev_m = m;
1660                 }
1661         }
1662
1663         return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1664 }
1665
1666 framecnt_t
1667 TempoMap::frame_at_beat (const double& beat) const
1668 {
1669         Glib::Threads::RWLock::ReaderLock lm (lock);
1670         return frame_at_beat_locked (_metrics, beat);
1671 }
1672
1673 double
1674 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1675 {
1676         /* CALLER HOLDS READ LOCK */
1677
1678         MeterSection* prev_m = 0;
1679
1680         /* because audio-locked meters have 'fake' integral beats,
1681            there is no pulse offset here.
1682         */
1683         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1684                 MeterSection* m;
1685                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1686                         if (prev_m) {
1687                                 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1688                                 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1689                                         break;
1690                                 }
1691                         }
1692                         prev_m = m;
1693                 }
1694         }
1695
1696         const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1697         const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1698         const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1699
1700         return ret;
1701 }
1702
1703 double
1704 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1705 {
1706         Glib::Threads::RWLock::ReaderLock lm (lock);
1707         return bbt_to_beats_locked (_metrics, bbt);
1708 }
1709
1710 Timecode::BBT_Time
1711 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1712 {
1713         /* CALLER HOLDS READ LOCK */
1714         MeterSection* prev_m = 0;
1715         const double beats = (b < 0.0) ? 0.0 : b;
1716
1717         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1718                 MeterSection* m = 0;
1719
1720                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1721                         if (prev_m) {
1722                                 if (m->beat() > beats) {
1723                                         /* this is the meter after the one our beat is on*/
1724                                         break;
1725                                 }
1726                         }
1727
1728                         prev_m = m;
1729                 }
1730         }
1731
1732         const double beats_in_ms = beats - prev_m->beat();
1733         const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1734         const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1735         const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1736         const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1737
1738         BBT_Time ret;
1739
1740         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1741         ret.beats = (uint32_t) floor (remaining_beats);
1742         ret.bars = total_bars;
1743
1744         /* 0 0 0 to 1 1 0 - based mapping*/
1745         ++ret.bars;
1746         ++ret.beats;
1747
1748         if (ret.ticks >= BBT_Time::ticks_per_beat) {
1749                 ++ret.beats;
1750                 ret.ticks -= BBT_Time::ticks_per_beat;
1751         }
1752
1753         if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1754                 ++ret.bars;
1755                 ret.beats = 1;
1756         }
1757
1758         return ret;
1759 }
1760
1761 Timecode::BBT_Time
1762 TempoMap::beats_to_bbt (const double& beats)
1763 {
1764         Glib::Threads::RWLock::ReaderLock lm (lock);
1765         return beats_to_bbt_locked (_metrics, beats);
1766 }
1767
1768 Timecode::BBT_Time
1769 TempoMap::pulse_to_bbt (const double& pulse)
1770 {
1771         Glib::Threads::RWLock::ReaderLock lm (lock);
1772         MeterSection* prev_m = 0;
1773
1774         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1775                 MeterSection* m = 0;
1776
1777                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1778
1779                         if (prev_m) {
1780                                 double const pulses_to_m = m->pulse() - prev_m->pulse();
1781                                 if (prev_m->pulse() + pulses_to_m > pulse) {
1782                                         /* this is the meter after the one our beat is on*/
1783                                         break;
1784                                 }
1785                         }
1786
1787                         prev_m = m;
1788                 }
1789         }
1790
1791         const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1792         const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1793         const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1794         const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1795         const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1796
1797         BBT_Time ret;
1798
1799         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1800         ret.beats = (uint32_t) floor (remaining_beats);
1801         ret.bars = total_bars;
1802
1803         /* 0 0 0 to 1 1 0 mapping*/
1804         ++ret.bars;
1805         ++ret.beats;
1806
1807         if (ret.ticks >= BBT_Time::ticks_per_beat) {
1808                 ++ret.beats;
1809                 ret.ticks -= BBT_Time::ticks_per_beat;
1810         }
1811
1812         if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1813                 ++ret.bars;
1814                 ret.beats = 1;
1815         }
1816
1817         return ret;
1818 }
1819
1820 void
1821 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1822 {
1823
1824         if (frame < 0) {
1825                 bbt.bars = 1;
1826                 bbt.beats = 1;
1827                 bbt.ticks = 0;
1828                 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1829                 return;
1830         }
1831         Glib::Threads::RWLock::ReaderLock lm (lock);
1832         const double beat = beat_at_frame_locked (_metrics, frame);
1833
1834         bbt = beats_to_bbt_locked (_metrics, beat);
1835 }
1836
1837 /* meter section based */
1838 framepos_t
1839 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1840 {
1841         /* HOLD THE READER LOCK */
1842
1843         const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1844         return ret;
1845 }
1846
1847 framepos_t
1848 TempoMap::frame_time (const BBT_Time& bbt)
1849 {
1850         if (bbt.bars < 1) {
1851                 warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
1852                 return 0;
1853         }
1854
1855         if (bbt.beats < 1) {
1856                 throw std::logic_error ("beats are counted from one");
1857         }
1858         Glib::Threads::RWLock::ReaderLock lm (lock);
1859
1860         return frame_time_locked (_metrics, bbt);
1861 }
1862
1863 bool
1864 TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
1865 {
1866         TempoSection* prev_t = 0;
1867         MeterSection* prev_m = 0;
1868
1869         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1870                 TempoSection* t;
1871                 MeterSection* m;
1872                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1873                         if (!t->active()) {
1874                                 continue;
1875                         }
1876                         if (prev_t) {
1877                                 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1878                                         return false;
1879                                 }
1880
1881                                 if (t->frame() == prev_t->frame()) {
1882                                         return false;
1883                                 }
1884
1885                                 /* precision check ensures pulses and frames align.*/
1886                                 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1887                                         if (!t->locked_to_meter()) {
1888                                                 return false;
1889                                         }
1890                                 }
1891                         }
1892                         prev_t = t;
1893                 }
1894
1895                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1896                         if (prev_m && m->position_lock_style() == AudioTime) {
1897                                 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_locked (metrics, m->frame() - 1));
1898                                 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
1899                                 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
1900
1901                                 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
1902                                         return false;
1903                                 }
1904                         }
1905
1906                         prev_m = m;
1907                 }
1908
1909         }
1910
1911         return true;
1912 }
1913
1914 bool
1915 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1916 {
1917         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1918                 TempoSection* t;
1919                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1920                         if (!t->movable()) {
1921                                 t->set_active (true);
1922                                 continue;
1923                         }
1924                         if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1925                                 t->set_active (false);
1926                                 t->set_pulse (0.0);
1927                         } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1928                                 t->set_active (true);
1929                         } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1930                                 return false;
1931                         }
1932                 }
1933         }
1934         return true;
1935 }
1936
1937 bool
1938 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1939 {
1940         TempoSection* prev_t = 0;
1941         TempoSection* section_prev = 0;
1942         framepos_t first_m_frame = 0;
1943
1944         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1945                 MeterSection* m;
1946                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1947                         if (!m->movable()) {
1948                                 first_m_frame = m->frame();
1949                                 break;
1950                         }
1951                 }
1952         }
1953         if (section->movable() && frame <= first_m_frame) {
1954                 return false;
1955         }
1956
1957         section->set_active (true);
1958         section->set_frame (frame);
1959
1960         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1961                 TempoSection* t;
1962                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1963
1964                         if (!t->active()) {
1965                                 continue;
1966                         }
1967                         if (prev_t) {
1968                                 if (t == section) {
1969                                         section_prev = prev_t;
1970                                         continue;
1971                                 }
1972                                 if (t->position_lock_style() == MusicTime) {
1973                                         prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1974                                         t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1975                                 } else {
1976                                         prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1977                                         if (!t->locked_to_meter()) {
1978                                                 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1979                                         }
1980                                 }
1981                         }
1982                         prev_t = t;
1983                 }
1984         }
1985
1986         if (section_prev) {
1987                 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1988                 if (!section->locked_to_meter()) {
1989                         section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1990                 }
1991         }
1992
1993         if (section->position_lock_style() == MusicTime) {
1994                 /* we're setting the frame */
1995                 section->set_position_lock_style (AudioTime);
1996                 recompute_tempos (imaginary);
1997                 section->set_position_lock_style (MusicTime);
1998         } else {
1999                 recompute_tempos (imaginary);
2000         }
2001
2002         if (check_solved (imaginary, true)) {
2003                 return true;
2004         }
2005
2006         MetricSectionFrameSorter fcmp;
2007         imaginary.sort (fcmp);
2008         if (section->position_lock_style() == MusicTime) {
2009                 /* we're setting the frame */
2010                 section->set_position_lock_style (AudioTime);
2011                 recompute_tempos (imaginary);
2012                 section->set_position_lock_style (MusicTime);
2013         } else {
2014                 recompute_tempos (imaginary);
2015         }
2016
2017         if (check_solved (imaginary, true)) {
2018                 return true;
2019         }
2020
2021         return false;
2022 }
2023
2024 bool
2025 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
2026 {
2027         TempoSection* prev_t = 0;
2028         TempoSection* section_prev = 0;
2029
2030         section->set_pulse (pulse);
2031
2032         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2033                 TempoSection* t;
2034                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2035                         if (!t->active()) {
2036                                 continue;
2037                         }
2038                         if (!t->movable()) {
2039                                 t->set_pulse (0.0);
2040                                 prev_t = t;
2041                                 continue;
2042                         }
2043                         if (prev_t) {
2044                                 if (t == section) {
2045                                         section_prev = prev_t;
2046                                         continue;
2047                                 }
2048                                 if (t->position_lock_style() == MusicTime) {
2049                                         prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2050                                         t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2051                                 } else {
2052                                         prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2053                                         if (!t->locked_to_meter()) {
2054                                                 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2055                                         }
2056                                 }
2057                         }
2058                         prev_t = t;
2059                 }
2060         }
2061         if (section_prev) {
2062                 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2063                 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2064         }
2065
2066         if (section->position_lock_style() == AudioTime) {
2067                 /* we're setting the pulse */
2068                 section->set_position_lock_style (MusicTime);
2069                 recompute_tempos (imaginary);
2070                 section->set_position_lock_style (AudioTime);
2071         } else {
2072                 recompute_tempos (imaginary);
2073         }
2074
2075         if (check_solved (imaginary, false)) {
2076                 return true;
2077         }
2078
2079         MetricSectionSorter cmp;
2080         imaginary.sort (cmp);
2081         if (section->position_lock_style() == AudioTime) {
2082                 /* we're setting the pulse */
2083                 section->set_position_lock_style (MusicTime);
2084                 recompute_tempos (imaginary);
2085                 section->set_position_lock_style (AudioTime);
2086         } else {
2087                 recompute_tempos (imaginary);
2088         }
2089
2090         if (check_solved (imaginary, false)) {
2091                 return true;
2092         }
2093
2094         return false;
2095 }
2096
2097 bool
2098 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2099 {
2100         /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2101         const MeterSection* other =  &meter_section_at_locked (imaginary, frame);
2102         if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2103                 return false;
2104         }
2105
2106         if (!section->movable()) {
2107                 /* lock the first tempo to our first meter */
2108                 if (!set_active_tempos (imaginary, frame)) {
2109                         return false;
2110                 }
2111         }
2112
2113         /* it would make sense to bail out if there is no audio-locked meter,
2114            however it may be desirable to move a music-locked meter by frame at some point.
2115         */
2116         TempoSection* meter_locked_tempo = 0;
2117         for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2118                 TempoSection* t;
2119                 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2120                         if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2121                                 meter_locked_tempo = t;
2122                                 break;
2123                         }
2124                 }
2125         }
2126
2127         MeterSection* prev_m = 0;
2128
2129         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2130                 MeterSection* m;
2131                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2132                         if (m == section){
2133                                 if (prev_m && section->movable()) {
2134                                         const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2135                                         if (beats + prev_m->beat() < section->beat()) {
2136                                                 /* disallow position change if it will alter our beat
2137                                                    we allow tempo changes to do this in recompute_meters().
2138                                                    blocking this is an option, but i'm not convinced that
2139                                                    this is what the user would actually want.
2140                                                    here we set the frame/pulse corresponding to its musical position.
2141                                                 */
2142
2143                                                 if (meter_locked_tempo) {
2144                                                         Metrics future_map;
2145                                                         bool solved = false;
2146                                                         TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2147                                                         const double new_pulse = ((section->beat() - prev_m->beat())
2148                                                                                   / prev_m->note_divisor()) + prev_m->pulse();
2149                                                         const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2150                                                         if ((solved = solve_map (future_map, tempo_copy, smallest_frame))) {
2151                                                                 meter_locked_tempo->set_pulse (new_pulse);
2152                                                                 solve_map (imaginary, meter_locked_tempo, smallest_frame);
2153                                                                 section->set_frame (smallest_frame);
2154                                                                 section->set_pulse (new_pulse);
2155                                                         } else {
2156                                                                 solved = false;
2157                                                         }
2158
2159                                                         Metrics::const_iterator d = future_map.begin();
2160                                                         while (d != future_map.end()) {
2161                                                                 delete (*d);
2162                                                                 ++d;
2163                                                         }
2164
2165                                                         if (!solved) {
2166                                                                 return false;
2167                                                         }
2168                                                 }
2169                                                 return false;
2170                                         } else {
2171                                                 if (meter_locked_tempo) {
2172                                                         Metrics future_map;
2173                                                         bool solved = false;
2174
2175                                                         TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2176                                                         MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
2177                                                         meter_copy->set_frame (frame);
2178
2179                                                         if ((solved = solve_map (future_map, tempo_copy, frame))) {
2180                                                                 section->set_frame (frame);
2181                                                                 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2182                                                                                                 / prev_m->note_divisor()) + prev_m->pulse());
2183                                                                 solve_map (imaginary, meter_locked_tempo, frame);
2184                                                         } else {
2185                                                                 solved = false;
2186                                                         }
2187
2188                                                         Metrics::const_iterator d = future_map.begin();
2189                                                         while (d != future_map.end()) {
2190                                                                 delete (*d);
2191                                                                 ++d;
2192                                                         }
2193
2194                                                         if (!solved) {
2195                                                                 return false;
2196                                                         }
2197                                                 }
2198                                         }
2199                                 } else {
2200                                         /* not movable (first meter atm) */
2201                                         if (meter_locked_tempo) {
2202                                                 Metrics future_map;
2203                                                 bool solved = false;
2204                                                 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2205
2206                                                 tempo_copy->set_frame (frame);
2207                                                 tempo_copy->set_pulse (0.0);
2208
2209                                                 if ((solved = solve_map (future_map, tempo_copy, frame))) {
2210                                                         section->set_frame (frame);
2211                                                         meter_locked_tempo->set_frame (frame);
2212                                                         meter_locked_tempo->set_pulse (0.0);
2213                                                         solve_map (imaginary, meter_locked_tempo, frame);
2214                                                 } else {
2215                                                         solved = false;
2216                                                 }
2217
2218                                                 Metrics::const_iterator d = future_map.begin();
2219                                                 while (d != future_map.end()) {
2220                                                         delete (*d);
2221                                                         ++d;
2222                                                 }
2223
2224                                                 if (!solved) {
2225                                                         return false;
2226                                                 }
2227
2228                                         } else {
2229                                                 return false;
2230                                         }
2231                                         pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2232                                         section->set_beat (b_bbt);
2233                                         section->set_pulse (0.0);
2234
2235                                 }
2236                                 break;
2237                         }
2238
2239                         prev_m = m;
2240                 }
2241         }
2242
2243         MetricSectionFrameSorter fcmp;
2244         imaginary.sort (fcmp);
2245         if (section->position_lock_style() == MusicTime) {
2246                 /* we're setting the frame */
2247                 section->set_position_lock_style (AudioTime);
2248                 recompute_meters (imaginary);
2249                 section->set_position_lock_style (MusicTime);
2250         } else {
2251                 recompute_meters (imaginary);
2252         }
2253
2254         return true;
2255 }
2256
2257 bool
2258 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2259 {
2260         /* disallow setting section to an existing meter's bbt */
2261         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2262                 MeterSection* m;
2263                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2264                         if (m != section && m->bbt().bars == when.bars) {
2265                                 return false;
2266                         }
2267                 }
2268         }
2269
2270         MeterSection* prev_m = 0;
2271         MeterSection* section_prev = 0;
2272
2273         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2274                 MeterSection* m;
2275                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2276                         pair<double, BBT_Time> b_bbt;
2277                         double new_pulse = 0.0;
2278
2279                         if (prev_m && m->bbt().bars > when.bars && !section_prev){
2280                                 section_prev = prev_m;
2281                                 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2282                                 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2283                                 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2284
2285                                 section->set_beat (b_bbt);
2286                                 section->set_pulse (pulse);
2287                                 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2288                                 prev_m = section;
2289                                 continue;
2290                         }
2291
2292                         if (m->position_lock_style() == AudioTime) {
2293                                 TempoSection* meter_locked_tempo = 0;
2294                                 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2295                                         TempoSection* t;
2296                                         if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2297                                                 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2298                                                         meter_locked_tempo = t;
2299                                                         break;
2300                                                 }
2301                                         }
2302                                 }
2303
2304                                 if (prev_m) {
2305                                         const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2306
2307                                         if (beats + prev_m->beat() != m->beat()) {
2308                                                 /* tempo/ meter change caused a change in beat (bar). */
2309                                                 b_bbt = make_pair (beats + prev_m->beat()
2310                                                                    , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2311                                                 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2312                                         } else if (m->movable()) {
2313                                                 b_bbt = make_pair (m->beat(), m->bbt());
2314                                                 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2315                                         }
2316                                 } else {
2317                                         b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2318                                 }
2319                                 if (meter_locked_tempo) {
2320                                         meter_locked_tempo->set_pulse (new_pulse);
2321                                         recompute_tempos (imaginary);
2322                                 }
2323                                 m->set_beat (b_bbt);
2324                                 m->set_pulse (new_pulse);
2325
2326                         } else {
2327                                 /* MusicTime */
2328                                 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2329                                 if (beats + prev_m->beat() != m->beat()) {
2330                                         /* tempo/ meter change caused a change in beat (bar). */
2331                                         b_bbt = make_pair (beats + prev_m->beat()
2332                                                            , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2333                                 } else {
2334                                         b_bbt = make_pair (beats + prev_m->beat()
2335                                                            , m->bbt());
2336                                 }
2337                                 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2338                                 m->set_beat (b_bbt);
2339                                 m->set_pulse (new_pulse);
2340                                 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2341                         }
2342
2343                         prev_m = m;
2344                 }
2345         }
2346
2347         if (!section_prev) {
2348
2349                 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2350                 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2351                 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2352
2353                 section->set_beat (b_bbt);
2354                 section->set_pulse (pulse);
2355                 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2356         }
2357
2358         MetricSectionSorter cmp;
2359         imaginary.sort (cmp);
2360
2361         if (section->position_lock_style() == AudioTime) {
2362                 /* we're setting the pulse */
2363                 section->set_position_lock_style (MusicTime);
2364                 recompute_meters (imaginary);
2365                 section->set_position_lock_style (AudioTime);
2366         } else {
2367                 recompute_meters (imaginary);
2368         }
2369
2370         return true;
2371 }
2372
2373 /** places a copy of _metrics into copy and returns a pointer
2374  *  to section's equivalent in copy.
2375  */
2376 TempoSection*
2377 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2378 {
2379         TempoSection* ret = 0;
2380
2381         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2382                 TempoSection* t;
2383                 MeterSection* m;
2384                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2385                         if (t == section) {
2386                                 ret = new TempoSection (*t);
2387                                 copy.push_back (ret);
2388                                 continue;
2389                         }
2390
2391                         TempoSection* cp = new TempoSection (*t);
2392                         copy.push_back (cp);
2393                 }
2394                 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2395                         MeterSection* cp = new MeterSection (*m);
2396                         copy.push_back (cp);
2397                 }
2398         }
2399
2400         return ret;
2401 }
2402
2403 MeterSection*
2404 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2405 {
2406         MeterSection* ret = 0;
2407
2408         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2409                 TempoSection* t;
2410                 MeterSection* m;
2411                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2412                         TempoSection* cp = new TempoSection (*t);
2413                         copy.push_back (cp);
2414                 }
2415
2416                 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2417                         if (m == section) {
2418                                 ret = new MeterSection (*m);
2419                                 copy.push_back (ret);
2420                                 continue;
2421                         }
2422                         MeterSection* cp = new MeterSection (*m);
2423                         copy.push_back (cp);
2424                 }
2425         }
2426
2427         return ret;
2428 }
2429
2430 bool
2431 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2432 {
2433         Metrics copy;
2434         TempoSection* tempo_copy = 0;
2435
2436         {
2437                 Glib::Threads::RWLock::ReaderLock lm (lock);
2438                 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2439                 if (!tempo_copy) {
2440                         return false;
2441                 }
2442         }
2443
2444         const double beat = bbt_to_beats_locked (copy, bbt);
2445         const bool ret = solve_map (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
2446
2447         Metrics::const_iterator d = copy.begin();
2448         while (d != copy.end()) {
2449                 delete (*d);
2450                 ++d;
2451         }
2452
2453         return ret;
2454 }
2455
2456 /**
2457 * 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,
2458 * taking any possible reordering as a consequence of this into account.
2459 * @param section - the section to be altered
2460 * @param bpm - the new Tempo
2461 * @param bbt - the bbt where the altered tempo will fall
2462 * @return returns - the position in frames where the new tempo section will lie.
2463 */
2464 framepos_t
2465 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2466 {
2467         Glib::Threads::RWLock::ReaderLock lm (lock);
2468         Metrics future_map;
2469         framepos_t ret = 0;
2470         TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2471         if (!tempo_copy) {
2472                 return 0;
2473         }
2474         const double beat = bbt_to_beats_locked (future_map, bbt);
2475
2476         if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2477                 ret = tempo_copy->frame();
2478         } else {
2479                 ret = section->frame();
2480         }
2481
2482         Metrics::const_iterator d = future_map.begin();
2483         while (d != future_map.end()) {
2484                 delete (*d);
2485                 ++d;
2486         }
2487         return ret;
2488 }
2489
2490 double
2491 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2492 {
2493         Glib::Threads::RWLock::ReaderLock lm (lock);
2494         Metrics future_map;
2495         double ret = 0.0;
2496         TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2497
2498         if (solve_map (future_map, tempo_copy, frame)) {
2499                 ret = tempo_copy->pulse();
2500         } else {
2501                 ret = section->pulse();
2502         }
2503
2504         Metrics::const_iterator d = future_map.begin();
2505         while (d != future_map.end()) {
2506                 delete (*d);
2507                 ++d;
2508         }
2509         return ret;
2510 }
2511
2512 void
2513 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2514 {
2515         Metrics future_map;
2516         {
2517                 Glib::Threads::RWLock::WriterLock lm (lock);
2518                 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2519                 if (solve_map (future_map, tempo_copy, frame)) {
2520                         solve_map (_metrics, ts, frame);
2521                         recompute_meters (_metrics);
2522                 }
2523         }
2524
2525         Metrics::const_iterator d = future_map.begin();
2526         while (d != future_map.end()) {
2527                 delete (*d);
2528                 ++d;
2529         }
2530
2531         MetricPositionChanged (); // Emit Signal
2532 }
2533
2534 void
2535 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2536 {
2537         Metrics future_map;
2538         {
2539                 Glib::Threads::RWLock::WriterLock lm (lock);
2540                 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2541                 if (solve_map (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2542                         solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2543                         recompute_meters (_metrics);
2544                 }
2545         }
2546
2547         Metrics::const_iterator d = future_map.begin();
2548         while (d != future_map.end()) {
2549                 delete (*d);
2550                 ++d;
2551         }
2552
2553         MetricPositionChanged (); // Emit Signal
2554 }
2555
2556 void
2557 TempoMap::gui_move_tempo_pulse (TempoSection* ts, const double& pulse)
2558 {
2559         Metrics future_map;
2560         {
2561                 Glib::Threads::RWLock::WriterLock lm (lock);
2562                 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2563                 if (solve_map (future_map, tempo_copy, pulse)) {
2564                         solve_map (_metrics, ts, pulse);
2565                         recompute_meters (_metrics);
2566                 }
2567         }
2568
2569         Metrics::const_iterator d = future_map.begin();
2570         while (d != future_map.end()) {
2571                 delete (*d);
2572                 ++d;
2573         }
2574
2575         MetricPositionChanged (); // Emit Signal
2576 }
2577
2578 void
2579 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t&  frame)
2580 {
2581         Metrics future_map;
2582         {
2583                 Glib::Threads::RWLock::WriterLock lm (lock);
2584                 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2585                 if (solve_map (future_map, copy, frame)) {
2586                         solve_map (_metrics, ms, frame);
2587                         recompute_tempos (_metrics);
2588                 }
2589         }
2590
2591         Metrics::const_iterator d = future_map.begin();
2592         while (d != future_map.end()) {
2593                 delete (*d);
2594                 ++d;
2595         }
2596
2597         MetricPositionChanged (); // Emit Signal
2598 }
2599
2600 void
2601 TempoMap::gui_move_meter (MeterSection* ms, const Timecode::BBT_Time& bbt)
2602 {
2603         Metrics future_map;
2604         {
2605                 Glib::Threads::RWLock::WriterLock lm (lock);
2606                 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2607                 if (solve_map (future_map, copy, bbt)) {
2608                         solve_map (_metrics, ms, bbt);
2609                         recompute_tempos (_metrics);
2610                 }
2611         }
2612
2613         Metrics::const_iterator d = future_map.begin();
2614         while (d != future_map.end()) {
2615                 delete (*d);
2616                 ++d;
2617         }
2618
2619         MetricPositionChanged (); // Emit Signal
2620 }
2621
2622 bool
2623 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2624 {
2625         Metrics future_map;
2626         bool can_solve = false;
2627         {
2628                 Glib::Threads::RWLock::WriterLock lm (lock);
2629                 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2630                 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2631                 recompute_tempos (future_map);
2632
2633                 if (check_solved (future_map, true)) {
2634                         ts->set_beats_per_minute (bpm.beats_per_minute());
2635                         recompute_map (_metrics);
2636                         can_solve = true;
2637                 }
2638         }
2639
2640         Metrics::const_iterator d = future_map.begin();
2641         while (d != future_map.end()) {
2642                 delete (*d);
2643                 ++d;
2644         }
2645         if (can_solve) {
2646                 MetricPositionChanged (); // Emit Signal
2647         }
2648         return can_solve;
2649 }
2650
2651 void
2652 TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
2653 {
2654         Metrics future_map;
2655         TempoSection* ts = 0;
2656
2657         if (frame <= first_meter().frame()) {
2658                 return;
2659         }
2660
2661         if (ms->position_lock_style() == AudioTime) {
2662                 /* disabled for now due to faked tempo locked to meter pulse */
2663                 return;
2664         }
2665
2666         {
2667                 Glib::Threads::RWLock::WriterLock lm (lock);
2668                 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2669                 if (!ts) {
2670                         return;
2671                 }
2672                 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2673                 TempoSection* prev_to_prev_t = 0;
2674                 const frameoffset_t fr_off = frame - ms->frame();
2675                 double new_bpm = 0.0;
2676
2677                 if (prev_t && prev_t->pulse() > 0.0) {
2678                         prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2679                 }
2680
2681                 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2682                    constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2683                 */
2684                 double contribution = 0.0;
2685                 frameoffset_t frame_contribution = 0;
2686                 frameoffset_t prev_t_frame_contribution = fr_off;
2687
2688                 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2689                         /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2690                         contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2691                         frame_contribution = contribution * (double) fr_off;
2692                         prev_t_frame_contribution = fr_off - frame_contribution;
2693                 }
2694
2695                 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2696
2697                         if (prev_t->position_lock_style() == MusicTime) {
2698                                 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2699                                         new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2700                                                                                 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2701
2702                                 } else {
2703                                         /* prev to prev is irrelevant */
2704                                         const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
2705                                         const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2706
2707                                         if (frame_pulse != prev_t->pulse()) {
2708                                                 new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
2709                                         } else {
2710                                                 new_bpm = prev_t->beats_per_minute();
2711                                         }
2712                                 }
2713                         } else {
2714                                 /* AudioTime */
2715                                 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2716                                         new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2717                                                                                 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2718                                 } else {
2719                                         /* prev_to_prev_t is irrelevant */
2720
2721                                         if (frame != prev_t->frame()) {
2722                                                 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2723                                         } else {
2724                                                 new_bpm = prev_t->beats_per_minute();
2725                                         }
2726                                 }
2727                         }
2728                 } else if (prev_t->c_func() < 0.0) {
2729                         if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2730                                 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
2731                         } else {
2732                                 /* prev_to_prev_t is irrelevant */
2733                                 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
2734                         }
2735
2736                         const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2737                         if (diff > -0.1 && diff  < 0.1) {
2738                                 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2739                                                                         / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2740                         }
2741
2742                 } else if (prev_t->c_func() > 0.0) {
2743                         if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2744                                 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
2745                         } else {
2746                                 /* prev_to_prev_t is irrelevant */
2747                                 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
2748                         }
2749
2750                         /* limits - a bit clunky, but meh */
2751                         const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2752                         if (diff > -0.1 && diff  < 0.1) {
2753                                 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2754                                                                         / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2755                         }
2756                 }
2757
2758                 prev_t->set_beats_per_minute (new_bpm);
2759                 recompute_tempos (future_map);
2760                 recompute_meters (future_map);
2761
2762                 if (check_solved (future_map, true)) {
2763
2764                         prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2765                         prev_t->set_beats_per_minute (new_bpm);
2766                         recompute_tempos (_metrics);
2767
2768                         if (ms->position_lock_style() == AudioTime) {
2769                                 ms->set_frame (frame);
2770                         }
2771
2772                         recompute_meters (_metrics);
2773                 }
2774         }
2775
2776         Metrics::const_iterator d = future_map.begin();
2777         while (d != future_map.end()) {
2778                 delete (*d);
2779                 ++d;
2780         }
2781
2782         MetricPositionChanged (); // Emit Signal
2783 }
2784
2785 void
2786 TempoMap::gui_dilate_tempo (const framepos_t& frame, const framepos_t& end_frame)
2787 {
2788         Metrics future_map;
2789         TempoSection* ts = 0;
2790
2791
2792         {
2793                 Glib::Threads::RWLock::WriterLock lm (lock);
2794                 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, frame - 1));
2795                 if (!ts) {
2796                         return;
2797                 }
2798                 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2799                 TempoSection* prev_to_prev_t = 0;
2800                 const frameoffset_t fr_off = end_frame - frame;
2801
2802                 if (prev_t && prev_t->pulse() > 0.0) {
2803                         prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2804                 }
2805
2806                 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2807                    constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2808                 */
2809                 double contribution = 0.0;
2810
2811                 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2812                         /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2813                         contribution = prev_to_prev_t->beats_per_minute() / (prev_to_prev_t->beats_per_minute() + prev_t->beats_per_minute());
2814                 }
2815
2816                 frameoffset_t frame_contribution = contribution * (double) fr_off;
2817                 frameoffset_t prev_t_frame_contribution = fr_off - frame_contribution;
2818
2819                 const double start_tempo = prev_t->tempo_at_frame (frame, _frame_rate);
2820                 const double end_tempo = prev_t->tempo_at_frame (frame + prev_t_frame_contribution, _frame_rate);
2821                 const double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2822                 const double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2823                 const double dpulse = (start_pulse) / (end_pulse);
2824                 double new_bpm = prev_t->tempo_at_frame (prev_t->frame() - prev_t_frame_contribution, _frame_rate) * (double) prev_t->note_type();
2825
2826                 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2827
2828                         if (prev_t->position_lock_style() == MusicTime) {
2829                                 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2830                                         new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
2831                                                                                 / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
2832
2833                                 } else {
2834                                         /* prev to prev is irrelevant */
2835
2836                                         if (start_pulse != prev_t->pulse()) {
2837                                                 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2838                                         } else {
2839                                                 new_bpm = prev_t->beats_per_minute();
2840                                         }
2841                                 }
2842                         } else {
2843                                 /* AudioTime */
2844                                 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2845                                         new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
2846                                                                                 / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
2847                                 } else {
2848                                         /* prev_to_prev_t is irrelevant */
2849
2850                                         if (end_frame != prev_t->frame()) {
2851                                                 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2852                                         } else {
2853                                                 new_bpm = prev_t->beats_per_minute();
2854                                         }
2855                                 }
2856                         }
2857                 } else if (prev_t->c_func() < 0.0) {
2858
2859                         const double end_minute = (((frame + prev_t_frame_contribution) - prev_t->frame()) / (double) _frame_rate) / 60.0;
2860                         const double future_c = log (end_tempo / (new_bpm / (double) prev_t->note_type())) / end_minute;
2861
2862                         const double dtempo = (start_tempo) / (end_tempo);
2863                         const double dframe = (frame) / (double) ((frame + prev_t_frame_contribution));
2864
2865                         /* limits - a bit clunky, but meh */
2866                         if (future_c > -10.1 && future_c  < 10.1) {
2867                                 new_bpm = prev_t->beats_per_minute() * (dpulse / dtempo);
2868                         } else {
2869                                 new_bpm = prev_t->beats_per_minute() * (dframe / dtempo);
2870                         }
2871
2872                 } else if (prev_t->c_func() > 0.0) {
2873
2874                         const double end_minute = (((frame + prev_t_frame_contribution) - prev_t->frame()) / (double) _frame_rate) / 60.0;
2875                         const double future_c = log (end_tempo / (new_bpm / (double) prev_t->note_type())) / end_minute;
2876
2877                         const double dtempo = (end_tempo) / (start_tempo);
2878                         const double dframe = (frame) / (double) ((frame + prev_t_frame_contribution));
2879
2880                         /* limits - a bit clunky, but meh */
2881                         if (future_c > -10.1 && future_c  < 10.1) {
2882                                 new_bpm = prev_t->beats_per_minute() * (dpulse / dtempo);
2883                         } else {
2884                                 new_bpm = prev_t->beats_per_minute() * (dframe / dtempo);
2885                         }
2886                 }
2887
2888                 prev_t->set_beats_per_minute (new_bpm);
2889                 recompute_tempos (future_map);
2890                 recompute_meters (future_map);
2891
2892                 if (check_solved (future_map, true)) {
2893
2894                         prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, frame - 1));
2895                         prev_t->set_beats_per_minute (new_bpm);
2896                         recompute_tempos (_metrics);
2897                         recompute_meters (_metrics);
2898                 }
2899         }
2900
2901         Metrics::const_iterator d = future_map.begin();
2902         while (d != future_map.end()) {
2903                 delete (*d);
2904                 ++d;
2905         }
2906
2907         MetricPositionChanged (); // Emit Signal
2908 }
2909
2910 framecnt_t
2911 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2912 {
2913         Glib::Threads::RWLock::ReaderLock lm (lock);
2914
2915         const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2916         const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2917         const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2918
2919         return frame_at_beat_locked (_metrics, total_beats);
2920 }
2921
2922 framepos_t
2923 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2924 {
2925         return round_to_type (fr, dir, Bar);
2926 }
2927
2928 framepos_t
2929 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2930 {
2931         return round_to_type (fr, dir, Beat);
2932 }
2933
2934 framepos_t
2935 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2936 {
2937         Glib::Threads::RWLock::ReaderLock lm (lock);
2938         uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2939         uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2940         uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2941
2942         ticks -= beats * BBT_Time::ticks_per_beat;
2943
2944         if (dir > 0) {
2945                 /* round to next (or same iff dir == RoundUpMaybe) */
2946
2947                 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2948
2949                 if (mod == 0 && dir == RoundUpMaybe) {
2950                         /* right on the subdivision, which is fine, so do nothing */
2951
2952                 } else if (mod == 0) {
2953                         /* right on the subdivision, so the difference is just the subdivision ticks */
2954                         ticks += ticks_one_subdivisions_worth;
2955
2956                 } else {
2957                         /* not on subdivision, compute distance to next subdivision */
2958
2959                         ticks += ticks_one_subdivisions_worth - mod;
2960                 }
2961
2962                 if (ticks >= BBT_Time::ticks_per_beat) {
2963                         ticks -= BBT_Time::ticks_per_beat;
2964                 }
2965         } else if (dir < 0) {
2966
2967                 /* round to previous (or same iff dir == RoundDownMaybe) */
2968
2969                 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2970
2971                 if (difference == 0 && dir == RoundDownAlways) {
2972                         /* right on the subdivision, but force-rounding down,
2973                            so the difference is just the subdivision ticks */
2974                         difference = ticks_one_subdivisions_worth;
2975                 }
2976
2977                 if (ticks < difference) {
2978                         ticks = BBT_Time::ticks_per_beat - ticks;
2979                 } else {
2980                         ticks -= difference;
2981                 }
2982
2983         } else {
2984                 /* round to nearest */
2985                 double rem;
2986
2987                 /* compute the distance to the previous and next subdivision */
2988
2989                 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2990
2991                         /* closer to the next subdivision, so shift forward */
2992
2993                         ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2994
2995                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2996
2997                         if (ticks > BBT_Time::ticks_per_beat) {
2998                                 ++beats;
2999                                 ticks -= BBT_Time::ticks_per_beat;
3000                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3001                         }
3002
3003                 } else if (rem > 0) {
3004
3005                         /* closer to previous subdivision, so shift backward */
3006
3007                         if (rem > ticks) {
3008                                 if (beats == 0) {
3009                                         /* can't go backwards past zero, so ... */
3010                                         return 0;
3011                                 }
3012                                 /* step back to previous beat */
3013                                 --beats;
3014                                 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3015                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3016                         } else {
3017                                 ticks = lrint (ticks - rem);
3018                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3019                         }
3020                 } else {
3021                         /* on the subdivision, do nothing */
3022                 }
3023         }
3024
3025         const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
3026
3027         return ret_frame;
3028 }
3029
3030 void
3031 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num, RoundMode dir)
3032 {
3033         if (sub_num == -1) {
3034                 if (dir > 0) {
3035                         ++when.bars;
3036                         when.beats = 1;
3037                         when.ticks = 0;
3038                 } else if (dir < 0) {
3039                         when.beats = 1;
3040                         when.ticks = 0;
3041                 } else {
3042                         const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
3043                         if ((double) when.beats > bpb / 2.0) {
3044                                 ++when.bars;
3045                         }
3046                         when.beats = 1;
3047                         when.ticks = 0;
3048                 }
3049
3050                 return;
3051
3052         } else if (sub_num == 0) {
3053                 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
3054                 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
3055                         ++when.beats;
3056                         while ((double) when.beats > bpb) {
3057                                 ++when.bars;
3058                                 when.beats -= (uint32_t) floor (bpb);
3059                         }
3060                 }
3061                 when.ticks = 0;
3062
3063                 return;
3064         }
3065
3066         const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
3067
3068         if (dir > 0) {
3069                 /* round to next (or same iff dir == RoundUpMaybe) */
3070
3071                 uint32_t mod = when.ticks % ticks_one_subdivisions_worth;
3072
3073                 if (mod == 0 && dir == RoundUpMaybe) {
3074                         /* right on the subdivision, which is fine, so do nothing */
3075
3076                 } else if (mod == 0) {
3077                         /* right on the subdivision, so the difference is just the subdivision ticks */
3078                         when.ticks += ticks_one_subdivisions_worth;
3079
3080                 } else {
3081                         /* not on subdivision, compute distance to next subdivision */
3082
3083                         when.ticks += ticks_one_subdivisions_worth - mod;
3084                 }
3085
3086                 if (when.ticks >= BBT_Time::ticks_per_beat) {
3087                         when.ticks -= BBT_Time::ticks_per_beat;
3088                 }
3089
3090         } else if (dir < 0) {
3091                 /* round to previous (or same iff dir == RoundDownMaybe) */
3092
3093                 uint32_t difference = when.ticks % ticks_one_subdivisions_worth;
3094
3095                 if (difference == 0 && dir == RoundDownAlways) {
3096                         /* right on the subdivision, but force-rounding down,
3097                            so the difference is just the subdivision ticks */
3098                         difference = ticks_one_subdivisions_worth;
3099                 }
3100
3101                 if (when.ticks < difference) {
3102                         when.ticks = BBT_Time::ticks_per_beat - when.ticks;
3103                 } else {
3104                         when.ticks -= difference;
3105                 }
3106
3107         } else {
3108                 /* round to nearest */  double rem;
3109                 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
3110                         /* closer to the next subdivision, so shift forward */
3111
3112                         when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
3113
3114                         if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
3115                                 ++when.beats;
3116                                 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
3117                         }
3118
3119                 } else if (rem > 0) {
3120                         /* closer to previous subdivision, so shift backward */
3121
3122                         if (rem > when.ticks) {
3123                                 if (when.beats == 0) {
3124                                         /* can't go backwards past zero, so ... */
3125                                 }
3126                                 /* step back to previous beat */
3127                                 --when.beats;
3128                                 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
3129                         } else {
3130                                 when.ticks = when.ticks - rem;
3131                         }
3132                 }
3133         }
3134 }
3135
3136 framepos_t
3137 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3138 {
3139         Glib::Threads::RWLock::ReaderLock lm (lock);
3140
3141         const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
3142         BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
3143
3144         switch (type) {
3145         case Bar:
3146                 if (dir < 0) {
3147                         /* find bar previous to 'frame' */
3148                         bbt.beats = 1;
3149                         bbt.ticks = 0;
3150                         return frame_time_locked (_metrics, bbt);
3151
3152                 } else if (dir > 0) {
3153                         /* find bar following 'frame' */
3154                         ++bbt.bars;
3155                         bbt.beats = 1;
3156                         bbt.ticks = 0;
3157                         return frame_time_locked (_metrics, bbt);
3158                 } else {
3159                         /* true rounding: find nearest bar */
3160                         framepos_t raw_ft = frame_time_locked (_metrics, bbt);
3161                         bbt.beats = 1;
3162                         bbt.ticks = 0;
3163                         framepos_t prev_ft = frame_time_locked (_metrics, bbt);
3164                         ++bbt.bars;
3165                         framepos_t next_ft = frame_time_locked (_metrics, bbt);
3166
3167                         if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) { 
3168                                 return next_ft;
3169                         } else {
3170                                 return prev_ft;
3171                         }
3172                 }
3173
3174                 break;
3175
3176         case Beat:
3177                 if (dir < 0) {
3178                         return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
3179                 } else if (dir > 0) {
3180                         return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
3181                 } else {
3182                         return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
3183                 }
3184                 break;
3185         }
3186
3187         return 0;
3188 }
3189
3190 void
3191 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3192                     framepos_t lower, framepos_t upper)
3193 {
3194         Glib::Threads::RWLock::ReaderLock lm (lock);
3195         int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3196         framecnt_t pos = 0;
3197         /* although the map handles negative beats, bbt doesn't. */
3198         if (cnt < 0.0) {
3199                 cnt = 0.0;
3200         }
3201         while (pos < upper) {
3202                 pos = frame_at_beat_locked (_metrics, cnt);
3203                 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
3204                 const MeterSection meter = meter_section_at_locked (_metrics, pos);
3205                 const BBT_Time bbt = beats_to_bbt (cnt);
3206                 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3207                 ++cnt;
3208         }
3209 }
3210
3211 const TempoSection&
3212 TempoMap::tempo_section_at (framepos_t frame) const
3213 {
3214         Glib::Threads::RWLock::ReaderLock lm (lock);
3215         return tempo_section_at_locked (_metrics, frame);
3216 }
3217
3218 const TempoSection&
3219 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
3220 {
3221         Metrics::const_iterator i;
3222         TempoSection* prev = 0;
3223
3224         for (i = metrics.begin(); i != metrics.end(); ++i) {
3225                 TempoSection* t;
3226
3227                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3228                         if (!t->active()) {
3229                                 continue;
3230                         }
3231                         if (prev && t->frame() > frame) {
3232                                 break;
3233                         }
3234
3235                         prev = t;
3236                 }
3237         }
3238
3239         if (prev == 0) {
3240                 fatal << endmsg;
3241                 abort(); /*NOTREACHED*/
3242         }
3243
3244         return *prev;
3245 }
3246
3247 const TempoSection&
3248 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3249 {
3250         TempoSection* prev_t = 0;
3251         const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3252
3253         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3254                 TempoSection* t;
3255                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3256                         if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3257                                 break;
3258                         }
3259                         prev_t = t;
3260                 }
3261
3262         }
3263         return *prev_t;
3264 }
3265
3266 const TempoSection&
3267 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
3268 {
3269         TempoSection* prev_t = 0;
3270
3271         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3272                 TempoSection* t;
3273                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3274                         if (prev_t && t->pulse() > pulse) {
3275                                 break;
3276                         }
3277                         prev_t = t;
3278                 }
3279
3280         }
3281         return *prev_t;
3282 }
3283
3284 /* don't use this to calculate length (the tempo is only correct for this frame).
3285    do that stuff based on the beat_at_frame and frame_at_beat api
3286 */
3287 double
3288 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3289 {
3290         Glib::Threads::RWLock::ReaderLock lm (lock);
3291
3292         const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
3293         const TempoSection* ts_after = 0;
3294         Metrics::const_iterator i;
3295
3296         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3297                 TempoSection* t;
3298
3299                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3300                         if (!t->active()) {
3301                                 continue;
3302                         }
3303                         if ((*i)->frame() > frame) {
3304                                 ts_after = t;
3305                                 break;
3306                         }
3307                 }
3308         }
3309
3310         if (ts_after) {
3311                 return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
3312         }
3313         /* must be treated as constant tempo */
3314         return ts_at->frames_per_beat (_frame_rate);
3315 }
3316
3317 const Tempo
3318 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
3319 {
3320         TempoSection* prev_t = 0;
3321
3322         Metrics::const_iterator i;
3323
3324         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3325                 TempoSection* t;
3326                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3327                         if (!t->active()) {
3328                                 continue;
3329                         }
3330                         if ((prev_t) && t->frame() > frame) {
3331                                 /* t is the section past frame */
3332                                 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
3333                                 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
3334                                 return ret_tempo;
3335                         }
3336                         prev_t = t;
3337                 }
3338         }
3339
3340         const double ret = prev_t->beats_per_minute();
3341         const Tempo ret_tempo (ret, prev_t->note_type ());
3342
3343         return ret_tempo;
3344 }
3345
3346 const Tempo
3347 TempoMap::tempo_at (const framepos_t& frame) const
3348 {
3349         Glib::Threads::RWLock::ReaderLock lm (lock);
3350         return tempo_at_locked (_metrics, frame);
3351 }
3352
3353 const MeterSection&
3354 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
3355 {
3356         Metrics::const_iterator i;
3357         MeterSection* prev = 0;
3358
3359         for (i = metrics.begin(); i != metrics.end(); ++i) {
3360                 MeterSection* m;
3361
3362                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3363
3364                         if (prev && (*i)->frame() > frame) {
3365                                 break;
3366                         }
3367
3368                         prev = m;
3369                 }
3370         }
3371
3372         if (prev == 0) {
3373                 fatal << endmsg;
3374                 abort(); /*NOTREACHED*/
3375         }
3376
3377         return *prev;
3378 }
3379
3380
3381 const MeterSection&
3382 TempoMap::meter_section_at (framepos_t frame) const
3383 {
3384         Glib::Threads::RWLock::ReaderLock lm (lock);
3385         return meter_section_at_locked (_metrics, frame);
3386 }
3387
3388 const MeterSection&
3389 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3390 {
3391         MeterSection* prev_m = 0;
3392
3393         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3394                 MeterSection* m;
3395                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3396                         if (prev_m && m->beat() > beat) {
3397                                 break;
3398                         }
3399                         prev_m = m;
3400                 }
3401
3402         }
3403         return *prev_m;
3404 }
3405
3406 const MeterSection&
3407 TempoMap::meter_section_at_beat (double beat) const
3408 {
3409         Glib::Threads::RWLock::ReaderLock lm (lock);
3410         return meter_section_at_beat_locked (_metrics, beat);
3411 }
3412
3413 const Meter&
3414 TempoMap::meter_at (framepos_t frame) const
3415 {
3416         TempoMetric m (metric_at (frame));
3417         return m.meter();
3418 }
3419
3420 void
3421 TempoMap::fix_legacy_session ()
3422 {
3423         MeterSection* prev_m = 0;
3424         TempoSection* prev_t = 0;
3425
3426         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3427                 MeterSection* m;
3428                 TempoSection* t;
3429
3430                 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3431                         if (!m->movable()) {
3432                                 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3433                                 m->set_beat (bbt);
3434                                 m->set_pulse (0.0);
3435                                 m->set_frame (0);
3436                                 m->set_position_lock_style (AudioTime);
3437                                 prev_m = m;
3438                                 continue;
3439                         }
3440                         if (prev_m) {
3441                                 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3442                                                                           + (m->bbt().beats - 1)
3443                                                                           + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3444                                                                           , m->bbt());
3445                                 m->set_beat (start);
3446                                 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3447                                         + (m->bbt().beats - 1)
3448                                         + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3449                                 m->set_pulse (start_beat / prev_m->note_divisor());
3450                         }
3451                         prev_m = m;
3452                 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3453
3454                         if (!t->active()) {
3455                                 continue;
3456                         }
3457
3458                         if (!t->movable()) {
3459                                 t->set_pulse (0.0);
3460                                 t->set_frame (0);
3461                                 t->set_position_lock_style (AudioTime);
3462                                 prev_t = t;
3463                                 continue;
3464                         }
3465
3466                         if (prev_t) {
3467                                 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3468                                         + (t->legacy_bbt().beats - 1)
3469                                         + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3470                                 if (prev_m) {
3471                                         t->set_pulse (beat / prev_m->note_divisor());
3472                                 } else {
3473                                         /* really shouldn't happen but.. */
3474                                         t->set_pulse (beat / 4.0);
3475                                 }
3476                         }
3477                         prev_t = t;
3478                 }
3479         }
3480 }
3481
3482 XMLNode&
3483 TempoMap::get_state ()
3484 {
3485         Metrics::const_iterator i;
3486         XMLNode *root = new XMLNode ("TempoMap");
3487
3488         {
3489                 Glib::Threads::RWLock::ReaderLock lm (lock);
3490                 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3491                         root->add_child_nocopy ((*i)->get_state());
3492                 }
3493         }
3494
3495         return *root;
3496 }
3497
3498 int
3499 TempoMap::set_state (const XMLNode& node, int /*version*/)
3500 {
3501         {
3502                 Glib::Threads::RWLock::WriterLock lm (lock);
3503
3504                 XMLNodeList nlist;
3505                 XMLNodeConstIterator niter;
3506                 Metrics old_metrics (_metrics);
3507                 _metrics.clear();
3508
3509                 nlist = node.children();
3510
3511                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3512                         XMLNode* child = *niter;
3513
3514                         if (child->name() == TempoSection::xml_state_node_name) {
3515
3516                                 try {
3517                                         TempoSection* ts = new TempoSection (*child);
3518                                         _metrics.push_back (ts);
3519                                 }
3520
3521                                 catch (failed_constructor& err){
3522                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3523                                         _metrics = old_metrics;
3524                                         break;
3525                                 }
3526
3527                         } else if (child->name() == MeterSection::xml_state_node_name) {
3528
3529                                 try {
3530                                         MeterSection* ms = new MeterSection (*child);
3531                                         _metrics.push_back (ms);
3532                                 }
3533
3534                                 catch (failed_constructor& err) {
3535                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3536                                         _metrics = old_metrics;
3537                                         break;
3538                                 }
3539                         }
3540                 }
3541
3542                 if (niter == nlist.end()) {
3543                         MetricSectionSorter cmp;
3544                         _metrics.sort (cmp);
3545                 }
3546
3547                 /* check for legacy sessions where bbt was the base musical unit for tempo */
3548                 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3549                         TempoSection* t;
3550                         if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3551                                 if (t->legacy_bbt().bars != 0) {
3552                                         fix_legacy_session();
3553                                         break;
3554                                 }
3555                                 break;
3556                         }
3557                 }
3558
3559                 /* check for multiple tempo/meters at the same location, which
3560                    ardour2 somehow allowed.
3561                 */
3562
3563                 Metrics::iterator prev = _metrics.end();
3564                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3565                         if (prev != _metrics.end()) {
3566                                 MeterSection* ms;
3567                                 MeterSection* prev_m;
3568                                 TempoSection* ts;
3569                                 TempoSection* prev_t;
3570                                 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3571                                         if (prev_m->pulse() == ms->pulse()) {
3572                                                 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3573                                                 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3574                                                 return -1;
3575                                         }
3576                                 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3577                                         if (prev_t->pulse() == ts->pulse()) {
3578                                                 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3579                                                 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3580                                                 return -1;
3581                                         }
3582                                 }
3583                         }
3584                         prev = i;
3585                 }
3586
3587                 recompute_map (_metrics);
3588         }
3589
3590         PropertyChanged (PropertyChange ());
3591
3592         return 0;
3593 }
3594
3595 void
3596 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3597 {
3598         Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3599         const MeterSection* m;
3600         const TempoSection* t;
3601         const TempoSection* prev_t = 0;
3602
3603         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3604
3605                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3606                         o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3607                           << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3608                         o << "current      : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3609                         if (prev_t) {
3610                                 o << "previous     : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3611                                 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;
3612                         }
3613                         prev_t = t;
3614                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3615                         o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3616                           << " pulse: " << m->pulse() <<  " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3617                 }
3618         }
3619         o << "------" << std::endl;
3620 }
3621
3622 int
3623 TempoMap::n_tempos() const
3624 {
3625         Glib::Threads::RWLock::ReaderLock lm (lock);
3626         int cnt = 0;
3627
3628         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3629                 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3630                         cnt++;
3631                 }
3632         }
3633
3634         return cnt;
3635 }
3636
3637 int
3638 TempoMap::n_meters() const
3639 {
3640         Glib::Threads::RWLock::ReaderLock lm (lock);
3641         int cnt = 0;
3642
3643         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3644                 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3645                         cnt++;
3646                 }
3647         }
3648
3649         return cnt;
3650 }
3651
3652 void
3653 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3654 {
3655         {
3656                 Glib::Threads::RWLock::WriterLock lm (lock);
3657                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3658                         if ((*i)->frame() >= where && (*i)->movable ()) {
3659                                 (*i)->set_frame ((*i)->frame() + amount);
3660                         }
3661                 }
3662
3663                 /* now reset the BBT time of all metrics, based on their new
3664                  * audio time. This is the only place where we do this reverse
3665                  * timestamp.
3666                  */
3667
3668                 Metrics::iterator i;
3669                 const MeterSection* meter;
3670                 const TempoSection* tempo;
3671                 MeterSection *m;
3672                 TempoSection *t;
3673
3674                 meter = &first_meter ();
3675                 tempo = &first_tempo ();
3676
3677                 BBT_Time start;
3678                 BBT_Time end;
3679
3680                 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3681
3682                 bool first = true;
3683                 MetricSection* prev = 0;
3684
3685                 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3686
3687                         BBT_Time bbt;
3688                         //TempoMetric metric (*meter, *tempo);
3689                         MeterSection* ms = const_cast<MeterSection*>(meter);
3690                         TempoSection* ts = const_cast<TempoSection*>(tempo);
3691                         if (prev) {
3692                                 if (ts){
3693                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3694                                                 if (!t->active()) {
3695                                                         continue;
3696                                                 }
3697                                                 ts->set_pulse (t->pulse());
3698                                         }
3699                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3700                                                 ts->set_pulse (m->pulse());
3701                                         }
3702                                         ts->set_frame (prev->frame());
3703
3704                                 }
3705                                 if (ms) {
3706                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3707                                                 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3708                                                 ms->set_beat (start);
3709                                                 ms->set_pulse (m->pulse());
3710                                         }
3711                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3712                                                 if (!t->active()) {
3713                                                         continue;
3714                                                 }
3715                                                 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3716                                                 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3717                                                 ms->set_beat (start);
3718                                                 ms->set_pulse (t->pulse());
3719                                         }
3720                                         ms->set_frame (prev->frame());
3721                                 }
3722
3723                         } else {
3724                                 // metric will be at frames=0 bbt=1|1|0 by default
3725                                 // which is correct for our purpose
3726                         }
3727
3728                         // cerr << bbt << endl;
3729
3730                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3731                                 if (!t->active()) {
3732                                         continue;
3733                                 }
3734                                 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3735                                 tempo = t;
3736                                 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3737                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3738                                 bbt_time (m->frame(), bbt);
3739
3740                                 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3741
3742                                 if (first) {
3743                                         first = false;
3744                                 } else {
3745
3746                                         if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3747                                                 /* round up to next beat */
3748                                                 bbt.beats += 1;
3749                                         }
3750
3751                                         bbt.ticks = 0;
3752
3753                                         if (bbt.beats != 1) {
3754                                                 /* round up to next bar */
3755                                                 bbt.bars += 1;
3756                                                 bbt.beats = 1;
3757                                         }
3758                                 }
3759                                 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3760                                 m->set_beat (start);
3761                                 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3762                                 meter = m;
3763                                 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3764                         } else {
3765                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3766                                 abort(); /*NOTREACHED*/
3767                         }
3768
3769                         prev = (*i);
3770                 }
3771
3772                 recompute_map (_metrics);
3773         }
3774
3775
3776         PropertyChanged (PropertyChange ());
3777 }
3778 bool
3779 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3780 {
3781         bool moved = false;
3782
3783         std::list<MetricSection*> metric_kill_list;
3784
3785         TempoSection* last_tempo = NULL;
3786         MeterSection* last_meter = NULL;
3787         bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3788         bool meter_after = false; // is there a meter marker likewise?
3789         {
3790                 Glib::Threads::RWLock::WriterLock lm (lock);
3791                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3792                         if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3793                                 metric_kill_list.push_back(*i);
3794                                 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3795                                 if (lt)
3796                                         last_tempo = lt;
3797                                 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3798                                 if (lm)
3799                                         last_meter = lm;
3800                         }
3801                         else if ((*i)->frame() >= where) {
3802                                 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3803                                 (*i)->set_frame ((*i)->frame() - amount);
3804                                 if ((*i)->frame() == where) {
3805                                         // marker was immediately after end of range
3806                                         tempo_after = dynamic_cast<TempoSection*> (*i);
3807                                         meter_after = dynamic_cast<MeterSection*> (*i);
3808                                 }
3809                                 moved = true;
3810                         }
3811                 }
3812
3813                 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3814                 if (last_tempo && !tempo_after) {
3815                         metric_kill_list.remove(last_tempo);
3816                         last_tempo->set_frame(where);
3817                         moved = true;
3818                 }
3819                 if (last_meter && !meter_after) {
3820                         metric_kill_list.remove(last_meter);
3821                         last_meter->set_frame(where);
3822                         moved = true;
3823                 }
3824
3825                 //remove all the remaining metrics
3826                 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3827                         _metrics.remove(*i);
3828                         moved = true;
3829                 }
3830
3831                 if (moved) {
3832                         recompute_map (_metrics);
3833                 }
3834         }
3835         PropertyChanged (PropertyChange ());
3836         return moved;
3837 }
3838
3839 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3840  *  pos can be -ve, if required.
3841  */
3842 framepos_t
3843 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3844 {
3845         return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3846 }
3847
3848 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3849 framepos_t
3850 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3851 {
3852         return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3853 }
3854
3855 /** Add the BBT interval op to pos and return the result */
3856 framepos_t
3857 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3858 {
3859         Glib::Threads::RWLock::ReaderLock lm (lock);
3860
3861         BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3862         pos_bbt.ticks += op.ticks;
3863         if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3864                 ++pos_bbt.beats;
3865                 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3866         }
3867         pos_bbt.beats += op.beats;
3868         /* the meter in effect will start on the bar */
3869         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();
3870         while (pos_bbt.beats >= divisions_per_bar + 1) {
3871                 ++pos_bbt.bars;
3872                 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3873                 pos_bbt.beats -= divisions_per_bar;
3874         }
3875         pos_bbt.bars += op.bars;
3876
3877         return frame_time_locked (_metrics, pos_bbt);
3878 }
3879
3880 /** Count the number of beats that are equivalent to distance when going forward,
3881     starting at pos.
3882 */
3883 Evoral::Beats
3884 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3885 {
3886         return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3887 }
3888
3889 struct bbtcmp {
3890     bool operator() (const BBT_Time& a, const BBT_Time& b) {
3891             return a < b;
3892     }
3893 };
3894
3895 std::ostream&
3896 operator<< (std::ostream& o, const Meter& m) {
3897         return o << m.divisions_per_bar() << '/' << m.note_divisor();
3898 }
3899
3900 std::ostream&
3901 operator<< (std::ostream& o, const Tempo& t) {
3902         return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3903 }
3904
3905 std::ostream&
3906 operator<< (std::ostream& o, const MetricSection& section) {
3907
3908         o << "MetricSection @ " << section.frame() << ' ';
3909
3910         const TempoSection* ts;
3911         const MeterSection* ms;
3912
3913         if ((ts = dynamic_cast<const TempoSection*> (&section)) != 0) {
3914                 o << *((const Tempo*) ts);
3915         } else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
3916                 o << *((const Meter*) ms);
3917         }
3918
3919         return o;
3920 }