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