Tempo ramps - restore correct drag behaviour wrt audio-locked meters and right-click...
[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 frameoffset_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 - (frameoffset_t) 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 frameoffset_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 frameoffset_t
384 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
385 {
386         return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
387 }
388
389 double
390 TempoSection::frame_to_minute (const frameoffset_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 }
658
659 void
660 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
661 {
662         bool removed = false;
663
664         {
665                 Glib::Threads::RWLock::WriterLock lm (lock);
666                 if ((removed = remove_tempo_locked (tempo))) {
667                         if (complete_operation) {
668                                 recompute_map (_metrics);
669                         }
670                 }
671         }
672
673         if (removed && complete_operation) {
674                 PropertyChanged (PropertyChange ());
675         }
676 }
677
678 bool
679 TempoMap::remove_tempo_locked (const TempoSection& tempo)
680 {
681         Metrics::iterator i;
682
683         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
684                 if (dynamic_cast<TempoSection*> (*i) != 0) {
685                         if (tempo.frame() == (*i)->frame()) {
686                                 if ((*i)->movable()) {
687                                         delete (*i);
688                                         _metrics.erase (i);
689                                         return true;
690                                 }
691                         }
692                 }
693         }
694
695         return false;
696 }
697
698 void
699 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
700 {
701         bool removed = false;
702
703         {
704                 Glib::Threads::RWLock::WriterLock lm (lock);
705                 if ((removed = remove_meter_locked (tempo))) {
706                         if (complete_operation) {
707                                 recompute_map (_metrics);
708                         }
709                 }
710         }
711
712         if (removed && complete_operation) {
713                 PropertyChanged (PropertyChange ());
714         }
715 }
716
717 bool
718 TempoMap::remove_meter_locked (const MeterSection& meter)
719 {
720         Metrics::iterator i;
721
722         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
723                 TempoSection* t = 0;
724                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
725                         if (meter.frame() == (*i)->frame()) {
726                                 if (t->locked_to_meter()) {
727                                         delete (*i);
728                                         _metrics.erase (i);
729                                         break;
730                                 }
731                         }
732                 }
733         }
734
735         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
736                 if (dynamic_cast<MeterSection*> (*i) != 0) {
737                         if (meter.frame() == (*i)->frame()) {
738                                 if ((*i)->movable()) {
739                                         delete (*i);
740                                         _metrics.erase (i);
741                                         return true;
742                                 }
743                         }
744                 }
745         }
746
747         return false;
748 }
749
750 void
751 TempoMap::do_insert (MetricSection* section)
752 {
753         bool need_add = true;
754         /* we only allow new meters to be inserted on beat 1 of an existing
755          * measure.
756          */
757         MeterSection* m = 0;
758         if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
759                 //assert (m->bbt().ticks == 0);
760
761                 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
762
763                         pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
764                         corrected.second.beats = 1;
765                         corrected.second.ticks = 0;
766                         corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
767                         warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
768                                                    m->bbt(), corrected.second) << endmsg;
769                         //m->set_pulse (corrected);
770                 }
771         }
772
773         /* Look for any existing MetricSection that is of the same type and
774            in the same bar as the new one, and remove it before adding
775            the new one. Note that this means that if we find a matching,
776            existing section, we can break out of the loop since we're
777            guaranteed that there is only one such match.
778         */
779
780         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
781
782                 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
783                 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
784                 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
785                 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
786
787                 if (tempo && insert_tempo) {
788
789                         /* Tempo sections */
790                         bool const ipm = insert_tempo->position_lock_style() == MusicTime;
791                         if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
792
793                                 if (!tempo->movable()) {
794
795                                         /* can't (re)move this section, so overwrite
796                                          * its data content (but not its properties as
797                                          * a section).
798                                          */
799
800                                         *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
801                                         (*i)->set_position_lock_style (AudioTime);
802                                         TempoSection* t;
803                                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
804                                                 t->set_type (insert_tempo->type());
805                                         }
806                                         need_add = false;
807                                 } else {
808                                         delete (*i);
809                                         _metrics.erase (i);
810                                 }
811                                 break;
812                         }
813
814                 } else if (meter && insert_meter) {
815
816                         /* Meter Sections */
817
818                         bool const ipm = insert_meter->position_lock_style() == MusicTime;
819
820                         if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
821
822                                 if (!meter->movable()) {
823
824                                         /* can't (re)move this section, so overwrite
825                                          * its data content (but not its properties as
826                                          * a section
827                                          */
828
829                                         *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
830                                         (*i)->set_position_lock_style (AudioTime);
831                                         need_add = false;
832                                 } else {
833                                         delete (*i);
834                                         _metrics.erase (i);
835                                 }
836
837                                 break;
838                         }
839                 } else {
840                         /* non-matching types, so we don't care */
841                 }
842         }
843
844         /* Add the given MetricSection, if we didn't just reset an existing
845          * one above
846          */
847
848         if (need_add) {
849                 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
850                 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
851                 Metrics::iterator i;
852                 if (insert_meter) {
853                         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
854                                 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
855
856                                 if (meter) {
857                                         bool const ipm = insert_meter->position_lock_style() == MusicTime;
858                                         if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
859                                                 break;
860                                         }
861                                 }
862                         }
863                 } else if (insert_tempo) {
864                         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
865                                 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
866
867                                 if (tempo) {
868                                         bool const ipm = insert_tempo->position_lock_style() == MusicTime;
869                                         if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
870                                                 break;
871                                         }
872                                 }
873                         }
874                 }
875
876                 _metrics.insert (i, section);
877                 //dump (_metrics, std::cerr);
878         }
879 }
880
881 void
882 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
883 {
884         {
885                 Glib::Threads::RWLock::WriterLock lm (lock);
886                 TempoSection& first (first_tempo());
887                 if (ts.pulse() != first.pulse()) {
888                         remove_tempo_locked (ts);
889                         add_tempo_locked (tempo, pulse, true, type);
890                 } else {
891                         first.set_type (type);
892                         {
893                                 /* cannot move the first tempo section */
894                                 *static_cast<Tempo*>(&first) = tempo;
895                                 recompute_map (_metrics);
896                         }
897                 }
898         }
899
900         PropertyChanged (PropertyChange ());
901 }
902
903 void
904 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
905 {
906         {
907                 Glib::Threads::RWLock::WriterLock lm (lock);
908                 TempoSection& first (first_tempo());
909                 if (ts.frame() != first.frame()) {
910                         remove_tempo_locked (ts);
911                         add_tempo_locked (tempo, frame, true, type);
912                 } else {
913                         first.set_type (type);
914                         first.set_pulse (0.0);
915                         first.set_position_lock_style (AudioTime);
916                         {
917                                 /* cannot move the first tempo section */
918                                 *static_cast<Tempo*>(&first) = tempo;
919                                 recompute_map (_metrics);
920                         }
921                 }
922         }
923
924         PropertyChanged (PropertyChange ());
925 }
926
927 TempoSection*
928 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
929 {
930         TempoSection* ts = 0;
931         {
932                 Glib::Threads::RWLock::WriterLock lm (lock);
933                 ts = add_tempo_locked (tempo, pulse, true, type);
934         }
935
936         PropertyChanged (PropertyChange ());
937
938         return ts;
939 }
940
941 TempoSection*
942 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
943 {
944         TempoSection* ts = 0;
945         {
946                 Glib::Threads::RWLock::WriterLock lm (lock);
947                 ts = add_tempo_locked (tempo, frame, true, type);
948         }
949
950
951         PropertyChanged (PropertyChange ());
952
953         return ts;
954 }
955
956 TempoSection*
957 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
958 {
959         TempoSection* t = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
960
961         do_insert (t);
962
963         if (recompute) {
964                 solve_map (_metrics, t, t->pulse());
965                 recompute_meters (_metrics);
966         }
967
968         return t;
969 }
970
971 TempoSection*
972 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
973 {
974         TempoSection* t = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
975
976         do_insert (t);
977
978         if (recompute) {
979                 solve_map (_metrics, t, t->frame());
980                 recompute_meters (_metrics);
981         }
982
983         return t;
984 }
985
986 void
987 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
988 {
989         {
990                 Glib::Threads::RWLock::WriterLock lm (lock);
991
992                 if (ms.movable()) {
993                         remove_meter_locked (ms);
994                         add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
995                 } else {
996                         MeterSection& first (first_meter());
997                         /* cannot move the first meter section */
998                         *static_cast<Meter*>(&first) = meter;
999                         first.set_position_lock_style (AudioTime);
1000                 }
1001                 recompute_map (_metrics);
1002         }
1003
1004         PropertyChanged (PropertyChange ());
1005 }
1006
1007 void
1008 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
1009 {
1010         {
1011                 Glib::Threads::RWLock::WriterLock lm (lock);
1012
1013                 const double beat = ms.beat();
1014                 const BBT_Time bbt = ms.bbt();
1015
1016                 if (ms.movable()) {
1017                         remove_meter_locked (ms);
1018                         add_meter_locked (meter, frame, beat, bbt, true);
1019                 } else {
1020                         MeterSection& first (first_meter());
1021                         TempoSection& first_t (first_tempo());
1022                         /* cannot move the first meter section */
1023                         *static_cast<Meter*>(&first) = meter;
1024                         first.set_position_lock_style (AudioTime);
1025                         first.set_pulse (0.0);
1026                         first.set_frame (frame);
1027                         pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1028                         first.set_beat (beat);
1029                         first_t.set_frame (first.frame());
1030                         first_t.set_pulse (0.0);
1031                         first_t.set_position_lock_style (AudioTime);
1032                 }
1033                 recompute_map (_metrics);
1034         }
1035         PropertyChanged (PropertyChange ());
1036 }
1037
1038
1039 MeterSection*
1040 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
1041 {
1042         MeterSection* m = 0;
1043         {
1044                 Glib::Threads::RWLock::WriterLock lm (lock);
1045                 m = add_meter_locked (meter, beat, where, true);
1046         }
1047
1048
1049 #ifndef NDEBUG
1050         if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1051                 dump (_metrics, std::cerr);
1052         }
1053 #endif
1054
1055         PropertyChanged (PropertyChange ());
1056
1057         return m;
1058 }
1059
1060 MeterSection*
1061 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1062 {
1063         MeterSection* m = 0;
1064         {
1065                 Glib::Threads::RWLock::WriterLock lm (lock);
1066                 m = add_meter_locked (meter, frame, beat, where, true);
1067         }
1068
1069
1070 #ifndef NDEBUG
1071         if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1072                 dump (_metrics, std::cerr);
1073         }
1074 #endif
1075
1076         PropertyChanged (PropertyChange ());
1077
1078         return m;
1079 }
1080
1081 MeterSection*
1082 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1083 {
1084         /* a new meter always starts a new bar on the first beat. so
1085            round the start time appropriately. remember that
1086            `where' is based on the existing tempo map, not
1087            the result after we insert the new meter.
1088
1089         */
1090
1091         if (where.beats != 1) {
1092                 where.beats = 1;
1093                 where.bars++;
1094         }
1095         /* new meters *always* start on a beat. */
1096         where.ticks = 0;
1097         const double pulse = pulse_at_beat_locked (_metrics, beat);
1098         MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1099         do_insert (new_meter);
1100
1101         if (recompute) {
1102                 solve_map (_metrics, new_meter, pulse);
1103         }
1104
1105         return new_meter;
1106 }
1107
1108 MeterSection*
1109 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1110 {
1111         MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1112         TempoSection* t = 0;
1113         double pulse = pulse_at_frame_locked (_metrics, frame);
1114         new_meter->set_pulse (pulse);
1115
1116         do_insert (new_meter);
1117
1118         /* add meter-locked tempo */
1119         t = add_tempo_locked (tempo_at_locked (_metrics, frame), frame, true, TempoSection::Ramp);
1120         t->set_locked_to_meter (true);
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                                         t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1303
1304                                 } else {
1305                                         prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1306                                         t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1307
1308                                 }
1309                         }
1310                         prev_t = t;
1311                 }
1312         }
1313         prev_t->set_c_func (0.0);
1314 }
1315
1316 /* tempos must be positioned correctly */
1317 void
1318 TempoMap::recompute_meters (Metrics& metrics)
1319 {
1320         MeterSection* meter = 0;
1321         MeterSection* prev_m = 0;
1322
1323         for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1324                 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1325                         if (meter->position_lock_style() == AudioTime) {
1326                                 double pulse = 0.0;
1327                                 pair<double, BBT_Time> b_bbt;
1328                                 if (meter->movable()) {
1329                                         const double beats = floor ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse())
1330                                                                     * prev_m->note_divisor() + 0.5);
1331
1332                                         if (beats + prev_m->beat() < meter->beat()) {
1333                                                 /* tempo change caused a change in beat (bar). */
1334                                                 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
1335                                                 b_bbt = make_pair (floor_beats + prev_m->beat()
1336                                                                    , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1337                                                 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
1338                                                 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
1339                                                 pulse = true_pulse - pulse_off;
1340                                         } else {
1341                                                 b_bbt = make_pair (meter->beat(), meter->bbt());
1342                                                 pulse = pulse_at_frame_locked (metrics, meter->frame());
1343                                         }
1344                                 } else {
1345                                         b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1346                                 }
1347                                 meter->set_beat (b_bbt);
1348                                 meter->set_pulse (pulse);
1349                         } else {
1350                                 double pulse = 0.0;
1351                                 pair<double, BBT_Time> new_beat;
1352                                 if (prev_m) {
1353                                         pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) *  prev_m->divisions_per_bar() / prev_m->note_divisor());
1354                                         new_beat = make_pair (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
1355                                 } else {
1356                                         /* shouldn't happen - the first is audio-locked */
1357                                         pulse = pulse_at_beat_locked (metrics, meter->beat());
1358                                         new_beat = make_pair (pulse, meter->bbt());
1359                                 }
1360
1361                                 meter->set_beat (new_beat);
1362                                 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1363                                 meter->set_pulse (pulse);
1364                         }
1365
1366                         prev_m = meter;
1367                 }
1368         }
1369         //dump (_metrics, std::cerr;
1370 }
1371
1372 void
1373 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1374 {
1375         /* CALLER MUST HOLD WRITE LOCK */
1376
1377         if (end < 0) {
1378
1379                 /* we will actually stop once we hit
1380                    the last metric.
1381                 */
1382                 end = max_framepos;
1383
1384         }
1385
1386         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1387
1388         if (end == 0) {
1389                 /* silly call from Session::process() during startup
1390                  */
1391                 return;
1392         }
1393
1394         recompute_tempos (metrics);
1395         recompute_meters (metrics);
1396 }
1397
1398 TempoMetric
1399 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1400 {
1401         Glib::Threads::RWLock::ReaderLock lm (lock);
1402         TempoMetric m (first_meter(), first_tempo());
1403
1404         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1405            at something, because we insert the default tempo and meter during
1406            TempoMap construction.
1407
1408            now see if we can find better candidates.
1409         */
1410
1411         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1412
1413                 if ((*i)->frame() > frame) {
1414                         break;
1415                 }
1416
1417                 m.set_metric(*i);
1418
1419                 if (last) {
1420                         *last = i;
1421                 }
1422         }
1423
1424         return m;
1425 }
1426
1427 /* XX meters only */
1428 TempoMetric
1429 TempoMap::metric_at (BBT_Time bbt) const
1430 {
1431         Glib::Threads::RWLock::ReaderLock lm (lock);
1432         TempoMetric m (first_meter(), first_tempo());
1433
1434         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1435            at something, because we insert the default tempo and meter during
1436            TempoMap construction.
1437
1438            now see if we can find better candidates.
1439         */
1440
1441         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1442                 MeterSection* mw;
1443                 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1444                         BBT_Time section_start (mw->bbt());
1445
1446                         if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1447                                 break;
1448                         }
1449
1450                         m.set_metric (*i);
1451                 }
1452         }
1453
1454         return m;
1455 }
1456
1457 double
1458 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1459 {
1460         MeterSection* prev_m = 0;
1461
1462         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1463                 MeterSection* m;
1464                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1465                         if (prev_m && m->beat() > beat) {
1466                                 break;
1467                         }
1468                         prev_m = m;
1469                 }
1470
1471         }
1472         double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1473         return ret;
1474 }
1475
1476 double
1477 TempoMap::pulse_at_beat (const double& beat) const
1478 {
1479         Glib::Threads::RWLock::ReaderLock lm (lock);
1480         return pulse_at_beat_locked (_metrics, beat);
1481 }
1482
1483 double
1484 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1485 {
1486         MeterSection* prev_m = 0;
1487
1488         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1489                 MeterSection* m;
1490                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1491                         if (prev_m && m->pulse() > pulse) {
1492                                 if ((pulse - prev_m->pulse()) * prev_m->note_divisor() < m->beat()) {
1493                                         break;
1494                                 }
1495                         }
1496                         prev_m = m;
1497                 }
1498         }
1499
1500         double const beats_in_section = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1501
1502         return beats_in_section + prev_m->beat();
1503 }
1504
1505 double
1506 TempoMap::beat_at_pulse (const double& pulse) const
1507 {
1508         Glib::Threads::RWLock::ReaderLock lm (lock);
1509         return beat_at_pulse_locked (_metrics, pulse);
1510 }
1511
1512 /* tempo section based */
1513 double
1514 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1515 {
1516         /* HOLD (at least) THE READER LOCK */
1517         TempoSection* prev_t = 0;
1518
1519         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1520                 TempoSection* t;
1521                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1522                         if (!t->active()) {
1523                                 continue;
1524                         }
1525                         if (prev_t && t->frame() > frame) {
1526                                 /*the previous ts is the one containing the frame */
1527                                 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1528                                 return ret;
1529                         }
1530                         prev_t = t;
1531                 }
1532         }
1533
1534         /* treated as constant for this ts */
1535         const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1536
1537         return pulses_in_section + prev_t->pulse();
1538 }
1539
1540 double
1541 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1542 {
1543         Glib::Threads::RWLock::ReaderLock lm (lock);
1544         return pulse_at_frame_locked (_metrics, frame);
1545 }
1546
1547 /* tempo section based */
1548 framecnt_t
1549 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1550 {
1551         /* HOLD THE READER LOCK */
1552
1553         const TempoSection* prev_t = 0;
1554
1555         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1556                 TempoSection* t;
1557
1558                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1559                         if (!t->active()) {
1560                                 continue;
1561                         }
1562                         if (prev_t && t->pulse() > pulse) {
1563                                 return prev_t->frame_at_pulse (pulse, _frame_rate);
1564                         }
1565
1566                         prev_t = t;
1567                 }
1568         }
1569         /* must be treated as constant, irrespective of _type */
1570         double const pulses_in_section = pulse - prev_t->pulse();
1571         double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1572
1573         framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1574
1575         return ret;
1576 }
1577
1578 framecnt_t
1579 TempoMap::frame_at_pulse (const double& pulse) const
1580 {
1581         Glib::Threads::RWLock::ReaderLock lm (lock);
1582         return frame_at_pulse_locked (_metrics, pulse);
1583 }
1584
1585 /* meter section based */
1586 double
1587 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1588 {
1589         const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1590         MeterSection* prev_m = 0;
1591         MeterSection* next_m = 0;
1592
1593         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1594                 MeterSection* m;
1595                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1596                         if (prev_m && m->frame() > frame) {
1597                                 next_m = m;
1598                                 break;
1599                         }
1600                         prev_m = m;
1601                 }
1602         }
1603         if (frame < prev_m->frame()) {
1604                 return 0.0;
1605         }
1606         const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1607
1608         if (next_m && next_m->beat() < beat) {
1609                 return next_m->beat();
1610         }
1611
1612         return beat;
1613 }
1614
1615 double
1616 TempoMap::beat_at_frame (const framecnt_t& frame) const
1617 {
1618         Glib::Threads::RWLock::ReaderLock lm (lock);
1619         return beat_at_frame_locked (_metrics, frame);
1620 }
1621
1622 /* meter section based */
1623 framecnt_t
1624 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1625 {
1626         const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1627         MeterSection* prev_m = 0;
1628
1629         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1630                 MeterSection* m;
1631                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1632                         if (prev_m && m->beat() > beat) {
1633                                 break;
1634                         }
1635                         prev_m = m;
1636                 }
1637         }
1638
1639         return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1640 }
1641
1642 framecnt_t
1643 TempoMap::frame_at_beat (const double& beat) const
1644 {
1645         Glib::Threads::RWLock::ReaderLock lm (lock);
1646         return frame_at_beat_locked (_metrics, beat);
1647 }
1648
1649 double
1650 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1651 {
1652         /* CALLER HOLDS READ LOCK */
1653
1654         MeterSection* prev_m = 0;
1655
1656         /* because audio-locked meters have 'fake' integral beats,
1657            there is no pulse offset here.
1658         */
1659         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1660                 MeterSection* m;
1661                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1662                         if (prev_m) {
1663                                 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1664                                 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1665                                         break;
1666                                 }
1667                         }
1668                         prev_m = m;
1669                 }
1670         }
1671
1672         const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1673         const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1674         const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1675
1676         return ret;
1677 }
1678
1679 double
1680 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1681 {
1682         Glib::Threads::RWLock::ReaderLock lm (lock);
1683         return bbt_to_beats_locked (_metrics, bbt);
1684 }
1685
1686 Timecode::BBT_Time
1687 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1688 {
1689         /* CALLER HOLDS READ LOCK */
1690         MeterSection* prev_m = 0;
1691         const double beats = (b < 0.0) ? 0.0 : b;
1692
1693         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1694                 MeterSection* m = 0;
1695
1696                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1697                         if (prev_m) {
1698                                 if (m->beat() > beats) {
1699                                         /* this is the meter after the one our beat is on*/
1700                                         break;
1701                                 }
1702                         }
1703
1704                         prev_m = m;
1705                 }
1706         }
1707
1708         const double beats_in_ms = beats - prev_m->beat();
1709         const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1710         const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1711         const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1712         const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1713
1714         BBT_Time ret;
1715
1716         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1717         ret.beats = (uint32_t) floor (remaining_beats);
1718         ret.bars = total_bars;
1719
1720         /* 0 0 0 to 1 1 0 - based mapping*/
1721         ++ret.bars;
1722         ++ret.beats;
1723
1724         if (ret.ticks >= BBT_Time::ticks_per_beat) {
1725                 ++ret.beats;
1726                 ret.ticks -= BBT_Time::ticks_per_beat;
1727         }
1728
1729         if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1730                 ++ret.bars;
1731                 ret.beats = 1;
1732         }
1733
1734         return ret;
1735 }
1736
1737 Timecode::BBT_Time
1738 TempoMap::beats_to_bbt (const double& beats)
1739 {
1740         Glib::Threads::RWLock::ReaderLock lm (lock);
1741         return beats_to_bbt_locked (_metrics, beats);
1742 }
1743
1744 Timecode::BBT_Time
1745 TempoMap::pulse_to_bbt (const double& pulse)
1746 {
1747         Glib::Threads::RWLock::ReaderLock lm (lock);
1748         MeterSection* prev_m = 0;
1749
1750         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1751                 MeterSection* m = 0;
1752
1753                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1754
1755                         if (prev_m) {
1756                                 double const pulses_to_m = m->pulse() - prev_m->pulse();
1757                                 if (prev_m->pulse() + pulses_to_m > pulse) {
1758                                         /* this is the meter after the one our beat is on*/
1759                                         break;
1760                                 }
1761                         }
1762
1763                         prev_m = m;
1764                 }
1765         }
1766
1767         const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1768         const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1769         const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1770         const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1771         const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1772
1773         BBT_Time ret;
1774
1775         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1776         ret.beats = (uint32_t) floor (remaining_beats);
1777         ret.bars = total_bars;
1778
1779         /* 0 0 0 to 1 1 0 mapping*/
1780         ++ret.bars;
1781         ++ret.beats;
1782
1783         if (ret.ticks >= BBT_Time::ticks_per_beat) {
1784                 ++ret.beats;
1785                 ret.ticks -= BBT_Time::ticks_per_beat;
1786         }
1787
1788         if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1789                 ++ret.bars;
1790                 ret.beats = 1;
1791         }
1792
1793         return ret;
1794 }
1795
1796 void
1797 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1798 {
1799
1800         if (frame < 0) {
1801                 bbt.bars = 1;
1802                 bbt.beats = 1;
1803                 bbt.ticks = 0;
1804                 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1805                 return;
1806         }
1807         Glib::Threads::RWLock::ReaderLock lm (lock);
1808         const double beat = beat_at_frame_locked (_metrics, frame);
1809
1810         bbt = beats_to_bbt_locked (_metrics, beat);
1811 }
1812
1813 /* meter section based */
1814 framepos_t
1815 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1816 {
1817         /* HOLD THE READER LOCK */
1818
1819         const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1820         return ret;
1821 }
1822
1823 framepos_t
1824 TempoMap::frame_time (const BBT_Time& bbt)
1825 {
1826         if (bbt.bars < 1) {
1827                 warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
1828                 return 0;
1829         }
1830
1831         if (bbt.beats < 1) {
1832                 throw std::logic_error ("beats are counted from one");
1833         }
1834         Glib::Threads::RWLock::ReaderLock lm (lock);
1835
1836         return frame_time_locked (_metrics, bbt);
1837 }
1838
1839 bool
1840 TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
1841 {
1842         TempoSection* prev_t = 0;
1843
1844         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1845                 TempoSection* t;
1846                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1847                         if (!t->active()) {
1848                                 continue;
1849                         }
1850                         if (prev_t) {
1851                                 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1852                                         return false;
1853                                 }
1854
1855                                 if (t->frame() == prev_t->frame()) {
1856                                         return false;
1857                                 }
1858
1859                                 /* precision check ensures pulses and frames align.*/
1860                                 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1861                                         return false;
1862                                 }
1863                         }
1864                         prev_t = t;
1865                 }
1866         }
1867
1868         return true;
1869 }
1870
1871 bool
1872 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1873 {
1874         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1875                 TempoSection* t;
1876                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1877                         if (!t->movable()) {
1878                                 t->set_active (true);
1879                                 continue;
1880                         }
1881                         if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1882                                 t->set_active (false);
1883                                 t->set_pulse (0.0);
1884                         } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1885                                 t->set_active (true);
1886                         } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1887                                 return false;
1888                         }
1889                 }
1890         }
1891         return true;
1892 }
1893
1894 bool
1895 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1896 {
1897         TempoSection* prev_t = 0;
1898         TempoSection* section_prev = 0;
1899         framepos_t first_m_frame = 0;
1900
1901         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1902                 MeterSection* m;
1903                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1904                         if (!m->movable()) {
1905                                 first_m_frame = m->frame();
1906                                 break;
1907                         }
1908                 }
1909         }
1910         if (section->movable() && frame <= first_m_frame) {
1911                 return false;
1912         }
1913
1914         section->set_active (true);
1915         section->set_frame (frame);
1916
1917         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1918                 TempoSection* t;
1919                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1920
1921                         if (!t->active()) {
1922                                 continue;
1923                         }
1924                         if (prev_t) {
1925                                 if (t == section) {
1926                                         section_prev = prev_t;
1927                                         continue;
1928                                 }
1929                                 if (t->position_lock_style() == MusicTime) {
1930                                         prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1931                                         t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1932                                 } else {
1933                                         prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1934                                         t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1935                                 }
1936                         }
1937                         prev_t = t;
1938                 }
1939         }
1940
1941         if (section_prev) {
1942                 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1943                 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1944         }
1945
1946         if (section->position_lock_style() == MusicTime) {
1947                 /* we're setting the frame */
1948                 section->set_position_lock_style (AudioTime);
1949                 recompute_tempos (imaginary);
1950                 section->set_position_lock_style (MusicTime);
1951         } else {
1952                 recompute_tempos (imaginary);
1953         }
1954
1955         if (check_solved (imaginary, true)) {
1956                 return true;
1957         }
1958
1959         MetricSectionFrameSorter fcmp;
1960         imaginary.sort (fcmp);
1961         if (section->position_lock_style() == MusicTime) {
1962                 /* we're setting the frame */
1963                 section->set_position_lock_style (AudioTime);
1964                 recompute_tempos (imaginary);
1965                 section->set_position_lock_style (MusicTime);
1966         } else {
1967                 recompute_tempos (imaginary);
1968         }
1969
1970         if (check_solved (imaginary, true)) {
1971                 return true;
1972         }
1973
1974         //dump (imaginary, std::cerr);
1975
1976         return false;
1977 }
1978
1979 bool
1980 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
1981 {
1982         TempoSection* prev_t = 0;
1983         TempoSection* section_prev = 0;
1984
1985         section->set_pulse (pulse);
1986
1987         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1988                 TempoSection* t;
1989                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1990                         if (!t->active()) {
1991                                 continue;
1992                         }
1993                         if (!t->movable()) {
1994                                 t->set_pulse (0.0);
1995                                 prev_t = t;
1996                                 continue;
1997                         }
1998                         if (prev_t) {
1999                                 if (t == section) {
2000                                         section_prev = prev_t;
2001                                         continue;
2002                                 }
2003                                 if (t->position_lock_style() == MusicTime) {
2004                                         prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2005                                         t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2006                                 } else {
2007                                         prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2008                                         t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2009                                 }
2010                         }
2011                         prev_t = t;
2012                 }
2013         }
2014         if (section_prev) {
2015                 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2016                 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2017         }
2018
2019         if (section->position_lock_style() == AudioTime) {
2020                 /* we're setting the pulse */
2021                 section->set_position_lock_style (MusicTime);
2022                 recompute_tempos (imaginary);
2023                 section->set_position_lock_style (AudioTime);
2024         } else {
2025                 recompute_tempos (imaginary);
2026         }
2027
2028         if (check_solved (imaginary, false)) {
2029                 return true;
2030         }
2031
2032         MetricSectionSorter cmp;
2033         imaginary.sort (cmp);
2034         if (section->position_lock_style() == AudioTime) {
2035                 /* we're setting the pulse */
2036                 section->set_position_lock_style (MusicTime);
2037                 recompute_tempos (imaginary);
2038                 section->set_position_lock_style (AudioTime);
2039         } else {
2040                 recompute_tempos (imaginary);
2041         }
2042
2043         if (check_solved (imaginary, false)) {
2044                 return true;
2045         }
2046
2047         //dump (imaginary, std::cerr);
2048
2049         return false;
2050 }
2051
2052 void
2053 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2054 {
2055         /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2056         const MeterSection* other =  &meter_section_at_locked (imaginary, frame);
2057         if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2058                 return;
2059         }
2060         MeterSection* prev_m = 0;
2061         if (!section->movable()) {
2062                 /* lock the first tempo to our first meter */
2063                 if (!set_active_tempos (imaginary, frame)) {
2064                         return;
2065                 }
2066                 TempoSection* first_t = &first_tempo();
2067                 Metrics future_map;
2068                 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
2069
2070                 new_section->set_frame (frame);
2071                 new_section->set_pulse (0.0);
2072                 new_section->set_active (true);
2073
2074                 if (solve_map (future_map, new_section, frame)) {
2075                         first_t->set_frame (frame);
2076                         first_t->set_pulse (0.0);
2077                         first_t->set_active (true);
2078                         solve_map (imaginary, first_t, frame);
2079                 } else {
2080                         return;
2081                 }
2082         }
2083
2084         TempoSection* meter_locked_tempo = 0;
2085
2086         if ((meter_locked_tempo = const_cast<TempoSection*>(&tempo_section_at_locked (imaginary, section->frame())))->frame() == section->frame()) {
2087                 if (!meter_locked_tempo->locked_to_meter()) {
2088                         meter_locked_tempo = 0;
2089                 }
2090         }
2091
2092         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2093                 MeterSection* m;
2094                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2095                         if (m == section){
2096                                 if (prev_m && section->movable()) {
2097                                         const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor());
2098                                         if (beats + prev_m->beat() < section->beat()) {
2099                                                 /* disallow position change if it will alter our beat
2100                                                    we allow tempo changes to do this in recompute_meters().
2101                                                    blocking this is an option, but i'm not convinced that
2102                                                    this is what the user would actually want.
2103                                                    here we set the frame/pulse corresponding to its musical position.
2104                                                 */
2105                                                 const double new_pulse = ((section->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
2106
2107                                                 if (meter_locked_tempo) {
2108                                                         Metrics future_map;
2109                                                         TempoSection* new_section = copy_metrics_and_point (future_map, meter_locked_tempo);
2110
2111                                                         new_section->set_active (true);
2112
2113                                                         if (solve_map (future_map, new_section, section->frame())) {
2114                                                                 meter_locked_tempo->set_active (true);
2115                                                                 solve_map (imaginary, meter_locked_tempo, section->frame());
2116                                                                 section->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2117                                                                 section->set_pulse (new_pulse);
2118                                                         } else {
2119                                                                 return;
2120                                                         }
2121                                                         meter_locked_tempo->set_frame (section->frame());
2122                                                         /* XX need to fake the pulse and prevent it from changing */
2123                                                         //meter_locked_tempo->set_pulse (pulse_at_frame_locked (imaginary, section->frame()));
2124                                                 }
2125                                                 return;
2126                                         } else {
2127                                                 if (meter_locked_tempo) {
2128                                                         Metrics future_map;
2129                                                         TempoSection* new_section = copy_metrics_and_point (future_map, meter_locked_tempo);
2130
2131                                                         new_section->set_active (true);
2132
2133                                                         if (solve_map (future_map, new_section, frame)) {
2134                                                                 meter_locked_tempo->set_active (true);
2135                                                                 solve_map (imaginary, meter_locked_tempo, frame);
2136                                                                 section->set_pulse (pulse_at_frame_locked (imaginary, frame));
2137                                                         } else {
2138                                                                 return;
2139                                                         }
2140                                                 }
2141                                         }
2142                                 } else {
2143                                         pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2144                                         section->set_beat (b_bbt);
2145                                         section->set_pulse (0.0);
2146
2147                                 }
2148                                 section->set_frame (frame);
2149                                 break;
2150                         }
2151
2152                         prev_m = m;
2153                 }
2154         }
2155
2156         MetricSectionFrameSorter fcmp;
2157         imaginary.sort (fcmp);
2158         if (section->position_lock_style() == MusicTime) {
2159                 /* we're setting the frame */
2160                 section->set_position_lock_style (AudioTime);
2161                 recompute_meters (imaginary);
2162                 section->set_position_lock_style (MusicTime);
2163         } else {
2164                 recompute_meters (imaginary);
2165         }
2166         //dump (imaginary, std::cerr);
2167 }
2168
2169 void
2170 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
2171 {
2172         MeterSection* prev_m = 0;
2173
2174         section->set_pulse (pulse);
2175
2176         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2177                 MeterSection* m;
2178                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2179                         if (prev_m && m == section){
2180                                 /* the first meter is always audio-locked, so prev_m should exist.
2181                                    should we allow setting audio locked meters by pulse?
2182                                 */
2183                                 const double beats = ((pulse - prev_m->pulse()) * prev_m->note_divisor());
2184                                 const int32_t bars = (beats + 1) / prev_m->divisions_per_bar();
2185                                 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), BBT_Time (bars + prev_m->bbt().bars, 1, 0));
2186                                 section->set_beat (b_bbt);
2187                                 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2188                                 break;
2189                         }
2190
2191                         prev_m = m;
2192                 }
2193         }
2194
2195         MetricSectionSorter cmp;
2196         imaginary.sort (cmp);
2197         if (section->position_lock_style() == AudioTime) {
2198                 /* we're setting the pulse */
2199                 section->set_position_lock_style (MusicTime);
2200                 recompute_meters (imaginary);
2201                 section->set_position_lock_style (AudioTime);
2202         } else {
2203                 recompute_meters (imaginary);
2204         }
2205 }
2206
2207 /** places a copy of _metrics into copy and returns a pointer
2208  *  to section's equivalent in copy.
2209  */
2210 TempoSection*
2211 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2212 {
2213         TempoSection* t;
2214         MeterSection* m;
2215         TempoSection* ret = 0;
2216
2217         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2218                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2219                         if (t == section) {
2220                                 if (t->position_lock_style() == MusicTime) {
2221                                         ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2222                                         ret->set_frame (t->frame());
2223                                 } else {
2224                                         ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2225                                         ret->set_pulse (t->pulse());
2226                                 }
2227                                 ret->set_c_func (t->c_func());
2228                                 ret->set_active (t->active());
2229                                 ret->set_movable (t->movable());
2230                                 ret->set_locked_to_meter (t->locked_to_meter());
2231                                 copy.push_back (ret);
2232                                 continue;
2233                         }
2234                         TempoSection* cp = 0;
2235                         if (t->position_lock_style() == MusicTime) {
2236                                 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2237                                 cp->set_frame (t->frame());
2238                         } else {
2239                                 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2240                                 cp->set_pulse (t->pulse());
2241                         }
2242                         cp->set_c_func (t->c_func());
2243                         cp->set_active (t->active());
2244                         cp->set_movable (t->movable());
2245                         cp->set_locked_to_meter (t->locked_to_meter());
2246                         copy.push_back (cp);
2247                 }
2248                 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2249                         MeterSection* cp = 0;
2250                         if (m->position_lock_style() == MusicTime) {
2251                                 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2252                                 cp->set_frame (m->frame());
2253                         } else {
2254                                 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2255                                 cp->set_pulse (m->pulse());
2256                         }
2257                         cp->set_movable (m->movable());
2258                         copy.push_back (cp);
2259                 }
2260         }
2261
2262         return ret;
2263 }
2264
2265 bool
2266 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2267 {
2268         Metrics copy;
2269         TempoSection* new_section = 0;
2270
2271         {
2272                 Glib::Threads::RWLock::ReaderLock lm (lock);
2273                 new_section = copy_metrics_and_point (copy, ts);
2274         }
2275
2276         const double beat = bbt_to_beats_locked (copy, bbt);
2277         const bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
2278
2279         Metrics::const_iterator d = copy.begin();
2280         while (d != copy.end()) {
2281                 delete (*d);
2282                 ++d;
2283         }
2284
2285         return ret;
2286 }
2287
2288 /**
2289 * 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,
2290 * taking any possible reordering as a consequence of this into account.
2291 * @param section - the section to be altered
2292 * @param bpm - the new Tempo
2293 * @param bbt - the bbt where the altered tempo will fall
2294 * @return returns - the position in frames where the new tempo section will lie.
2295 */
2296 framepos_t
2297 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2298 {
2299         Glib::Threads::RWLock::ReaderLock lm (lock);
2300         Metrics future_map;
2301         framepos_t ret = 0;
2302         TempoSection* new_section = copy_metrics_and_point (future_map, section);
2303
2304         const double beat = bbt_to_beats_locked (future_map, bbt);
2305
2306         if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2307                 ret = new_section->frame();
2308         } else {
2309                 ret = frame_at_beat_locked (_metrics, beat);
2310         }
2311
2312         Metrics::const_iterator d = future_map.begin();
2313         while (d != future_map.end()) {
2314                 delete (*d);
2315                 ++d;
2316         }
2317         return ret;
2318 }
2319
2320 double
2321 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2322 {
2323         Glib::Threads::RWLock::ReaderLock lm (lock);
2324         Metrics future_map;
2325         double ret = 0.0;
2326         TempoSection* new_section = copy_metrics_and_point (future_map, section);
2327
2328         if (solve_map (future_map, new_section, frame)) {
2329                 ret = new_section->pulse();
2330         } else {
2331                 ret = pulse_at_frame_locked (_metrics, frame);
2332         }
2333
2334         Metrics::const_iterator d = future_map.begin();
2335         while (d != future_map.end()) {
2336                 delete (*d);
2337                 ++d;
2338         }
2339         return ret;
2340 }
2341
2342 void
2343 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2344 {
2345         Metrics future_map;
2346         {
2347                 Glib::Threads::RWLock::WriterLock lm (lock);
2348                 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2349                 if (solve_map (future_map, new_section, frame)) {
2350                         solve_map (_metrics, ts, frame);
2351                         recompute_meters (_metrics);
2352                 }
2353         }
2354
2355         Metrics::const_iterator d = future_map.begin();
2356         while (d != future_map.end()) {
2357                 delete (*d);
2358                 ++d;
2359         }
2360
2361         MetricPositionChanged (); // Emit Signal
2362 }
2363
2364 void
2365 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2366 {
2367         Metrics future_map;
2368         {
2369                 Glib::Threads::RWLock::WriterLock lm (lock);
2370                 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2371                 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2372                         solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2373                         recompute_meters (_metrics);
2374                 }
2375         }
2376
2377         Metrics::const_iterator d = future_map.begin();
2378         while (d != future_map.end()) {
2379                 delete (*d);
2380                 ++d;
2381         }
2382
2383         MetricPositionChanged (); // Emit Signal
2384 }
2385
2386 void
2387 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t&  frame)
2388 {
2389         {
2390                 Glib::Threads::RWLock::WriterLock lm (lock);
2391                 solve_map (_metrics, ms, frame);
2392         }
2393
2394         MetricPositionChanged (); // Emit Signal
2395 }
2396
2397 void
2398 TempoMap::gui_move_meter (MeterSection* ms, const double& pulse)
2399 {
2400         {
2401                 Glib::Threads::RWLock::WriterLock lm (lock);
2402                 solve_map (_metrics, ms, pulse);
2403         }
2404
2405         MetricPositionChanged (); // Emit Signal
2406 }
2407
2408 bool
2409 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2410 {
2411         Metrics future_map;
2412         bool can_solve = false;
2413         {
2414                 Glib::Threads::RWLock::WriterLock lm (lock);
2415                 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2416                 new_section->set_beats_per_minute (bpm.beats_per_minute());
2417                 recompute_tempos (future_map);
2418
2419                 if (check_solved (future_map, true)) {
2420                         ts->set_beats_per_minute (bpm.beats_per_minute());
2421                         recompute_map (_metrics);
2422                         can_solve = true;
2423                 }
2424         }
2425
2426         Metrics::const_iterator d = future_map.begin();
2427         while (d != future_map.end()) {
2428                 delete (*d);
2429                 ++d;
2430         }
2431         if (can_solve) {
2432                 MetricPositionChanged (); // Emit Signal
2433         }
2434         return can_solve;
2435 }
2436
2437 void
2438 TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
2439 {
2440         Metrics future_map;
2441         TempoSection* ts = 0;
2442         {
2443                 Glib::Threads::RWLock::WriterLock lm (lock);
2444                 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2445                 TempoSection* prev_t = copy_metrics_and_point (future_map, ts);
2446                 TempoSection* prev_to_prev_t = 0;
2447                 const frameoffset_t fr_off = frame - ms->frame();
2448                 double new_bpm = 0.0;
2449
2450                 if (prev_t) {
2451                         prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2452                 }
2453
2454                 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2455                    constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2456                 */
2457                 double contribution = 0.0;
2458                 frameoffset_t frame_contribution = 0.0;
2459                 frameoffset_t prev_t_frame_contribution = 0.0;
2460
2461                 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2462                         /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2463                         contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2464                         frame_contribution = contribution * (double) fr_off;
2465                         prev_t_frame_contribution = fr_off - frame_contribution;
2466                 }
2467
2468                 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2469
2470                         if (prev_t->position_lock_style() == MusicTime) {
2471                                 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2472                                         new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2473                                                                                 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2474
2475                                 } else {
2476                                         /* prev to prev is irrelevant */
2477                                         const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
2478                                         const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2479
2480                                         if (frame_pulse != prev_t->pulse()) {
2481                                                 new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
2482                                         } else {
2483                                                 new_bpm = prev_t->beats_per_minute();
2484                                         }
2485                                 }
2486                         } else {
2487                                 /* AudioTime */
2488                                 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2489                                         new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2490                                                                                 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2491                                 } else {
2492                                         /* prev_to_prev_t is irrelevant */
2493
2494                                         if (frame != prev_t->frame()) {
2495                                                 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2496                                         } else {
2497                                                 new_bpm = prev_t->beats_per_minute();
2498                                         }
2499                                 }
2500                         }
2501                 } else if (prev_t->c_func() < 0.0) {
2502                         if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2503                                 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
2504                         } else {
2505                                 /* prev_to_prev_t is irrelevant */
2506                                 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
2507                         }
2508
2509                         const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2510                         if (diff > -0.1 && diff  < 0.1) {
2511                                 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2512                                                                         / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2513                         }
2514
2515                 } else if (prev_t->c_func() > 0.0) {
2516                         if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2517                                 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
2518                         } else {
2519                                 /* prev_to_prev_t is irrelevant */
2520                                 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
2521                         }
2522
2523                         /* limits - a bit clunky, but meh */
2524                         const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2525                         if (diff > -0.1 && diff  < 0.1) {
2526                                 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2527                                                                         / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2528                         }
2529                 }
2530
2531                 prev_t->set_beats_per_minute (new_bpm);
2532                 recompute_tempos (future_map);
2533
2534                 if (check_solved (future_map, true)) {
2535
2536                         prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2537                         prev_t->set_beats_per_minute (new_bpm);
2538                         recompute_tempos (_metrics);
2539
2540                         if (ms->position_lock_style() == AudioTime) {
2541                                 ms->set_frame (frame);
2542                         }
2543
2544                         recompute_meters (_metrics);
2545                 }
2546         }
2547
2548         Metrics::const_iterator d = future_map.begin();
2549         while (d != future_map.end()) {
2550                 delete (*d);
2551                 ++d;
2552         }
2553
2554         MetricPositionChanged (); // Emit Signal
2555         return;
2556 }
2557
2558 framecnt_t
2559 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2560 {
2561         Glib::Threads::RWLock::ReaderLock lm (lock);
2562
2563         const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2564         const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2565         const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2566
2567         return frame_at_beat_locked (_metrics, total_beats);
2568 }
2569
2570 framepos_t
2571 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2572 {
2573         return round_to_type (fr, dir, Bar);
2574 }
2575
2576 framepos_t
2577 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2578 {
2579         return round_to_type (fr, dir, Beat);
2580 }
2581
2582 framepos_t
2583 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2584 {
2585         Glib::Threads::RWLock::ReaderLock lm (lock);
2586         uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2587         uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2588         uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2589
2590         ticks -= beats * BBT_Time::ticks_per_beat;
2591
2592         if (dir > 0) {
2593                 /* round to next (or same iff dir == RoundUpMaybe) */
2594
2595                 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2596
2597                 if (mod == 0 && dir == RoundUpMaybe) {
2598                         /* right on the subdivision, which is fine, so do nothing */
2599
2600                 } else if (mod == 0) {
2601                         /* right on the subdivision, so the difference is just the subdivision ticks */
2602                         ticks += ticks_one_subdivisions_worth;
2603
2604                 } else {
2605                         /* not on subdivision, compute distance to next subdivision */
2606
2607                         ticks += ticks_one_subdivisions_worth - mod;
2608                 }
2609
2610                 if (ticks >= BBT_Time::ticks_per_beat) {
2611                         ticks -= BBT_Time::ticks_per_beat;
2612                 }
2613         } else if (dir < 0) {
2614
2615                 /* round to previous (or same iff dir == RoundDownMaybe) */
2616
2617                 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2618
2619                 if (difference == 0 && dir == RoundDownAlways) {
2620                         /* right on the subdivision, but force-rounding down,
2621                            so the difference is just the subdivision ticks */
2622                         difference = ticks_one_subdivisions_worth;
2623                 }
2624
2625                 if (ticks < difference) {
2626                         ticks = BBT_Time::ticks_per_beat - ticks;
2627                 } else {
2628                         ticks -= difference;
2629                 }
2630
2631         } else {
2632                 /* round to nearest */
2633                 double rem;
2634
2635                 /* compute the distance to the previous and next subdivision */
2636
2637                 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2638
2639                         /* closer to the next subdivision, so shift forward */
2640
2641                         ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2642
2643                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2644
2645                         if (ticks > BBT_Time::ticks_per_beat) {
2646                                 ++beats;
2647                                 ticks -= BBT_Time::ticks_per_beat;
2648                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2649                         }
2650
2651                 } else if (rem > 0) {
2652
2653                         /* closer to previous subdivision, so shift backward */
2654
2655                         if (rem > ticks) {
2656                                 if (beats == 0) {
2657                                         /* can't go backwards past zero, so ... */
2658                                         return 0;
2659                                 }
2660                                 /* step back to previous beat */
2661                                 --beats;
2662                                 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2663                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2664                         } else {
2665                                 ticks = lrint (ticks - rem);
2666                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2667                         }
2668                 } else {
2669                         /* on the subdivision, do nothing */
2670                 }
2671         }
2672
2673         const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2674
2675         return ret_frame;
2676 }
2677
2678 void
2679 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2680 {
2681         if (sub_num == -1) {
2682                 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2683                 if ((double) when.beats > bpb / 2.0) {
2684                         ++when.bars;
2685                 }
2686                 when.beats = 1;
2687                 when.ticks = 0;
2688                 return;
2689         } else if (sub_num == 0) {
2690                 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2691                 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2692                         ++when.beats;
2693                         while ((double) when.beats > bpb) {
2694                                 ++when.bars;
2695                                 when.beats -= (uint32_t) floor (bpb);
2696                         }
2697                 }
2698                 when.ticks = 0;
2699                 return;
2700         }
2701         const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2702         double rem;
2703         if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2704                 /* closer to the next subdivision, so shift forward */
2705
2706                 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2707
2708                 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2709                         ++when.beats;
2710                         when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2711                 }
2712
2713         } else if (rem > 0) {
2714                 /* closer to previous subdivision, so shift backward */
2715
2716                 if (rem > when.ticks) {
2717                         if (when.beats == 0) {
2718                                 /* can't go backwards past zero, so ... */
2719                         }
2720                         /* step back to previous beat */
2721                         --when.beats;
2722                         when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2723                 } else {
2724                         when.ticks = when.ticks - rem;
2725                 }
2726         }
2727 }
2728
2729 framepos_t
2730 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2731 {
2732         Glib::Threads::RWLock::ReaderLock lm (lock);
2733
2734         const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2735         BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2736
2737         switch (type) {
2738         case Bar:
2739                 if (dir < 0) {
2740                         /* find bar previous to 'frame' */
2741                         bbt.beats = 1;
2742                         bbt.ticks = 0;
2743                         return frame_time_locked (_metrics, bbt);
2744
2745                 } else if (dir > 0) {
2746                         /* find bar following 'frame' */
2747                         ++bbt.bars;
2748                         bbt.beats = 1;
2749                         bbt.ticks = 0;
2750                         return frame_time_locked (_metrics, bbt);
2751                 } else {
2752                         /* true rounding: find nearest bar */
2753                         framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2754                         bbt.beats = 1;
2755                         bbt.ticks = 0;
2756                         framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2757                         ++bbt.bars;
2758                         framepos_t next_ft = frame_time_locked (_metrics, bbt);
2759
2760                         if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) { 
2761                                 return next_ft;
2762                         } else {
2763                                 return prev_ft;
2764                         }
2765                 }
2766
2767                 break;
2768
2769         case Beat:
2770                 if (dir < 0) {
2771                         return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2772                 } else if (dir > 0) {
2773                         return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2774                 } else {
2775                         return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2776                 }
2777                 break;
2778         }
2779
2780         return 0;
2781 }
2782
2783 void
2784 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2785                     framepos_t lower, framepos_t upper)
2786 {
2787         Glib::Threads::RWLock::ReaderLock lm (lock);
2788         int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
2789         framecnt_t pos = 0;
2790         /* although the map handles negative beats, bbt doesn't. */
2791         if (cnt < 0.0) {
2792                 cnt = 0.0;
2793         }
2794         while (pos < upper) {
2795                 pos = frame_at_beat_locked (_metrics, cnt);
2796                 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
2797                 const MeterSection meter = meter_section_at_locked (_metrics, pos);
2798                 const BBT_Time bbt = beats_to_bbt (cnt);
2799                 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2800                 ++cnt;
2801         }
2802 }
2803
2804 const TempoSection&
2805 TempoMap::tempo_section_at (framepos_t frame) const
2806 {
2807         Glib::Threads::RWLock::ReaderLock lm (lock);
2808         return tempo_section_at_locked (_metrics, frame);
2809 }
2810
2811 const TempoSection&
2812 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
2813 {
2814         Metrics::const_iterator i;
2815         TempoSection* prev = 0;
2816
2817         for (i = metrics.begin(); i != metrics.end(); ++i) {
2818                 TempoSection* t;
2819
2820                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2821                         if (!t->active()) {
2822                                 continue;
2823                         }
2824                         if (prev && t->frame() > frame) {
2825                                 break;
2826                         }
2827
2828                         prev = t;
2829                 }
2830         }
2831
2832         if (prev == 0) {
2833                 fatal << endmsg;
2834                 abort(); /*NOTREACHED*/
2835         }
2836
2837         return *prev;
2838 }
2839
2840 const TempoSection&
2841 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
2842 {
2843         TempoSection* prev_t = 0;
2844         const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
2845
2846         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2847                 TempoSection* t;
2848                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2849                         if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
2850                                 break;
2851                         }
2852                         prev_t = t;
2853                 }
2854
2855         }
2856         return *prev_t;
2857 }
2858
2859 const TempoSection&
2860 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2861 {
2862         TempoSection* prev_t = 0;
2863
2864         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2865                 TempoSection* t;
2866                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2867                         if (prev_t && t->pulse() > pulse) {
2868                                 break;
2869                         }
2870                         prev_t = t;
2871                 }
2872
2873         }
2874         return *prev_t;
2875 }
2876
2877 /* don't use this to calculate length (the tempo is only correct for this frame).
2878    do that stuff based on the beat_at_frame and frame_at_beat api
2879 */
2880 double
2881 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2882 {
2883         Glib::Threads::RWLock::ReaderLock lm (lock);
2884
2885         const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
2886         const TempoSection* ts_after = 0;
2887         Metrics::const_iterator i;
2888
2889         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2890                 TempoSection* t;
2891
2892                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2893                         if (!t->active()) {
2894                                 continue;
2895                         }
2896                         if ((*i)->frame() > frame) {
2897                                 ts_after = t;
2898                                 break;
2899                         }
2900                 }
2901         }
2902
2903         if (ts_after) {
2904                 return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2905         }
2906         /* must be treated as constant tempo */
2907         return ts_at->frames_per_beat (_frame_rate);
2908 }
2909
2910 const Tempo
2911 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
2912 {
2913         TempoSection* prev_t = 0;
2914
2915         Metrics::const_iterator i;
2916
2917         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2918                 TempoSection* t;
2919                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2920                         if (!t->active()) {
2921                                 continue;
2922                         }
2923                         if ((prev_t) && t->frame() > frame) {
2924                                 /* t is the section past frame */
2925                                 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
2926                                 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
2927                                 return ret_tempo;
2928                         }
2929                         prev_t = t;
2930                 }
2931         }
2932
2933         const double ret = prev_t->beats_per_minute();
2934         const Tempo ret_tempo (ret, prev_t->note_type ());
2935
2936         return ret_tempo;
2937 }
2938
2939 const Tempo
2940 TempoMap::tempo_at (const framepos_t& frame) const
2941 {
2942         Glib::Threads::RWLock::ReaderLock lm (lock);
2943         return tempo_at_locked (_metrics, frame);
2944 }
2945
2946 const MeterSection&
2947 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
2948 {
2949         Metrics::const_iterator i;
2950         MeterSection* prev = 0;
2951
2952         for (i = metrics.begin(); i != metrics.end(); ++i) {
2953                 MeterSection* m;
2954
2955                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2956
2957                         if (prev && (*i)->frame() > frame) {
2958                                 break;
2959                         }
2960
2961                         prev = m;
2962                 }
2963         }
2964
2965         if (prev == 0) {
2966                 fatal << endmsg;
2967                 abort(); /*NOTREACHED*/
2968         }
2969
2970         return *prev;
2971 }
2972
2973
2974 const MeterSection&
2975 TempoMap::meter_section_at (framepos_t frame) const
2976 {
2977         Glib::Threads::RWLock::ReaderLock lm (lock);
2978         return meter_section_at_locked (_metrics, frame);
2979 }
2980
2981 const MeterSection&
2982 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
2983 {
2984         MeterSection* prev_m = 0;
2985
2986         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2987                 MeterSection* m;
2988                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2989                         if (prev_m && m->beat() > beat) {
2990                                 break;
2991                         }
2992                         prev_m = m;
2993                 }
2994
2995         }
2996         return *prev_m;
2997 }
2998
2999 const MeterSection&
3000 TempoMap::meter_section_at_beat (double beat) const
3001 {
3002         Glib::Threads::RWLock::ReaderLock lm (lock);
3003         return meter_section_at_beat_locked (_metrics, beat);
3004 }
3005
3006 const Meter&
3007 TempoMap::meter_at (framepos_t frame) const
3008 {
3009         TempoMetric m (metric_at (frame));
3010         return m.meter();
3011 }
3012
3013 void
3014 TempoMap::fix_legacy_session ()
3015 {
3016         MeterSection* prev_m = 0;
3017         TempoSection* prev_t = 0;
3018
3019         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3020                 MeterSection* m;
3021                 TempoSection* t;
3022
3023                 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3024                         if (!m->movable()) {
3025                                 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3026                                 m->set_beat (bbt);
3027                                 m->set_pulse (0.0);
3028                                 m->set_frame (0);
3029                                 m->set_position_lock_style (AudioTime);
3030                                 prev_m = m;
3031                                 continue;
3032                         }
3033                         if (prev_m) {
3034                                 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3035                                                                           + (m->bbt().beats - 1)
3036                                                                           + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3037                                                                           , m->bbt());
3038                                 m->set_beat (start);
3039                                 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3040                                         + (m->bbt().beats - 1)
3041                                         + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3042                                 m->set_pulse (start_beat / prev_m->note_divisor());
3043                         }
3044                         prev_m = m;
3045                 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3046
3047                         if (!t->active()) {
3048                                 continue;
3049                         }
3050
3051                         if (!t->movable()) {
3052                                 t->set_pulse (0.0);
3053                                 t->set_frame (0);
3054                                 t->set_position_lock_style (AudioTime);
3055                                 prev_t = t;
3056                                 continue;
3057                         }
3058
3059                         if (prev_t) {
3060                                 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3061                                         + (t->legacy_bbt().beats - 1)
3062                                         + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3063                                 if (prev_m) {
3064                                         t->set_pulse (beat / prev_m->note_divisor());
3065                                 } else {
3066                                         /* really shouldn't happen but.. */
3067                                         t->set_pulse (beat / 4.0);
3068                                 }
3069                         }
3070                         prev_t = t;
3071                 }
3072         }
3073 }
3074
3075 XMLNode&
3076 TempoMap::get_state ()
3077 {
3078         Metrics::const_iterator i;
3079         XMLNode *root = new XMLNode ("TempoMap");
3080
3081         {
3082                 Glib::Threads::RWLock::ReaderLock lm (lock);
3083                 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3084                         root->add_child_nocopy ((*i)->get_state());
3085                 }
3086         }
3087
3088         return *root;
3089 }
3090
3091 int
3092 TempoMap::set_state (const XMLNode& node, int /*version*/)
3093 {
3094         {
3095                 Glib::Threads::RWLock::WriterLock lm (lock);
3096
3097                 XMLNodeList nlist;
3098                 XMLNodeConstIterator niter;
3099                 Metrics old_metrics (_metrics);
3100                 _metrics.clear();
3101
3102                 nlist = node.children();
3103
3104                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3105                         XMLNode* child = *niter;
3106
3107                         if (child->name() == TempoSection::xml_state_node_name) {
3108
3109                                 try {
3110                                         TempoSection* ts = new TempoSection (*child);
3111                                         _metrics.push_back (ts);
3112                                 }
3113
3114                                 catch (failed_constructor& err){
3115                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3116                                         _metrics = old_metrics;
3117                                         break;
3118                                 }
3119
3120                         } else if (child->name() == MeterSection::xml_state_node_name) {
3121
3122                                 try {
3123                                         MeterSection* ms = new MeterSection (*child);
3124                                         _metrics.push_back (ms);
3125                                 }
3126
3127                                 catch (failed_constructor& err) {
3128                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3129                                         _metrics = old_metrics;
3130                                         break;
3131                                 }
3132                         }
3133                 }
3134
3135                 if (niter == nlist.end()) {
3136                         MetricSectionSorter cmp;
3137                         _metrics.sort (cmp);
3138                 }
3139
3140                 /* check for legacy sessions where bbt was the base musical unit for tempo */
3141                 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3142                         TempoSection* t;
3143                         if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3144                                 if (t->legacy_bbt().bars != 0) {
3145                                         fix_legacy_session();
3146                                         break;
3147                                 }
3148                                 break;
3149                         }
3150                 }
3151
3152                 /* check for multiple tempo/meters at the same location, which
3153                    ardour2 somehow allowed.
3154                 */
3155
3156                 Metrics::iterator prev = _metrics.end();
3157                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3158                         if (prev != _metrics.end()) {
3159                                 MeterSection* ms;
3160                                 MeterSection* prev_m;
3161                                 TempoSection* ts;
3162                                 TempoSection* prev_t;
3163                                 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3164                                         if (prev_m->pulse() == ms->pulse()) {
3165                                                 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3166                                                 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3167                                                 return -1;
3168                                         }
3169                                 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3170                                         if (prev_t->pulse() == ts->pulse()) {
3171                                                 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3172                                                 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3173                                                 return -1;
3174                                         }
3175                                 }
3176                         }
3177                         prev = i;
3178                 }
3179
3180                 recompute_map (_metrics);
3181         }
3182
3183         PropertyChanged (PropertyChange ());
3184
3185         return 0;
3186 }
3187
3188 void
3189 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3190 {
3191         Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3192         const MeterSection* m;
3193         const TempoSection* t;
3194         const TempoSection* prev_t = 0;
3195
3196         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3197
3198                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3199                         o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3200                           << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3201                         o << "current      : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3202                         if (prev_t) {
3203                                 o << "previous     : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3204                                 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;
3205                         }
3206                         prev_t = t;
3207                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3208                         o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3209                           << " pulse: " << m->pulse() <<  " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3210                 }
3211         }
3212         o << "------" << std::endl;
3213 }
3214
3215 int
3216 TempoMap::n_tempos() const
3217 {
3218         Glib::Threads::RWLock::ReaderLock lm (lock);
3219         int cnt = 0;
3220
3221         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3222                 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3223                         cnt++;
3224                 }
3225         }
3226
3227         return cnt;
3228 }
3229
3230 int
3231 TempoMap::n_meters() const
3232 {
3233         Glib::Threads::RWLock::ReaderLock lm (lock);
3234         int cnt = 0;
3235
3236         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3237                 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3238                         cnt++;
3239                 }
3240         }
3241
3242         return cnt;
3243 }
3244
3245 void
3246 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3247 {
3248         {
3249                 Glib::Threads::RWLock::WriterLock lm (lock);
3250                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3251                         if ((*i)->frame() >= where && (*i)->movable ()) {
3252                                 (*i)->set_frame ((*i)->frame() + amount);
3253                         }
3254                 }
3255
3256                 /* now reset the BBT time of all metrics, based on their new
3257                  * audio time. This is the only place where we do this reverse
3258                  * timestamp.
3259                  */
3260
3261                 Metrics::iterator i;
3262                 const MeterSection* meter;
3263                 const TempoSection* tempo;
3264                 MeterSection *m;
3265                 TempoSection *t;
3266
3267                 meter = &first_meter ();
3268                 tempo = &first_tempo ();
3269
3270                 BBT_Time start;
3271                 BBT_Time end;
3272
3273                 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3274
3275                 bool first = true;
3276                 MetricSection* prev = 0;
3277
3278                 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3279
3280                         BBT_Time bbt;
3281                         //TempoMetric metric (*meter, *tempo);
3282                         MeterSection* ms = const_cast<MeterSection*>(meter);
3283                         TempoSection* ts = const_cast<TempoSection*>(tempo);
3284                         if (prev) {
3285                                 if (ts){
3286                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3287                                                 if (!t->active()) {
3288                                                         continue;
3289                                                 }
3290                                                 ts->set_pulse (t->pulse());
3291                                         }
3292                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3293                                                 ts->set_pulse (m->pulse());
3294                                         }
3295                                         ts->set_frame (prev->frame());
3296
3297                                 }
3298                                 if (ms) {
3299                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3300                                                 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3301                                                 ms->set_beat (start);
3302                                                 ms->set_pulse (m->pulse());
3303                                         }
3304                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3305                                                 if (!t->active()) {
3306                                                         continue;
3307                                                 }
3308                                                 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3309                                                 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3310                                                 ms->set_beat (start);
3311                                                 ms->set_pulse (t->pulse());
3312                                         }
3313                                         ms->set_frame (prev->frame());
3314                                 }
3315
3316                         } else {
3317                                 // metric will be at frames=0 bbt=1|1|0 by default
3318                                 // which is correct for our purpose
3319                         }
3320
3321                         // cerr << bbt << endl;
3322
3323                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3324                                 if (!t->active()) {
3325                                         continue;
3326                                 }
3327                                 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3328                                 tempo = t;
3329                                 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3330                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3331                                 bbt_time (m->frame(), bbt);
3332
3333                                 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3334
3335                                 if (first) {
3336                                         first = false;
3337                                 } else {
3338
3339                                         if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3340                                                 /* round up to next beat */
3341                                                 bbt.beats += 1;
3342                                         }
3343
3344                                         bbt.ticks = 0;
3345
3346                                         if (bbt.beats != 1) {
3347                                                 /* round up to next bar */
3348                                                 bbt.bars += 1;
3349                                                 bbt.beats = 1;
3350                                         }
3351                                 }
3352                                 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3353                                 m->set_beat (start);
3354                                 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3355                                 meter = m;
3356                                 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3357                         } else {
3358                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3359                                 abort(); /*NOTREACHED*/
3360                         }
3361
3362                         prev = (*i);
3363                 }
3364
3365                 recompute_map (_metrics);
3366         }
3367
3368
3369         PropertyChanged (PropertyChange ());
3370 }
3371 bool
3372 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3373 {
3374         bool moved = false;
3375
3376         std::list<MetricSection*> metric_kill_list;
3377
3378         TempoSection* last_tempo = NULL;
3379         MeterSection* last_meter = NULL;
3380         bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3381         bool meter_after = false; // is there a meter marker likewise?
3382         {
3383                 Glib::Threads::RWLock::WriterLock lm (lock);
3384                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3385                         if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3386                                 metric_kill_list.push_back(*i);
3387                                 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3388                                 if (lt)
3389                                         last_tempo = lt;
3390                                 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3391                                 if (lm)
3392                                         last_meter = lm;
3393                         }
3394                         else if ((*i)->frame() >= where) {
3395                                 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3396                                 (*i)->set_frame ((*i)->frame() - amount);
3397                                 if ((*i)->frame() == where) {
3398                                         // marker was immediately after end of range
3399                                         tempo_after = dynamic_cast<TempoSection*> (*i);
3400                                         meter_after = dynamic_cast<MeterSection*> (*i);
3401                                 }
3402                                 moved = true;
3403                         }
3404                 }
3405
3406                 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3407                 if (last_tempo && !tempo_after) {
3408                         metric_kill_list.remove(last_tempo);
3409                         last_tempo->set_frame(where);
3410                         moved = true;
3411                 }
3412                 if (last_meter && !meter_after) {
3413                         metric_kill_list.remove(last_meter);
3414                         last_meter->set_frame(where);
3415                         moved = true;
3416                 }
3417
3418                 //remove all the remaining metrics
3419                 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3420                         _metrics.remove(*i);
3421                         moved = true;
3422                 }
3423
3424                 if (moved) {
3425                         recompute_map (_metrics);
3426                 }
3427         }
3428         PropertyChanged (PropertyChange ());
3429         return moved;
3430 }
3431
3432 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3433  *  pos can be -ve, if required.
3434  */
3435 framepos_t
3436 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3437 {
3438         return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3439 }
3440
3441 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3442 framepos_t
3443 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3444 {
3445         return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3446 }
3447
3448 /** Add the BBT interval op to pos and return the result */
3449 framepos_t
3450 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3451 {
3452         Glib::Threads::RWLock::ReaderLock lm (lock);
3453
3454         BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3455         pos_bbt.ticks += op.ticks;
3456         if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3457                 ++pos_bbt.beats;
3458                 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3459         }
3460         pos_bbt.beats += op.beats;
3461         /* the meter in effect will start on the bar */
3462         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();
3463         while (pos_bbt.beats >= divisions_per_bar + 1) {
3464                 ++pos_bbt.bars;
3465                 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3466                 pos_bbt.beats -= divisions_per_bar;
3467         }
3468         pos_bbt.bars += op.bars;
3469
3470         return frame_time_locked (_metrics, pos_bbt);
3471 }
3472
3473 /** Count the number of beats that are equivalent to distance when going forward,
3474     starting at pos.
3475 */
3476 Evoral::Beats
3477 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3478 {
3479         return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3480 }
3481
3482 struct bbtcmp {
3483     bool operator() (const BBT_Time& a, const BBT_Time& b) {
3484             return a < b;
3485     }
3486 };
3487
3488 std::ostream&
3489 operator<< (std::ostream& o, const Meter& m) {
3490         return o << m.divisions_per_bar() << '/' << m.note_divisor();
3491 }
3492
3493 std::ostream&
3494 operator<< (std::ostream& o, const Tempo& t) {
3495         return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3496 }
3497
3498 std::ostream&
3499 operator<< (std::ostream& o, const MetricSection& section) {
3500
3501         o << "MetricSection @ " << section.frame() << ' ';
3502
3503         const TempoSection* ts;
3504         const MeterSection* ms;
3505
3506         if ((ts = dynamic_cast<const TempoSection*> (&section)) != 0) {
3507                 o << *((const Tempo*) ts);
3508         } else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
3509                 o << *((const Meter*) ms);
3510         }
3511
3512         return o;
3513 }