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