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