Tempo ramps - one approach to audio-locked meters.
[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                                         if (floor_beats + prev_m->beat() < meter->beat()) {
1292                                                 /* tempo change caused a change in beat (bar). */
1293                                                 b_bbt = make_pair (floor_beats + prev_m->beat(), BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1294                                                 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
1295                                                 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
1296                                                 pulse = true_pulse - pulse_off;
1297                                         } else {
1298                                                 b_bbt = make_pair (meter->beat(), meter->bbt());
1299                                                 pulse = pulse_at_frame_locked (metrics, meter->frame());
1300                                         }
1301                                 } else {
1302                                         b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1303                                 }
1304                                 meter->set_beat (b_bbt);
1305                                 meter->set_pulse (pulse);
1306                         } else {
1307                                 double pulse = 0.0;
1308                                 pair<double, BBT_Time> new_beat;
1309                                 if (prev_m) {
1310                                         pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) *  prev_m->divisions_per_bar() / prev_m->note_divisor());
1311                                         new_beat = make_pair (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
1312                                 } else {
1313                                         /* shouldn't happen - the first is audio-locked */
1314                                         pulse = pulse_at_beat_locked (metrics, meter->beat());
1315                                         new_beat = make_pair (pulse, meter->bbt());
1316                                 }
1317
1318                                 meter->set_beat (new_beat);
1319                                 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1320                                 meter->set_pulse (pulse);
1321                         }
1322
1323                         prev_m = meter;
1324                 }
1325         }
1326         //dump (_metrics, std::cerr;
1327 }
1328
1329 void
1330 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1331 {
1332         /* CALLER MUST HOLD WRITE LOCK */
1333
1334         if (end < 0) {
1335
1336                 /* we will actually stop once we hit
1337                    the last metric.
1338                 */
1339                 end = max_framepos;
1340
1341         }
1342
1343         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1344
1345         if (end == 0) {
1346                 /* silly call from Session::process() during startup
1347                  */
1348                 return;
1349         }
1350
1351         recompute_tempos (metrics);
1352         recompute_meters (metrics);
1353 }
1354
1355 TempoMetric
1356 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1357 {
1358         Glib::Threads::RWLock::ReaderLock lm (lock);
1359         TempoMetric m (first_meter(), first_tempo());
1360
1361         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1362            at something, because we insert the default tempo and meter during
1363            TempoMap construction.
1364
1365            now see if we can find better candidates.
1366         */
1367
1368         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1369
1370                 if ((*i)->frame() > frame) {
1371                         break;
1372                 }
1373
1374                 m.set_metric(*i);
1375
1376                 if (last) {
1377                         *last = i;
1378                 }
1379         }
1380
1381         return m;
1382 }
1383
1384 /* XX meters only */
1385 TempoMetric
1386 TempoMap::metric_at (BBT_Time bbt) const
1387 {
1388         Glib::Threads::RWLock::ReaderLock lm (lock);
1389         TempoMetric m (first_meter(), first_tempo());
1390
1391         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1392            at something, because we insert the default tempo and meter during
1393            TempoMap construction.
1394
1395            now see if we can find better candidates.
1396         */
1397
1398         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1399                 MeterSection* mw;
1400                 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1401                         BBT_Time section_start (mw->bbt());
1402
1403                         if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1404                                 break;
1405                         }
1406
1407                         m.set_metric (*i);
1408                 }
1409         }
1410
1411         return m;
1412 }
1413
1414 double
1415 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1416 {
1417         MeterSection* prev_m = 0;
1418
1419         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1420                 MeterSection* m;
1421                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1422                         if (prev_m && m->beat() > beat) {
1423                                 break;
1424                         }
1425                         prev_m = m;
1426                 }
1427
1428         }
1429         double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1430         return ret;
1431 }
1432
1433 double
1434 TempoMap::pulse_at_beat (const double& beat) const
1435 {
1436         Glib::Threads::RWLock::ReaderLock lm (lock);
1437         return pulse_at_beat_locked (_metrics, beat);
1438 }
1439
1440 double
1441 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1442 {
1443         MeterSection* prev_m = 0;
1444
1445         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1446                 MeterSection* m;
1447                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1448                         if (prev_m && m->pulse() > pulse) {
1449                                 break;
1450                         }
1451                         prev_m = m;
1452                 }
1453         }
1454
1455         double const beats_in_section = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1456
1457         return beats_in_section + prev_m->beat();
1458 }
1459
1460 double
1461 TempoMap::beat_at_pulse (const double& pulse) const
1462 {
1463         Glib::Threads::RWLock::ReaderLock lm (lock);
1464         return beat_at_pulse_locked (_metrics, pulse);
1465 }
1466
1467 double
1468 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1469 {
1470         /* HOLD (at least) THE READER LOCK */
1471         TempoSection* prev_t = 0;
1472
1473         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1474                 TempoSection* t;
1475                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1476                         if (!t->active()) {
1477                                 continue;
1478                         }
1479                         if (prev_t && t->frame() > frame) {
1480                                 /*the previous ts is the one containing the frame */
1481                                 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1482                                 return ret;
1483                         }
1484                         prev_t = t;
1485                 }
1486         }
1487
1488         /* treated as constant for this ts */
1489         const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1490
1491         return pulses_in_section + prev_t->pulse();
1492 }
1493
1494 double
1495 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1496 {
1497         Glib::Threads::RWLock::ReaderLock lm (lock);
1498         return pulse_at_frame_locked (_metrics, frame);
1499 }
1500
1501 framecnt_t
1502 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1503 {
1504         /* HOLD THE READER LOCK */
1505
1506         const TempoSection* prev_t = 0;
1507
1508         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1509                 TempoSection* t;
1510
1511                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1512                         if (!t->active()) {
1513                                 continue;
1514                         }
1515                         if (prev_t && t->pulse() > pulse) {
1516                                 return prev_t->frame_at_pulse (pulse, _frame_rate);
1517                         }
1518
1519                         prev_t = t;
1520                 }
1521         }
1522         /* must be treated as constant, irrespective of _type */
1523         double const pulses_in_section = pulse - prev_t->pulse();
1524         double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1525
1526         framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1527
1528         return ret;
1529 }
1530
1531 framecnt_t
1532 TempoMap::frame_at_pulse (const double& pulse) const
1533 {
1534         Glib::Threads::RWLock::ReaderLock lm (lock);
1535         return frame_at_pulse_locked (_metrics, pulse);
1536 }
1537
1538 double
1539 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1540 {
1541         const double pulse = pulse_at_frame_locked (metrics, frame);
1542         return beat_at_pulse_locked (metrics, pulse);
1543 }
1544
1545 double
1546 TempoMap::beat_at_frame (const framecnt_t& frame) const
1547 {
1548         Glib::Threads::RWLock::ReaderLock lm (lock);
1549         return beat_at_frame_locked (_metrics, frame);
1550 }
1551
1552 framecnt_t
1553 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1554 {
1555         const framecnt_t frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1556         return frame;
1557 }
1558
1559 framecnt_t
1560 TempoMap::frame_at_beat (const double& beat) const
1561 {
1562         Glib::Threads::RWLock::ReaderLock lm (lock);
1563         return frame_at_beat_locked (_metrics, beat);
1564 }
1565
1566 double
1567 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1568 {
1569         /* CALLER HOLDS READ LOCK */
1570
1571         MeterSection* prev_m = 0;
1572
1573         /* because audio-locked meters have 'fake' integral beats,
1574            there is no pulse offset here.
1575         */
1576         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1577                 MeterSection* m;
1578                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1579                         if (prev_m) {
1580                                 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1581                                 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1582                                         break;
1583                                 }
1584                         }
1585                         prev_m = m;
1586                 }
1587         }
1588
1589         const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1590         const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1591         const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1592
1593         return ret;
1594 }
1595
1596 double
1597 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1598 {
1599         Glib::Threads::RWLock::ReaderLock lm (lock);
1600         return bbt_to_beats_locked (_metrics, bbt);
1601 }
1602
1603 Timecode::BBT_Time
1604 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1605 {
1606         /* CALLER HOLDS READ LOCK */
1607         MeterSection* prev_m = 0;
1608         const double beats = (b < 0.0) ? 0.0 : b;
1609
1610         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1611                 MeterSection* m = 0;
1612
1613                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1614                         if (prev_m) {
1615                                 if (m->beat() > beats) {
1616                                         /* this is the meter after the one our beat is on*/
1617                                         break;
1618                                 }
1619                         }
1620
1621                         prev_m = m;
1622                 }
1623         }
1624
1625         const double beats_in_ms = beats - prev_m->beat();
1626         const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1627         const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1628         const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1629         const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1630
1631         BBT_Time ret;
1632
1633         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1634         ret.beats = (uint32_t) floor (remaining_beats);
1635         ret.bars = total_bars;
1636
1637         /* 0 0 0 to 1 1 0 - based mapping*/
1638         ++ret.bars;
1639         ++ret.beats;
1640
1641         if (ret.ticks >= BBT_Time::ticks_per_beat) {
1642                 ++ret.beats;
1643                 ret.ticks -= BBT_Time::ticks_per_beat;
1644         }
1645
1646         if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1647                 ++ret.bars;
1648                 ret.beats = 1;
1649         }
1650
1651         return ret;
1652 }
1653
1654 Timecode::BBT_Time
1655 TempoMap::beats_to_bbt (const double& beats)
1656 {
1657         Glib::Threads::RWLock::ReaderLock lm (lock);
1658         return beats_to_bbt_locked (_metrics, beats);
1659 }
1660
1661 Timecode::BBT_Time
1662 TempoMap::pulse_to_bbt (const double& pulse)
1663 {
1664         Glib::Threads::RWLock::ReaderLock lm (lock);
1665         MeterSection* prev_m = 0;
1666
1667         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1668                 MeterSection* m = 0;
1669
1670                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1671
1672                         if (prev_m) {
1673                                 double const pulses_to_m = m->pulse() - prev_m->pulse();
1674                                 if (prev_m->pulse() + pulses_to_m > pulse) {
1675                                         /* this is the meter after the one our beat is on*/
1676                                         break;
1677                                 }
1678                         }
1679
1680                         prev_m = m;
1681                 }
1682         }
1683
1684         const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1685         const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1686         const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1687         const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1688         const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1689
1690         BBT_Time ret;
1691
1692         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1693         ret.beats = (uint32_t) floor (remaining_beats);
1694         ret.bars = total_bars;
1695
1696         /* 0 0 0 to 1 1 0 mapping*/
1697         ++ret.bars;
1698         ++ret.beats;
1699
1700         if (ret.ticks >= BBT_Time::ticks_per_beat) {
1701                 ++ret.beats;
1702                 ret.ticks -= BBT_Time::ticks_per_beat;
1703         }
1704
1705         if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1706                 ++ret.bars;
1707                 ret.beats = 1;
1708         }
1709
1710         return ret;
1711 }
1712
1713 void
1714 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1715 {
1716
1717         if (frame < 0) {
1718                 bbt.bars = 1;
1719                 bbt.beats = 1;
1720                 bbt.ticks = 0;
1721                 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1722                 return;
1723         }
1724         Glib::Threads::RWLock::ReaderLock lm (lock);
1725         const double beat = beat_at_frame_locked (_metrics, frame);
1726
1727         bbt = beats_to_bbt_locked (_metrics, beat);
1728 }
1729
1730 framepos_t
1731 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1732 {
1733         /* HOLD THE READER LOCK */
1734
1735         const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1736         return ret;
1737 }
1738
1739 framepos_t
1740 TempoMap::frame_time (const BBT_Time& bbt)
1741 {
1742         if (bbt.bars < 1) {
1743                 warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
1744                 return 0;
1745         }
1746
1747         if (bbt.beats < 1) {
1748                 throw std::logic_error ("beats are counted from one");
1749         }
1750         Glib::Threads::RWLock::ReaderLock lm (lock);
1751
1752         return frame_time_locked (_metrics, bbt);
1753 }
1754
1755 bool
1756 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1757 {
1758         TempoSection* prev_t = 0;
1759
1760         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1761                 TempoSection* t;
1762                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1763                         if (!t->active()) {
1764                                 continue;
1765                         }
1766                         if (prev_t) {
1767                                 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1768                                         return false;
1769                                 }
1770
1771                                 if (t->frame() == prev_t->frame()) {
1772                                         return false;
1773                                 }
1774
1775                                 /* precision check ensures pulses and frames align.*/
1776                                 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1777                                         return false;
1778                                 }
1779                         }
1780                         prev_t = t;
1781                 }
1782         }
1783
1784         return true;
1785 }
1786
1787 bool
1788 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1789 {
1790         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1791                 TempoSection* t;
1792                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1793                         if (!t->movable()) {
1794                                 t->set_active (true);
1795                                 continue;
1796                         }
1797                         if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1798                                 t->set_active (false);
1799                                 t->set_pulse (0.0);
1800                         } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1801                                 t->set_active (true);
1802                         } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1803                                 return false;
1804                         }
1805                 }
1806         }
1807         return true;
1808 }
1809
1810 bool
1811 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1812 {
1813         TempoSection* prev_t = 0;
1814         TempoSection* section_prev = 0;
1815         framepos_t first_m_frame = 0;
1816
1817         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1818                 MeterSection* m;
1819                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1820                         if (!m->movable()) {
1821                                 first_m_frame = m->frame();
1822                                 break;
1823                         }
1824                 }
1825         }
1826         if (section->movable() && frame <= first_m_frame) {
1827                 return false;
1828         }
1829
1830         section->set_active (true);
1831         section->set_frame (frame);
1832
1833         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1834                 TempoSection* t;
1835                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1836
1837                         if (!t->active()) {
1838                                 continue;
1839                         }
1840                         if (prev_t) {
1841                                 if (t == section) {
1842                                         section_prev = prev_t;
1843                                         continue;
1844                                 }
1845                                 if (t->position_lock_style() == MusicTime) {
1846                                         prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1847                                         t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1848                                 } else {
1849                                         prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1850                                         t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1851                                 }
1852                         }
1853                         prev_t = t;
1854                 }
1855         }
1856
1857         if (section_prev) {
1858                 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1859                 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1860         }
1861
1862         if (section->position_lock_style() == MusicTime) {
1863                 /* we're setting the frame */
1864                 section->set_position_lock_style (AudioTime);
1865                 recompute_tempos (imaginary);
1866                 section->set_position_lock_style (MusicTime);
1867         } else {
1868                 recompute_tempos (imaginary);
1869         }
1870
1871         if (check_solved (imaginary, true)) {
1872                 recompute_meters (imaginary);
1873                 return true;
1874         }
1875
1876         MetricSectionFrameSorter fcmp;
1877         imaginary.sort (fcmp);
1878         if (section->position_lock_style() == MusicTime) {
1879                 /* we're setting the frame */
1880                 section->set_position_lock_style (AudioTime);
1881                 recompute_tempos (imaginary);
1882                 section->set_position_lock_style (MusicTime);
1883         } else {
1884                 recompute_tempos (imaginary);
1885         }
1886
1887         if (check_solved (imaginary, true)) {
1888                 recompute_meters (imaginary);
1889                 return true;
1890         }
1891 #if (0)
1892         MetricSectionSorter cmp;
1893         imaginary.sort (cmp);
1894         if (section->position_lock_style() == MusicTime) {
1895                 /* we're setting the frame */
1896                 section->set_position_lock_style (AudioTime);
1897                 recompute_tempos (imaginary);
1898                 section->set_position_lock_style (MusicTime);
1899         } else {
1900                 recompute_tempos (imaginary);
1901         }
1902
1903         if (check_solved (imaginary, true)) {
1904                 recompute_meters (imaginary);
1905                 return true;
1906         }
1907 #endif
1908         //dump (imaginary, std::cerr);
1909
1910         return false;
1911 }
1912
1913 bool
1914 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
1915 {
1916         TempoSection* prev_t = 0;
1917         TempoSection* section_prev = 0;
1918
1919         section->set_pulse (pulse);
1920
1921         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1922                 TempoSection* t;
1923                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1924                         if (!t->active()) {
1925                                 continue;
1926                         }
1927                         if (!t->movable()) {
1928                                 t->set_pulse (0.0);
1929                                 prev_t = t;
1930                                 continue;
1931                         }
1932                         if (prev_t) {
1933                                 if (t == section) {
1934                                         section_prev = prev_t;
1935                                         continue;
1936                                 }
1937                                 if (t->position_lock_style() == MusicTime) {
1938                                         prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1939                                         t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1940                                 } else {
1941                                         prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1942                                         t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1943                                 }
1944                         }
1945                         prev_t = t;
1946                 }
1947         }
1948         if (section_prev) {
1949                 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1950                 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1951         }
1952
1953         if (section->position_lock_style() == AudioTime) {
1954                 /* we're setting the pulse */
1955                 section->set_position_lock_style (MusicTime);
1956                 recompute_tempos (imaginary);
1957                 section->set_position_lock_style (AudioTime);
1958         } else {
1959                 recompute_tempos (imaginary);
1960         }
1961
1962         if (check_solved (imaginary, false)) {
1963                 recompute_meters (imaginary);
1964                 return true;
1965         }
1966
1967         MetricSectionSorter cmp;
1968         imaginary.sort (cmp);
1969         if (section->position_lock_style() == AudioTime) {
1970                 /* we're setting the pulse */
1971                 section->set_position_lock_style (MusicTime);
1972                 recompute_tempos (imaginary);
1973                 section->set_position_lock_style (AudioTime);
1974         } else {
1975                 recompute_tempos (imaginary);
1976         }
1977
1978         if (check_solved (imaginary, false)) {
1979                 recompute_meters (imaginary);
1980                 return true;
1981         }
1982 #if (0)
1983         MetricSectionFrameSorter fcmp;
1984         imaginary.sort (fcmp);
1985         if (section->position_lock_style() == AudioTime) {
1986                 /* we're setting the pulse */
1987                 section->set_position_lock_style (MusicTime);
1988                 recompute_tempos (imaginary);
1989                 section->set_position_lock_style (AudioTime);
1990         } else {
1991                 recompute_tempos (imaginary);
1992         }
1993
1994         if (check_solved (imaginary, false)) {
1995                 recompute_meters (imaginary);
1996                 return true;
1997         }
1998 #endif
1999         //dump (imaginary, std::cerr);
2000
2001         return false;
2002 }
2003
2004 void
2005 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2006 {
2007         /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2008         const MeterSection* other =  &meter_section_at_locked (frame);
2009         if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2010                 return;
2011         }
2012         MeterSection* prev_m = 0;
2013
2014         if (!section->movable()) {
2015                 /* lock the first tempo to our first meter */
2016                 if (!set_active_tempos (imaginary, frame)) {
2017                         return;
2018                 }
2019                 TempoSection* first_t = &first_tempo();
2020                 Metrics future_map;
2021                 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
2022
2023                 new_section->set_frame (frame);
2024                 new_section->set_pulse (0.0);
2025                 new_section->set_active (true);
2026
2027                 if (solve_map (future_map, new_section, frame)) {
2028                         first_t->set_frame (frame);
2029                         first_t->set_pulse (0.0);
2030                         first_t->set_active (true);
2031                         solve_map (imaginary, first_t, frame);
2032                 } else {
2033                         return;
2034                 }
2035         }
2036
2037         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2038                 MeterSection* m;
2039                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2040                         if (m == section){
2041                                 /*
2042                                   here we set the beat for this frame.
2043                                   we set it 'incorrectly' to the next bar's first beat
2044                                   and use the delta to find the meter's pulse.
2045                                 */
2046                                 double new_pulse = 0.0;
2047                                 pair<double, BBT_Time> b_bbt;
2048
2049                                 if (section->movable()) {
2050                                         const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor());
2051                                         const double floor_beats = beats - fmod (beats,  prev_m->divisions_per_bar());
2052                                         if (floor_beats + prev_m->beat() < section->beat()) {
2053                                                 /* disallow position change if it will alter out beat
2054                                                    we allow tempo changes to do this in recompute_meters().
2055                                                    blocking this is an option, but i'm not convinced that
2056                                                    this is what the user would actually want.
2057                                                 */
2058                                                 return;
2059                                         }
2060                                         b_bbt = make_pair (section->beat(), section->bbt());
2061                                         new_pulse = pulse_at_frame_locked (imaginary, frame);
2062                                 } else {
2063                                         b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2064                                 }
2065                                 section->set_frame (frame);
2066                                 section->set_beat (b_bbt);
2067                                 section->set_pulse (new_pulse);
2068                                 prev_m = m;
2069
2070                                 continue;
2071                         }
2072                         if (prev_m) {
2073                                 double new_pulse = 0.0;
2074                                 if (m->position_lock_style() == MusicTime) {
2075                                         new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) *  prev_m->divisions_per_bar() / prev_m->note_divisor());
2076                                         m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2077                                         if (m->frame() > section->frame()) {
2078                                                 /* moving 'section' will affect later meters' beat (but not bbt).*/
2079                                                 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2080                                                 m->set_beat (new_beat);
2081                                         }
2082                                 } else {
2083                                         pair<double, BBT_Time> b_bbt;
2084                                         if (m->movable()) {
2085                                                 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2086                                                 const double floor_beats = beats - fmod (beats , prev_m->divisions_per_bar());
2087                                                 b_bbt = make_pair (floor_beats + prev_m->beat()
2088                                                                    , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2089                                                 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2090                                                 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2091                                                 new_pulse = true_pulse - pulse_off;
2092                                         } else {
2093                                                 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2094                                                 new_pulse = 0.0;
2095                                         }
2096                                         m->set_beat (b_bbt);
2097                                 }
2098                                 m->set_pulse (new_pulse);
2099                         }
2100                         prev_m = m;
2101                 }
2102         }
2103
2104         MetricSectionFrameSorter fcmp;
2105         imaginary.sort (fcmp);
2106         if (section->position_lock_style() == MusicTime) {
2107                 /* we're setting the frame */
2108                 section->set_position_lock_style (AudioTime);
2109                 recompute_meters (imaginary);
2110                 section->set_position_lock_style (MusicTime);
2111         } else {
2112                 recompute_meters (imaginary);
2113         }
2114         //dump (imaginary, std::cerr);
2115 }
2116
2117 void
2118 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
2119 {
2120         MeterSection* prev_m = 0;
2121         MeterSection* section_prev = 0;
2122
2123         section->set_pulse (pulse);
2124
2125         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2126                 MeterSection* m;
2127                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2128                         if (m == section){
2129                                 section_prev = prev_m;
2130                                 continue;
2131                         }
2132                         if (prev_m) {
2133                                 double new_pulse = 0.0;
2134                                 if (m->position_lock_style() == MusicTime) {
2135                                         new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) *  prev_m->divisions_per_bar() / prev_m->note_divisor());
2136                                         m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2137
2138                                         if (new_pulse > section->pulse()) {
2139                                                 /* moving 'section' will affect later meters' beat (but not bbt).*/
2140                                                 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2141                                                 m->set_beat (new_beat);
2142                                         }
2143                                 } else {
2144                                         pair<double, BBT_Time> b_bbt;
2145                                         if (m->movable()) {
2146                                                 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2147                                                 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
2148                                                 b_bbt = make_pair (floor_beats + prev_m->beat()
2149                                                                    , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2150                                                 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2151                                                 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2152                                                 new_pulse = true_pulse - pulse_off;
2153                                         } else {
2154                                                 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2155                                         }
2156                                         m->set_beat (b_bbt);
2157                                 }
2158                                 m->set_pulse (new_pulse);
2159                         }
2160                         prev_m = m;
2161                 }
2162         }
2163
2164         if (section_prev) {
2165                 const double beats = ((pulse - section_prev->pulse()) * section_prev->note_divisor());
2166                 const int32_t bars = (beats + 1) / section_prev->divisions_per_bar();
2167                 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), BBT_Time (bars + section_prev->bbt().bars, 1, 0));
2168                 section->set_beat (b_bbt);
2169                 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2170         }
2171
2172         MetricSectionSorter cmp;
2173         imaginary.sort (cmp);
2174         if (section->position_lock_style() == AudioTime) {
2175                 /* we're setting the pulse */
2176                 section->set_position_lock_style (MusicTime);
2177                 recompute_meters (imaginary);
2178                 section->set_position_lock_style (AudioTime);
2179         } else {
2180                 recompute_meters (imaginary);
2181         }
2182 }
2183
2184 /** places a copy of _metrics into copy and returns a pointer
2185  *  to section's equivalent in copy.
2186  */
2187 TempoSection*
2188 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2189 {
2190         TempoSection* t;
2191         MeterSection* m;
2192         TempoSection* ret = 0;
2193
2194         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2195                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2196                         if (t == section) {
2197                                 if (t->position_lock_style() == MusicTime) {
2198                                         ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2199                                         ret->set_frame (t->frame());
2200                                 } else {
2201                                         ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2202                                         ret->set_pulse (t->pulse());
2203                                 }
2204                                 ret->set_active (t->active());
2205                                 ret->set_movable (t->movable());
2206                                 copy.push_back (ret);
2207                                 continue;
2208                         }
2209                         TempoSection* cp = 0;
2210                         if (t->position_lock_style() == MusicTime) {
2211                                 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2212                                 cp->set_frame (t->frame());
2213                         } else {
2214                                 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2215                                 cp->set_pulse (t->pulse());
2216                         }
2217                         cp->set_active (t->active());
2218                         cp->set_movable (t->movable());
2219                         copy.push_back (cp);
2220                 }
2221                 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2222                         MeterSection* cp = 0;
2223                         if (m->position_lock_style() == MusicTime) {
2224                                 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2225                                 cp->set_frame (m->frame());
2226                         } else {
2227                                 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2228                                 cp->set_pulse (m->pulse());
2229                         }
2230                         cp->set_movable (m->movable());
2231                         copy.push_back (cp);
2232                 }
2233         }
2234
2235         return ret;
2236 }
2237
2238 bool
2239 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2240 {
2241         Metrics copy;
2242         TempoSection* new_section = 0;
2243
2244         {
2245                 Glib::Threads::RWLock::ReaderLock lm (lock);
2246                 new_section = copy_metrics_and_point (copy, ts);
2247         }
2248
2249         double const beat = bbt_to_beats_locked (copy, bbt);
2250         bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
2251
2252         Metrics::const_iterator d = copy.begin();
2253         while (d != copy.end()) {
2254                 delete (*d);
2255                 ++d;
2256         }
2257
2258         return ret;
2259 }
2260
2261 /**
2262 * 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,
2263 * taking any possible reordering as a consequence of this into account.
2264 * @param section - the section to be altered
2265 * @param bpm - the new Tempo
2266 * @param bbt - the bbt where the altered tempo will fall
2267 * @return returns - the position in frames where the new tempo section will lie.
2268 */
2269 framepos_t
2270 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2271 {
2272         Glib::Threads::RWLock::ReaderLock lm (lock);
2273         Metrics future_map;
2274         framepos_t ret = 0;
2275         TempoSection* new_section = copy_metrics_and_point (future_map, section);
2276
2277         const double beat = bbt_to_beats_locked (future_map, bbt);
2278
2279         if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2280                 ret = new_section->frame();
2281         } else {
2282                 ret = frame_at_beat_locked (_metrics, beat);
2283         }
2284
2285         Metrics::const_iterator d = future_map.begin();
2286         while (d != future_map.end()) {
2287                 delete (*d);
2288                 ++d;
2289         }
2290         return ret;
2291 }
2292
2293 double
2294 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2295 {
2296         Glib::Threads::RWLock::ReaderLock lm (lock);
2297         Metrics future_map;
2298         double ret = 0.0;
2299         TempoSection* new_section = copy_metrics_and_point (future_map, section);
2300
2301         if (solve_map (future_map, new_section, frame)) {
2302                 ret = new_section->pulse();
2303         } else {
2304                 ret = pulse_at_frame_locked (_metrics, frame);
2305         }
2306
2307         Metrics::const_iterator d = future_map.begin();
2308         while (d != future_map.end()) {
2309                 delete (*d);
2310                 ++d;
2311         }
2312         return ret;
2313 }
2314
2315 void
2316 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2317 {
2318         Metrics future_map;
2319         {
2320                 Glib::Threads::RWLock::WriterLock lm (lock);
2321                 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2322                 if (solve_map (future_map, new_section, frame)) {
2323                         solve_map (_metrics, ts, frame);
2324                 }
2325         }
2326
2327         Metrics::const_iterator d = future_map.begin();
2328         while (d != future_map.end()) {
2329                 delete (*d);
2330                 ++d;
2331         }
2332
2333         MetricPositionChanged (); // Emit Signal
2334 }
2335
2336 void
2337 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2338 {
2339         Metrics future_map;
2340         {
2341                 Glib::Threads::RWLock::WriterLock lm (lock);
2342                 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2343                 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2344                         solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2345                 }
2346         }
2347
2348         Metrics::const_iterator d = future_map.begin();
2349         while (d != future_map.end()) {
2350                 delete (*d);
2351                 ++d;
2352         }
2353
2354         MetricPositionChanged (); // Emit Signal
2355 }
2356
2357 void
2358 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t&  frame)
2359 {
2360         {
2361                 Glib::Threads::RWLock::WriterLock lm (lock);
2362                 solve_map (_metrics, ms, frame);
2363         }
2364
2365         MetricPositionChanged (); // Emit Signal
2366 }
2367
2368 void
2369 TempoMap::gui_move_meter (MeterSection* ms, const double&  beat)
2370 {
2371         {
2372                 Glib::Threads::RWLock::WriterLock lm (lock);
2373                 solve_map (_metrics, ms, pulse_at_beat_locked (_metrics, beat));
2374         }
2375
2376         MetricPositionChanged (); // Emit Signal
2377 }
2378
2379 bool
2380 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2381 {
2382         Metrics future_map;
2383         bool can_solve = false;
2384         {
2385                 Glib::Threads::RWLock::WriterLock lm (lock);
2386                 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2387                 new_section->set_beats_per_minute (bpm.beats_per_minute());
2388                 recompute_tempos (future_map);
2389
2390                 if (check_solved (future_map, true)) {
2391                         ts->set_beats_per_minute (bpm.beats_per_minute());
2392                         recompute_map (_metrics);
2393                         can_solve = true;
2394                 }
2395         }
2396
2397         Metrics::const_iterator d = future_map.begin();
2398         while (d != future_map.end()) {
2399                 delete (*d);
2400                 ++d;
2401         }
2402         if (can_solve) {
2403                 MetricPositionChanged (); // Emit Signal
2404         }
2405         return can_solve;
2406 }
2407
2408 framecnt_t
2409 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2410 {
2411         Glib::Threads::RWLock::ReaderLock lm (lock);
2412
2413         const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2414         const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2415         const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2416
2417         return frame_at_beat_locked (_metrics, total_beats);
2418 }
2419
2420 framepos_t
2421 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2422 {
2423         return round_to_type (fr, dir, Bar);
2424 }
2425
2426 framepos_t
2427 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2428 {
2429         return round_to_type (fr, dir, Beat);
2430 }
2431
2432 framepos_t
2433 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2434 {
2435         Glib::Threads::RWLock::ReaderLock lm (lock);
2436         uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2437         uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2438         uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2439
2440         ticks -= beats * BBT_Time::ticks_per_beat;
2441
2442         if (dir > 0) {
2443                 /* round to next (or same iff dir == RoundUpMaybe) */
2444
2445                 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2446
2447                 if (mod == 0 && dir == RoundUpMaybe) {
2448                         /* right on the subdivision, which is fine, so do nothing */
2449
2450                 } else if (mod == 0) {
2451                         /* right on the subdivision, so the difference is just the subdivision ticks */
2452                         ticks += ticks_one_subdivisions_worth;
2453
2454                 } else {
2455                         /* not on subdivision, compute distance to next subdivision */
2456
2457                         ticks += ticks_one_subdivisions_worth - mod;
2458                 }
2459
2460                 if (ticks >= BBT_Time::ticks_per_beat) {
2461                         ticks -= BBT_Time::ticks_per_beat;
2462                 }
2463         } else if (dir < 0) {
2464
2465                 /* round to previous (or same iff dir == RoundDownMaybe) */
2466
2467                 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2468
2469                 if (difference == 0 && dir == RoundDownAlways) {
2470                         /* right on the subdivision, but force-rounding down,
2471                            so the difference is just the subdivision ticks */
2472                         difference = ticks_one_subdivisions_worth;
2473                 }
2474
2475                 if (ticks < difference) {
2476                         ticks = BBT_Time::ticks_per_beat - ticks;
2477                 } else {
2478                         ticks -= difference;
2479                 }
2480
2481         } else {
2482                 /* round to nearest */
2483                 double rem;
2484
2485                 /* compute the distance to the previous and next subdivision */
2486
2487                 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2488
2489                         /* closer to the next subdivision, so shift forward */
2490
2491                         ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2492
2493                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2494
2495                         if (ticks > BBT_Time::ticks_per_beat) {
2496                                 ++beats;
2497                                 ticks -= BBT_Time::ticks_per_beat;
2498                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2499                         }
2500
2501                 } else if (rem > 0) {
2502
2503                         /* closer to previous subdivision, so shift backward */
2504
2505                         if (rem > ticks) {
2506                                 if (beats == 0) {
2507                                         /* can't go backwards past zero, so ... */
2508                                         return 0;
2509                                 }
2510                                 /* step back to previous beat */
2511                                 --beats;
2512                                 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2513                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2514                         } else {
2515                                 ticks = lrint (ticks - rem);
2516                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2517                         }
2518                 } else {
2519                         /* on the subdivision, do nothing */
2520                 }
2521         }
2522
2523         const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2524
2525         return ret_frame;
2526 }
2527
2528 void
2529 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2530 {
2531         if (sub_num == -1) {
2532                 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2533                 if ((double) when.beats > bpb / 2.0) {
2534                         ++when.bars;
2535                 }
2536                 when.beats = 1;
2537                 when.ticks = 0;
2538                 return;
2539         } else if (sub_num == 0) {
2540                 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2541                 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2542                         ++when.beats;
2543                         while ((double) when.beats > bpb) {
2544                                 ++when.bars;
2545                                 when.beats -= (uint32_t) floor (bpb);
2546                         }
2547                 }
2548                 when.ticks = 0;
2549                 return;
2550         }
2551         const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2552         double rem;
2553         if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2554                 /* closer to the next subdivision, so shift forward */
2555
2556                 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2557
2558                 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2559                         ++when.beats;
2560                         when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2561                 }
2562
2563         } else if (rem > 0) {
2564                 /* closer to previous subdivision, so shift backward */
2565
2566                 if (rem > when.ticks) {
2567                         if (when.beats == 0) {
2568                                 /* can't go backwards past zero, so ... */
2569                         }
2570                         /* step back to previous beat */
2571                         --when.beats;
2572                         when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2573                 } else {
2574                         when.ticks = when.ticks - rem;
2575                 }
2576         }
2577 }
2578
2579 framepos_t
2580 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2581 {
2582         Glib::Threads::RWLock::ReaderLock lm (lock);
2583
2584         const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2585         BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2586
2587         switch (type) {
2588         case Bar:
2589                 if (dir < 0) {
2590                         /* find bar previous to 'frame' */
2591                         bbt.beats = 1;
2592                         bbt.ticks = 0;
2593                         return frame_time_locked (_metrics, bbt);
2594
2595                 } else if (dir > 0) {
2596                         /* find bar following 'frame' */
2597                         ++bbt.bars;
2598                         bbt.beats = 1;
2599                         bbt.ticks = 0;
2600                         return frame_time_locked (_metrics, bbt);
2601                 } else {
2602                         /* true rounding: find nearest bar */
2603                         framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2604                         bbt.beats = 1;
2605                         bbt.ticks = 0;
2606                         framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2607                         ++bbt.bars;
2608                         framepos_t next_ft = frame_time_locked (_metrics, bbt);
2609
2610                         if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) { 
2611                                 return next_ft;
2612                         } else {
2613                                 return prev_ft;
2614                         }
2615                 }
2616
2617                 break;
2618
2619         case Beat:
2620                 if (dir < 0) {
2621                         return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2622                 } else if (dir > 0) {
2623                         return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2624                 } else {
2625                         return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2626                 }
2627                 break;
2628         }
2629
2630         return 0;
2631 }
2632
2633 void
2634 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2635                     framepos_t lower, framepos_t upper)
2636 {
2637         Glib::Threads::RWLock::ReaderLock lm (lock);
2638         const int32_t upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2639         int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2640         /* although the map handles negative beats, bbt doesn't. */
2641         if (cnt < 0.0) {
2642                 cnt = 0.0;
2643         }
2644         while (cnt <= upper_beat) {
2645                 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2646                 const TempoSection tempo = tempo_section_at_locked (pos);
2647                 const MeterSection meter = meter_section_at_locked (pos);
2648                 const BBT_Time bbt = beats_to_bbt (cnt);
2649                 points.push_back (BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2650                 ++cnt;
2651         }
2652 }
2653
2654 const TempoSection&
2655 TempoMap::tempo_section_at (framepos_t frame) const
2656 {
2657         Glib::Threads::RWLock::ReaderLock lm (lock);
2658         return tempo_section_at_locked (frame);
2659 }
2660
2661 const TempoSection&
2662 TempoMap::tempo_section_at_locked (framepos_t frame) const
2663 {
2664         Metrics::const_iterator i;
2665         TempoSection* prev = 0;
2666
2667         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2668                 TempoSection* t;
2669
2670                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2671                         if (!t->active()) {
2672                                 continue;
2673                         }
2674                         if (t->frame() > frame) {
2675                                 break;
2676                         }
2677
2678                         prev = t;
2679                 }
2680         }
2681
2682         if (prev == 0) {
2683                 fatal << endmsg;
2684                 abort(); /*NOTREACHED*/
2685         }
2686
2687         return *prev;
2688 }
2689
2690
2691 /* don't use this to calculate length (the tempo is only correct for this frame).
2692    do that stuff based on the beat_at_frame and frame_at_beat api
2693 */
2694 double
2695 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2696 {
2697         Glib::Threads::RWLock::ReaderLock lm (lock);
2698
2699         const TempoSection* ts_at = &tempo_section_at_locked (frame);
2700         const TempoSection* ts_after = 0;
2701         Metrics::const_iterator i;
2702
2703         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2704                 TempoSection* t;
2705
2706                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2707                         if (!t->active()) {
2708                                 continue;
2709                         }
2710                         if ((*i)->frame() > frame) {
2711                                 ts_after = t;
2712                                 break;
2713                         }
2714                 }
2715         }
2716
2717         if (ts_after) {
2718                 return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2719         }
2720         /* must be treated as constant tempo */
2721         return ts_at->frames_per_beat (_frame_rate);
2722 }
2723
2724 const Tempo
2725 TempoMap::tempo_at (const framepos_t& frame) const
2726 {
2727         Glib::Threads::RWLock::ReaderLock lm (lock);
2728         return tempo_at_locked (frame);
2729 }
2730
2731 const Tempo
2732 TempoMap::tempo_at_locked (const framepos_t& frame) const
2733 {
2734         TempoSection* prev_t = 0;
2735
2736         Metrics::const_iterator i;
2737
2738         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2739                 TempoSection* t;
2740                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2741                         if (!t->active()) {
2742                                 continue;
2743                         }
2744                         if ((prev_t) && t->frame() > frame) {
2745                                 /* t is the section past frame */
2746                                 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
2747                                 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
2748                                 return ret_tempo;
2749                         }
2750                         prev_t = t;
2751                 }
2752         }
2753
2754         const double ret = prev_t->beats_per_minute();
2755         const Tempo ret_tempo (ret, prev_t->note_type ());
2756
2757         return ret_tempo;
2758 }
2759
2760 const MeterSection&
2761 TempoMap::meter_section_at (framepos_t frame) const
2762 {
2763         Glib::Threads::RWLock::ReaderLock lm (lock);
2764         return meter_section_at_locked (frame);
2765 }
2766
2767 const MeterSection&
2768 TempoMap::meter_section_at_locked (framepos_t frame) const
2769 {
2770         Metrics::const_iterator i;
2771         MeterSection* prev = 0;
2772
2773         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2774                 MeterSection* m;
2775
2776                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2777
2778                         if (prev && (*i)->frame() > frame) {
2779                                 break;
2780                         }
2781
2782                         prev = m;
2783                 }
2784         }
2785
2786         if (prev == 0) {
2787                 fatal << endmsg;
2788                 abort(); /*NOTREACHED*/
2789         }
2790
2791         return *prev;
2792 }
2793
2794 const Meter&
2795 TempoMap::meter_at (framepos_t frame) const
2796 {
2797         TempoMetric m (metric_at (frame));
2798         return m.meter();
2799 }
2800
2801 const MeterSection&
2802 TempoMap::meter_section_at (const double& beat) const
2803 {
2804         MeterSection* prev_m = 0;
2805         Glib::Threads::RWLock::ReaderLock lm (lock);
2806
2807         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2808                 MeterSection* m;
2809                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2810                         if (prev_m && m->beat() > beat) {
2811                                 break;
2812                         }
2813                         prev_m = m;
2814                 }
2815
2816         }
2817         return *prev_m;
2818 }
2819
2820 void
2821 TempoMap::fix_legacy_session ()
2822 {
2823         MeterSection* prev_m = 0;
2824         TempoSection* prev_t = 0;
2825
2826         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2827                 MeterSection* m;
2828                 TempoSection* t;
2829
2830                 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2831                         if (!m->movable()) {
2832                                 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2833                                 m->set_beat (bbt);
2834                                 m->set_pulse (0.0);
2835                                 m->set_frame (0);
2836                                 m->set_position_lock_style (AudioTime);
2837                                 prev_m = m;
2838                                 continue;
2839                         }
2840                         if (prev_m) {
2841                                 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
2842                                                                           + (m->bbt().beats - 1)
2843                                                                           + (m->bbt().ticks / BBT_Time::ticks_per_beat)
2844                                                                           , m->bbt());
2845                                 m->set_beat (start);
2846                                 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
2847                                         + (m->bbt().beats - 1)
2848                                         + (m->bbt().ticks / BBT_Time::ticks_per_beat);
2849                                 m->set_pulse (start_beat / prev_m->note_divisor());
2850                         }
2851                         prev_m = m;
2852                 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2853
2854                         if (!t->active()) {
2855                                 continue;
2856                         }
2857
2858                         if (!t->movable()) {
2859                                 t->set_pulse (0.0);
2860                                 t->set_frame (0);
2861                                 t->set_position_lock_style (AudioTime);
2862                                 prev_t = t;
2863                                 continue;
2864                         }
2865
2866                         if (prev_t) {
2867                                 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
2868                                         + (t->legacy_bbt().beats - 1)
2869                                         + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2870                                 if (prev_m) {
2871                                         t->set_pulse (beat / prev_m->note_divisor());
2872                                 } else {
2873                                         /* really shouldn't happen but.. */
2874                                         t->set_pulse (beat / 4.0);
2875                                 }
2876                         }
2877                         prev_t = t;
2878                 }
2879         }
2880 }
2881
2882 XMLNode&
2883 TempoMap::get_state ()
2884 {
2885         Metrics::const_iterator i;
2886         XMLNode *root = new XMLNode ("TempoMap");
2887
2888         {
2889                 Glib::Threads::RWLock::ReaderLock lm (lock);
2890                 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2891                         root->add_child_nocopy ((*i)->get_state());
2892                 }
2893         }
2894
2895         return *root;
2896 }
2897
2898 int
2899 TempoMap::set_state (const XMLNode& node, int /*version*/)
2900 {
2901         {
2902                 Glib::Threads::RWLock::WriterLock lm (lock);
2903
2904                 XMLNodeList nlist;
2905                 XMLNodeConstIterator niter;
2906                 Metrics old_metrics (_metrics);
2907                 _metrics.clear();
2908
2909                 nlist = node.children();
2910
2911                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2912                         XMLNode* child = *niter;
2913
2914                         if (child->name() == TempoSection::xml_state_node_name) {
2915
2916                                 try {
2917                                         TempoSection* ts = new TempoSection (*child);
2918                                         _metrics.push_back (ts);
2919                                 }
2920
2921                                 catch (failed_constructor& err){
2922                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2923                                         _metrics = old_metrics;
2924                                         break;
2925                                 }
2926
2927                         } else if (child->name() == MeterSection::xml_state_node_name) {
2928
2929                                 try {
2930                                         MeterSection* ms = new MeterSection (*child);
2931                                         _metrics.push_back (ms);
2932                                 }
2933
2934                                 catch (failed_constructor& err) {
2935                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2936                                         _metrics = old_metrics;
2937                                         break;
2938                                 }
2939                         }
2940                 }
2941
2942                 if (niter == nlist.end()) {
2943                         MetricSectionSorter cmp;
2944                         _metrics.sort (cmp);
2945                 }
2946
2947                 /* check for legacy sessions where bbt was the base musical unit for tempo */
2948                 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2949                         TempoSection* t;
2950                         if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2951                                 if (t->legacy_bbt().bars != 0) {
2952                                         fix_legacy_session();
2953                                         break;
2954                                 }
2955                                 break;
2956                         }
2957                 }
2958
2959                 /* check for multiple tempo/meters at the same location, which
2960                    ardour2 somehow allowed.
2961                 */
2962
2963                 Metrics::iterator prev = _metrics.end();
2964                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2965                         if (prev != _metrics.end()) {
2966                                 MeterSection* ms;
2967                                 MeterSection* prev_m;
2968                                 TempoSection* ts;
2969                                 TempoSection* prev_t;
2970                                 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2971                                         if (prev_m->pulse() == ms->pulse()) {
2972                                                 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2973                                                 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2974                                                 return -1;
2975                                         }
2976                                 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2977                                         if (prev_t->pulse() == ts->pulse()) {
2978                                                 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2979                                                 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2980                                                 return -1;
2981                                         }
2982                                 }
2983                         }
2984                         prev = i;
2985                 }
2986
2987                 recompute_map (_metrics);
2988         }
2989
2990         PropertyChanged (PropertyChange ());
2991
2992         return 0;
2993 }
2994
2995 void
2996 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2997 {
2998         Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2999         const MeterSection* m;
3000         const TempoSection* t;
3001         const TempoSection* prev_t = 0;
3002
3003         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3004
3005                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3006                         o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3007                           << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3008                         o << "current      : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3009                         if (prev_t) {
3010                                 o << "previous     : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3011                                 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;
3012                         }
3013                         prev_t = t;
3014                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3015                         o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3016                           << " pulse: " << m->pulse() <<  " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3017                 }
3018         }
3019         o << "------" << std::endl;
3020 }
3021
3022 int
3023 TempoMap::n_tempos() 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 TempoSection*>(*i) != 0) {
3030                         cnt++;
3031                 }
3032         }
3033
3034         return cnt;
3035 }
3036
3037 int
3038 TempoMap::n_meters() const
3039 {
3040         Glib::Threads::RWLock::ReaderLock lm (lock);
3041         int cnt = 0;
3042
3043         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3044                 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3045                         cnt++;
3046                 }
3047         }
3048
3049         return cnt;
3050 }
3051
3052 void
3053 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3054 {
3055         {
3056                 Glib::Threads::RWLock::WriterLock lm (lock);
3057                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3058                         if ((*i)->frame() >= where && (*i)->movable ()) {
3059                                 (*i)->set_frame ((*i)->frame() + amount);
3060                         }
3061                 }
3062
3063                 /* now reset the BBT time of all metrics, based on their new
3064                  * audio time. This is the only place where we do this reverse
3065                  * timestamp.
3066                  */
3067
3068                 Metrics::iterator i;
3069                 const MeterSection* meter;
3070                 const TempoSection* tempo;
3071                 MeterSection *m;
3072                 TempoSection *t;
3073
3074                 meter = &first_meter ();
3075                 tempo = &first_tempo ();
3076
3077                 BBT_Time start;
3078                 BBT_Time end;
3079
3080                 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3081
3082                 bool first = true;
3083                 MetricSection* prev = 0;
3084
3085                 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3086
3087                         BBT_Time bbt;
3088                         //TempoMetric metric (*meter, *tempo);
3089                         MeterSection* ms = const_cast<MeterSection*>(meter);
3090                         TempoSection* ts = const_cast<TempoSection*>(tempo);
3091                         if (prev) {
3092                                 if (ts){
3093                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3094                                                 if (!t->active()) {
3095                                                         continue;
3096                                                 }
3097                                                 ts->set_pulse (t->pulse());
3098                                         }
3099                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3100                                                 ts->set_pulse (m->pulse());
3101                                         }
3102                                         ts->set_frame (prev->frame());
3103
3104                                 }
3105                                 if (ms) {
3106                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3107                                                 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3108                                                 ms->set_beat (start);
3109                                                 ms->set_pulse (m->pulse());
3110                                         }
3111                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3112                                                 if (!t->active()) {
3113                                                         continue;
3114                                                 }
3115                                                 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3116                                                 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3117                                                 ms->set_beat (start);
3118                                                 ms->set_pulse (t->pulse());
3119                                         }
3120                                         ms->set_frame (prev->frame());
3121                                 }
3122
3123                         } else {
3124                                 // metric will be at frames=0 bbt=1|1|0 by default
3125                                 // which is correct for our purpose
3126                         }
3127
3128                         // cerr << bbt << endl;
3129
3130                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3131                                 if (!t->active()) {
3132                                         continue;
3133                                 }
3134                                 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3135                                 tempo = t;
3136                                 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3137                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3138                                 bbt_time (m->frame(), bbt);
3139
3140                                 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3141
3142                                 if (first) {
3143                                         first = false;
3144                                 } else {
3145
3146                                         if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3147                                                 /* round up to next beat */
3148                                                 bbt.beats += 1;
3149                                         }
3150
3151                                         bbt.ticks = 0;
3152
3153                                         if (bbt.beats != 1) {
3154                                                 /* round up to next bar */
3155                                                 bbt.bars += 1;
3156                                                 bbt.beats = 1;
3157                                         }
3158                                 }
3159                                 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3160                                 m->set_beat (start);
3161                                 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3162                                 meter = m;
3163                                 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3164                         } else {
3165                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3166                                 abort(); /*NOTREACHED*/
3167                         }
3168
3169                         prev = (*i);
3170                 }
3171
3172                 recompute_map (_metrics);
3173         }
3174
3175
3176         PropertyChanged (PropertyChange ());
3177 }
3178 bool
3179 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3180 {
3181         bool moved = false;
3182
3183         std::list<MetricSection*> metric_kill_list;
3184
3185         TempoSection* last_tempo = NULL;
3186         MeterSection* last_meter = NULL;
3187         bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3188         bool meter_after = false; // is there a meter marker likewise?
3189         {
3190                 Glib::Threads::RWLock::WriterLock lm (lock);
3191                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3192                         if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3193                                 metric_kill_list.push_back(*i);
3194                                 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3195                                 if (lt)
3196                                         last_tempo = lt;
3197                                 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3198                                 if (lm)
3199                                         last_meter = lm;
3200                         }
3201                         else if ((*i)->frame() >= where) {
3202                                 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3203                                 (*i)->set_frame ((*i)->frame() - amount);
3204                                 if ((*i)->frame() == where) {
3205                                         // marker was immediately after end of range
3206                                         tempo_after = dynamic_cast<TempoSection*> (*i);
3207                                         meter_after = dynamic_cast<MeterSection*> (*i);
3208                                 }
3209                                 moved = true;
3210                         }
3211                 }
3212
3213                 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3214                 if (last_tempo && !tempo_after) {
3215                         metric_kill_list.remove(last_tempo);
3216                         last_tempo->set_frame(where);
3217                         moved = true;
3218                 }
3219                 if (last_meter && !meter_after) {
3220                         metric_kill_list.remove(last_meter);
3221                         last_meter->set_frame(where);
3222                         moved = true;
3223                 }
3224
3225                 //remove all the remaining metrics
3226                 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3227                         _metrics.remove(*i);
3228                         moved = true;
3229                 }
3230
3231                 if (moved) {
3232                         recompute_map (_metrics);
3233                 }
3234         }
3235         PropertyChanged (PropertyChange ());
3236         return moved;
3237 }
3238
3239 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3240  *  pos can be -ve, if required.
3241  */
3242 framepos_t
3243 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3244 {
3245         return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3246 }
3247
3248 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3249 framepos_t
3250 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3251 {
3252         return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3253 }
3254
3255 /** Add the BBT interval op to pos and return the result */
3256 framepos_t
3257 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3258 {
3259         Glib::Threads::RWLock::ReaderLock lm (lock);
3260
3261         BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3262         pos_bbt.ticks += op.ticks;
3263         if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3264                 ++pos_bbt.beats;
3265                 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3266         }
3267         pos_bbt.beats += op.beats;
3268         /* the meter in effect will start on the bar */
3269         double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3270         while (pos_bbt.beats >= divisions_per_bar + 1) {
3271                 ++pos_bbt.bars;
3272                 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3273                 pos_bbt.beats -= divisions_per_bar;
3274         }
3275         pos_bbt.bars += op.bars;
3276
3277         return frame_time_locked (_metrics, pos_bbt);
3278 }
3279
3280 /** Count the number of beats that are equivalent to distance when going forward,
3281     starting at pos.
3282 */
3283 Evoral::Beats
3284 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3285 {
3286         return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3287 }
3288
3289 struct bbtcmp {
3290     bool operator() (const BBT_Time& a, const BBT_Time& b) {
3291             return a < b;
3292     }
3293 };
3294
3295 std::ostream&
3296 operator<< (std::ostream& o, const Meter& m) {
3297         return o << m.divisions_per_bar() << '/' << m.note_divisor();
3298 }
3299
3300 std::ostream&
3301 operator<< (std::ostream& o, const Tempo& t) {
3302         return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3303 }
3304
3305 std::ostream&
3306 operator<< (std::ostream& o, const MetricSection& section) {
3307
3308         o << "MetricSection @ " << section.frame() << ' ';
3309
3310         const TempoSection* ts;
3311         const MeterSection* ms;
3312
3313         if ((ts = dynamic_cast<const TempoSection*> (&section)) != 0) {
3314                 o << *((const Tempo*) ts);
3315         } else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
3316                 o << *((const Meter*) ms);
3317         }
3318
3319         return o;
3320 }