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