fix mb32 well-known EQ (4 bands, different ports)
[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 "pbd/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, 4.0, 120.0);
49
50 framepos_t
51 MetricSection::frame_at_minute (const double& time) const
52 {
53         return (framepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
54 }
55
56 double
57 MetricSection::minute_at_frame (const framepos_t& frame) const
58 {
59         return (frame / (double) _sample_rate) / 60.0;
60 }
61
62 /***********************************************************************/
63
64 double
65 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
66 {
67         /* This is tempo- and meter-sensitive. The number it returns
68            is based on the interval between any two lines in the
69            grid that is constructed from tempo and meter sections.
70
71            The return value IS NOT interpretable in terms of "beats".
72         */
73
74         return (60.0 * sr) / (tempo.note_types_per_minute() * (_note_type / tempo.note_type()));
75 }
76
77 double
78 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
79 {
80         return frames_per_grid (tempo, sr) * _divisions_per_bar;
81 }
82
83 /***********************************************************************/
84
85 const string TempoSection::xml_state_node_name = "Tempo";
86
87 TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate)
88         : MetricSection (0.0, 0, MusicTime, true, sample_rate)
89         , Tempo (TempoMap::default_tempo())
90         , _c (0.0)
91         , _active (true)
92         , _locked_to_meter (false)
93         , _clamped (false)
94         , _legacy_end (false)
95 {
96         XMLProperty const * prop;
97         LocaleGuard lg;
98         BBT_Time bbt;
99         double pulse;
100         uint32_t frame;
101
102         _legacy_bbt = BBT_Time (0, 0, 0);
103
104         if ((prop = node.property ("start")) != 0) {
105                 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
106                             &bbt.bars,
107                             &bbt.beats,
108                             &bbt.ticks) == 3) {
109                         /* legacy session - start used to be in bbt*/
110                         _legacy_bbt = bbt;
111                         pulse = -1.0;
112                         info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
113                 }
114         }
115
116         if ((prop = node.property ("pulse")) != 0) {
117                 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
118                         error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
119                 }
120         }
121
122         set_pulse (pulse);
123
124         if ((prop = node.property ("frame")) != 0) {
125                 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
126                         error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
127                         throw failed_constructor();
128                 } else {
129                         set_minute (minute_at_frame (frame));
130                 }
131         }
132
133         /* XX replace old beats-per-minute name with note-types-per-minute */
134         if ((prop = node.property ("beats-per-minute")) != 0) {
135                 if (sscanf (prop->value().c_str(), "%lf", &_note_types_per_minute) != 1 || _note_types_per_minute < 0.0) {
136                         error << _("TempoSection XML node has an illegal \"beats-per-minute\" value") << endmsg;
137                         throw failed_constructor();
138                 }
139         }
140
141         if ((prop = node.property ("note-type")) == 0) {
142                 /* older session, make note type be quarter by default */
143                 _note_type = 4.0;
144         } else {
145                 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
146                         error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
147                         throw failed_constructor();
148                 }
149         }
150
151         if ((prop = node.property ("clamped")) == 0) {
152                 warning << _("TempoSection XML node has no \"clamped\" property") << endmsg;
153                 set_clamped (false);
154         } else {
155                 set_clamped (string_is_affirmative (prop->value()));
156         }
157
158         /* XX replace old end-beats-per-minute name with note-types-per-minute */
159         if ((prop = node.property ("end-beats-per-minute")) != 0) {
160                 if (sscanf (prop->value().c_str(), "%lf", &_end_note_types_per_minute) != 1 || _end_note_types_per_minute < 0.0) {
161                         info << _("TempoSection XML node has an illegal \"in-beats-per-minute\" value") << endmsg;
162                         //throw failed_constructor();
163                         _end_note_types_per_minute = _note_types_per_minute;
164                         _legacy_end = true;
165                 }
166         } else {
167                 _legacy_end = true;
168         }
169
170         if ((prop = node.property ("tempo-type")) != 0) {
171                 TempoSection::Type old_type;
172
173                 old_type = Type (string_2_enum (prop->value(), old_type));
174                 if (old_type == TempoSection::Constant) {
175                         _end_note_types_per_minute = _note_types_per_minute;
176                 }
177         }
178
179         if ((prop = node.property ("movable")) == 0) {
180                 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
181                 throw failed_constructor();
182         }
183
184         set_initial (!string_is_affirmative (prop->value()));
185
186         if ((prop = node.property ("active")) == 0) {
187                 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
188                 set_active(true);
189         } else {
190                 set_active (string_is_affirmative (prop->value()));
191         }
192
193         if ((prop = node.property ("lock-style")) == 0) {
194                 if (!initial()) {
195                         set_position_lock_style (MusicTime);
196                 } else {
197                         set_position_lock_style (AudioTime);
198                 }
199         } else {
200                 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
201         }
202
203         if ((prop = node.property ("locked-to-meter")) == 0) {
204                 if (initial()) {
205                         set_locked_to_meter (true);
206                 } else {
207                         set_locked_to_meter (false);
208                 }
209         } else {
210                 set_locked_to_meter (string_is_affirmative (prop->value()));
211         }
212
213         /* 5.5 marked initial tempo as not locked to meter. this should always be true anyway */
214         if (initial()) {
215                 set_locked_to_meter (true);
216         }
217 }
218
219 XMLNode&
220 TempoSection::get_state() const
221 {
222         XMLNode *root = new XMLNode (xml_state_node_name);
223         char buf[256];
224         LocaleGuard lg;
225
226         snprintf (buf, sizeof (buf), "%lf", pulse());
227         root->add_property ("pulse", buf);
228         snprintf (buf, sizeof (buf), "%li", frame());
229         root->add_property ("frame", buf);
230         snprintf (buf, sizeof (buf), "%lf", _note_types_per_minute);
231         root->add_property ("beats-per-minute", buf);
232         snprintf (buf, sizeof (buf), "%lf", _note_type);
233         root->add_property ("note-type", buf);
234         snprintf (buf, sizeof (buf), "%s", _clamped?"yes":"no");
235         root->add_property ("clamped", buf);
236         snprintf (buf, sizeof (buf), "%lf", _end_note_types_per_minute);
237         root->add_property ("end-beats-per-minute", buf);
238         snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
239         root->add_property ("movable", buf);
240         snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
241         root->add_property ("active", buf);
242         root->add_property ("lock-style", enum_2_string (position_lock_style()));
243         root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
244
245         return *root;
246 }
247
248 /** returns the Tempo at the session-relative minute.
249 */
250 Tempo
251 TempoSection::tempo_at_minute (const double& m) const
252 {
253         const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
254         if (constant) {
255                 return Tempo (note_types_per_minute(), note_type());
256         }
257
258         return Tempo (_tempo_at_time (m - minute()), _note_type, _end_note_types_per_minute);
259 }
260
261 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
262  *  @param ntpm the tempo in mote types per minute used to calculate the returned minute
263  *  @param p the pulse used to calculate the returned minute for constant tempi
264  *  @return the minute at the supplied tempo
265  *
266  *  note that the note_type is currently ignored in this function. see below.
267  *
268 */
269
270 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
271  *  there should be no ramp between the two even if we are ramped.
272  *  in other words a ramp should only place a curve on note_types_per_minute.
273  *  we should be able to use Tempo note type here, but the above
274  *  complicates things a bit.
275 */
276 double
277 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
278 {
279         const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
280         if (constant) {
281                 return ((p - pulse()) / pulses_per_minute()) + minute();
282         }
283
284         return _time_at_tempo (ntpm) + minute();
285 }
286
287 /** returns the Tempo at the supplied whole-note pulse.
288  */
289 Tempo
290 TempoSection::tempo_at_pulse (const double& p) const
291 {
292         const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
293
294         if (constant) {
295                 return Tempo (note_types_per_minute(), note_type());
296         }
297
298         return Tempo (_tempo_at_pulse (p - pulse()), _note_type, _end_note_types_per_minute);
299 }
300
301 /** returns the whole-note pulse where a tempo in note types per minute occurs.
302  *  constant tempi require minute m.
303  *  @param ntpm the note types per minute value used to calculate the returned pulse
304  *  @param m the minute used to calculate the returned pulse if the tempo is constant
305  *  @return the whole-note pulse at the supplied tempo
306  *
307  *  note that note_type is currently ignored in this function. see minute_at_tempo().
308  *
309  *  for constant tempi, this is anaologous to pulse_at_minute().
310 */
311 double
312 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
313 {
314         const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
315         if (constant) {
316                 return ((m - minute()) * pulses_per_minute()) + pulse();
317         }
318
319         return _pulse_at_tempo (ntpm) + pulse();
320 }
321
322 /** returns the whole-note pulse at the supplied session-relative minute.
323 */
324 double
325 TempoSection::pulse_at_minute (const double& m) const
326 {
327         const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
328         if (constant) {
329                 return ((m - minute()) * pulses_per_minute()) + pulse();
330         }
331
332         return _pulse_at_time (m - minute()) + pulse();
333 }
334
335 /** returns the session-relative minute at the supplied whole-note pulse.
336 */
337 double
338 TempoSection::minute_at_pulse (const double& p) const
339 {
340         const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
341         if (constant) {
342                 return ((p - pulse()) / pulses_per_minute()) + minute();
343         }
344
345         return _time_at_pulse (p - pulse()) + minute();
346 }
347
348 /** returns thw whole-note pulse at session frame position f.
349  *  @param f the frame position.
350  *  @return the position in whole-note pulses corresponding to f
351  *
352  *  for use with musical units whose granularity is coarser than frames (e.g. ticks)
353 */
354 double
355 TempoSection::pulse_at_frame (const framepos_t& f) const
356 {
357         const bool constant = type() == Constant || _c == 0.0 || (initial() && f < frame());
358         if (constant) {
359                 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
360         }
361
362         return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
363 }
364
365 framepos_t
366 TempoSection::frame_at_pulse (const double& p) const
367 {
368         const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
369         if (constant) {
370                 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
371         }
372
373         return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
374 }
375
376 /*
377 Ramp Overview
378
379       |                     *
380 Tempo |                   *
381 Tt----|-----------------*|
382 Ta----|--------------|*  |
383       |            * |   |
384       |         *    |   |
385       |     *        |   |
386 T0----|*             |   |
387   *   |              |   |
388       _______________|___|____
389       time           a   t (next tempo)
390       [        c         ] defines c
391
392 Duration in beats at time a is the integral of some Tempo function.
393 In our case, the Tempo function (Tempo at time t) is
394 T(t) = T0(e^(ct))
395
396 with function constant
397 c = log(Ta/T0)/a
398 so
399 a = log(Ta/T0)/c
400
401 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
402 b(t) = T0(e^(ct) - 1) / c
403
404 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:
405 t(b) = log((c.b / T0) + 1) / c
406
407 The time t at which Tempo T occurs is a as above:
408 t(T) = log(T / T0) / c
409
410 The beat at which a Tempo T occurs is:
411 b(T) = (T - T0) / c
412
413 The Tempo at which beat b occurs is:
414 T(b) = b.c + T0
415
416 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
417 Our problem is that we usually don't know t.
418 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.
419 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
420 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
421
422 By substituting our expanded t as a in the c function above, our problem is reduced to:
423 c = T0 (e^(log (Ta / T0)) - 1) / b
424
425 Of course the word 'beat' has been left loosely defined above.
426 In music, a beat is defined by the musical pulse (which comes from the tempo)
427 and the meter in use at a particular time (how many  pulse divisions there are in one bar).
428 It would be more accurate to substitute the work 'pulse' for 'beat' above.
429
430 Anyway ...
431
432 We can now store c for future time calculations.
433 If the following tempo section (the one that defines c in conjunction with this one)
434 is changed or moved, c is no longer valid.
435
436 The public methods are session-relative.
437
438 Most of this stuff is taken from this paper:
439
440 WHERE’S THE BEAT?
441 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
442 Jan C. Schacher
443 Martin Neukom
444 Zurich University of Arts
445 Institute for Computer Music and Sound Technology
446
447 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
448
449 */
450
451 /** compute this ramp's function constant from some tempo-pulse point
452  * @param end_npm end tempo (in note types per minute)
453  * @param end_pulse duration (pulses into global start) of some other position.
454  * @return the calculated function constant
455 */
456 double
457 TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const
458 {
459         if (note_types_per_minute() == end_npm || type() == Constant) {
460                 return 0.0;
461         }
462
463         double const log_tempo_ratio = log (end_npm / note_types_per_minute());
464         return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
465 }
466
467 /** compute the function constant from some tempo-time point.
468  * @param end_npm tempo (note types/min.)
469  * @param end_minute distance (in minutes) from session origin
470  * @return the calculated function constant
471 */
472 double
473 TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const
474 {
475         if (note_types_per_minute() == end_npm || type() == Constant) {
476                 return 0.0;
477         }
478
479         return c_func (end_npm, end_minute - minute());
480 }
481
482 /* position function */
483 double
484 TempoSection::a_func (double end_npm, double c) const
485 {
486         return log (end_npm / note_types_per_minute()) / c;
487 }
488
489 /*function constant*/
490 double
491 TempoSection::c_func (double end_npm, double end_time) const
492 {
493         return log (end_npm / note_types_per_minute()) / end_time;
494 }
495
496 /* tempo in note types per minute at time in minutes */
497 double
498 TempoSection::_tempo_at_time (const double& time) const
499 {
500         return exp (_c * time) * note_types_per_minute();
501 }
502
503 /* time in minutes at tempo in note types per minute */
504 double
505 TempoSection::_time_at_tempo (const double& npm) const
506 {
507         return log (npm / note_types_per_minute()) / _c;
508 }
509
510 /* pulse at tempo in note types per minute */
511 double
512 TempoSection::_pulse_at_tempo (const double& npm) const
513 {
514         return ((npm - note_types_per_minute()) / _c) / _note_type;
515 }
516
517 /* tempo in note types per minute at pulse */
518 double
519 TempoSection::_tempo_at_pulse (const double& pulse) const
520 {
521         return (pulse * _note_type * _c) + note_types_per_minute();
522 }
523
524 /* pulse at time in minutes */
525 double
526 TempoSection::_pulse_at_time (const double& time) const
527 {
528         return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
529 }
530
531 /* time in minutes at pulse */
532 double
533 TempoSection::_time_at_pulse (const double& pulse) const
534 {
535         return log1p ((_c * pulse * _note_type) / note_types_per_minute()) / _c;
536 }
537
538 /***********************************************************************/
539
540 const string MeterSection::xml_state_node_name = "Meter";
541
542 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
543         : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
544 {
545         XMLProperty const * prop;
546         LocaleGuard lg;
547         BBT_Time bbt;
548         double pulse = 0.0;
549         double beat = 0.0;
550         framepos_t frame = 0;
551         pair<double, BBT_Time> start;
552
553         if ((prop = node.property ("start")) != 0) {
554                 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
555                     &bbt.bars,
556                     &bbt.beats,
557                     &bbt.ticks) < 3) {
558                         error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
559                 } else {
560                         /* legacy session - start used to be in bbt*/
561                         info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
562                         pulse = -1.0;
563                 }
564         }
565
566         if ((prop = node.property ("pulse")) != 0) {
567                 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
568                         error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
569                 }
570         }
571         set_pulse (pulse);
572
573         if ((prop = node.property ("beat")) != 0) {
574                 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
575                         error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
576                 }
577         }
578
579         start.first = beat;
580
581         if ((prop = node.property ("bbt")) == 0) {
582                 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
583         } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
584                     &bbt.bars,
585                     &bbt.beats,
586                     &bbt.ticks) < 3) {
587                 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
588                 throw failed_constructor();
589         }
590
591         start.second = bbt;
592         set_beat (start);
593
594         if ((prop = node.property ("frame")) != 0) {
595                 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
596                         error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
597                         throw failed_constructor();
598                 } else {
599                         set_minute (minute_at_frame (frame));
600                 }
601         }
602
603         /* beats-per-bar is old; divisions-per-bar is new */
604
605         if ((prop = node.property ("divisions-per-bar")) == 0) {
606                 if ((prop = node.property ("beats-per-bar")) == 0) {
607                         error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
608                         throw failed_constructor();
609                 }
610         }
611         if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
612                 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
613                 throw failed_constructor();
614         }
615
616         if ((prop = node.property ("note-type")) == 0) {
617                 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
618                 throw failed_constructor();
619         }
620         if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
621                 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
622                 throw failed_constructor();
623         }
624
625         if ((prop = node.property ("movable")) == 0) {
626                 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
627                 throw failed_constructor();
628         }
629
630         set_initial (!string_is_affirmative (prop->value()));
631
632         if ((prop = node.property ("lock-style")) == 0) {
633                 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
634                 if (!initial()) {
635                         set_position_lock_style (MusicTime);
636                 } else {
637                         set_position_lock_style (AudioTime);
638                 }
639         } else {
640                 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
641         }
642 }
643
644 XMLNode&
645 MeterSection::get_state() const
646 {
647         XMLNode *root = new XMLNode (xml_state_node_name);
648         char buf[256];
649         LocaleGuard lg;
650
651         snprintf (buf, sizeof (buf), "%lf", pulse());
652         root->add_property ("pulse", buf);
653         snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
654                   bbt().bars,
655                   bbt().beats,
656                   bbt().ticks);
657         root->add_property ("bbt", buf);
658         snprintf (buf, sizeof (buf), "%lf", beat());
659         root->add_property ("beat", buf);
660         snprintf (buf, sizeof (buf), "%lf", _note_type);
661         root->add_property ("note-type", buf);
662         snprintf (buf, sizeof (buf), "%li", frame());
663         root->add_property ("frame", buf);
664         root->add_property ("lock-style", enum_2_string (position_lock_style()));
665         snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
666         root->add_property ("divisions-per-bar", buf);
667         snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
668         root->add_property ("movable", buf);
669
670         return *root;
671 }
672
673 /***********************************************************************/
674 /*
675   Tempo Map Overview
676
677   Tempo determines the rate of musical pulse determined by its components
678         note types per minute - the rate per minute of the whole note divisor _note_type
679         note type             - the division of whole notes (pulses) which occur at the rate of note types per minute.
680   Meter divides the musical pulse into measures and beats according to its components
681         divisions_per_bar
682         note_divisor
683
684   TempoSection - translates between time, musical pulse and tempo.
685         has a musical location in whole notes (pulses).
686         has a time location in minutes.
687         Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
688         (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
689
690   MeterSection - translates between BBT, meter-based beat and musical pulse.
691         has a musical location in whole notes (pulses)
692         has a musical location in meter-based beats
693         has a musical location in BBT time
694         has a time location expressed in minutes.
695
696   TempoSection and MeterSection may be locked to either audio or music (position lock style).
697   The lock style determines the location type to be kept as a reference when location is recalculated.
698
699   The first tempo and meter are special. they must move together, and are locked to audio.
700   Audio locked tempi which lie before the first meter are made inactive.
701
702   Recomputing the map is the process where the 'missing' location types are calculated.
703         We construct the tempo map by first using the locked location type of each section
704         to determine non-locked location types (pulse or minute position).
705         We then use this map to find the pulse or minute position of each meter (again depending on lock style).
706
707   Having done this, we can now traverse the Metrics list by pulse or minute
708   to query its relevant meter/tempo.
709
710   It is important to keep the _metrics in an order that makes sense.
711   Because ramped MusicTime and AudioTime tempos can interact with each other,
712   reordering is frequent. Care must be taken to keep _metrics in a solved state.
713   Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
714
715   Music and Audio
716
717   Music and audio-locked objects may seem interchangeable on the surface, but when translating
718   between audio samples and beat, remember that a sample is only a quantised approximation
719   of the actual time (in minutes) of a beat.
720   Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
721   mean that this frame is the actual location in time of 1|3|0.
722
723   You cannot use a frame measurement to determine beat distance except under special circumstances
724   (e.g. where the user has requested that a beat lie on a SMPTE frame or if the tempo is known to be constant over the duration).
725
726   This means is that a user operating on a musical grid must supply the desired beat position and/or current beat quantization in order for the
727   sample space the user is operating at to be translated correctly to the object.
728
729   The current approach is to interpret the supplied frame using the grid division the user has currently selected.
730   If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
731   the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
732
733   tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
734
735   When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
736   The result is rounded to audio frames.
737   When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
738
739   So :
740   frame_at_beat (beat_at_frame (frame)) == frame
741   but :
742   beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
743
744   Doing the second one will result in a beat distance error of up to 0.5 audio samples.
745   frames_between_quarter_notes () eliminats this effect when determining time duration
746   from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
747
748   The above pointless example could instead do:
749   beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
750
751   The Shaggs - Things I Wonder
752   https://www.youtube.com/watch?v=9wQK6zMJOoQ
753
754 */
755 struct MetricSectionSorter {
756     bool operator() (const MetricSection* a, const MetricSection* b) {
757             return a->pulse() < b->pulse();
758     }
759 };
760
761 struct MetricSectionFrameSorter {
762     bool operator() (const MetricSection* a, const MetricSection* b) {
763             return a->frame() < b->frame();
764     }
765 };
766
767 TempoMap::TempoMap (framecnt_t fr)
768 {
769         _frame_rate = fr;
770         BBT_Time start (1, 1, 0);
771
772         TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo, AudioTime, fr);
773         MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
774
775         t->set_initial (true);
776         t->set_locked_to_meter (true);
777
778         m->set_initial (true);
779
780         /* note: frame time is correct (zero) for both of these */
781
782         _metrics.push_back (t);
783         _metrics.push_back (m);
784
785 }
786
787 TempoMap&
788 TempoMap::operator= (TempoMap const & other)
789 {
790         if (&other != this) {
791                 Glib::Threads::RWLock::ReaderLock lr (other.lock);
792                 Glib::Threads::RWLock::WriterLock lm (lock);
793                 _frame_rate = other._frame_rate;
794
795                 Metrics::const_iterator d = _metrics.begin();
796                 while (d != _metrics.end()) {
797                         delete (*d);
798                         ++d;
799                 }
800                 _metrics.clear();
801
802                 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
803                         TempoSection const * const ts = dynamic_cast<TempoSection const * const> (*m);
804                         MeterSection const * const ms = dynamic_cast<MeterSection const * const> (*m);
805
806                         if (ts) {
807                                 TempoSection* new_section = new TempoSection (*ts);
808                                 _metrics.push_back (new_section);
809                         } else {
810                                 MeterSection* new_section = new MeterSection (*ms);
811                                 _metrics.push_back (new_section);
812                         }
813                 }
814         }
815
816         PropertyChanged (PropertyChange());
817
818         return *this;
819 }
820
821 TempoMap::~TempoMap ()
822 {
823         Metrics::const_iterator d = _metrics.begin();
824         while (d != _metrics.end()) {
825                 delete (*d);
826                 ++d;
827         }
828         _metrics.clear();
829 }
830
831 framepos_t
832 TempoMap::frame_at_minute (const double time) const
833 {
834         return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
835 }
836
837 double
838 TempoMap::minute_at_frame (const framepos_t frame) const
839 {
840         return (frame / (double) _frame_rate) / 60.0;
841 }
842
843 void
844 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
845 {
846         bool removed = false;
847
848         {
849                 Glib::Threads::RWLock::WriterLock lm (lock);
850                 if ((removed = remove_tempo_locked (tempo))) {
851                         if (complete_operation) {
852                                 recompute_map (_metrics);
853                         }
854                 }
855         }
856
857         if (removed && complete_operation) {
858                 PropertyChanged (PropertyChange ());
859         }
860 }
861
862 bool
863 TempoMap::remove_tempo_locked (const TempoSection& tempo)
864 {
865         Metrics::iterator i;
866
867         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
868                 if (dynamic_cast<TempoSection*> (*i) != 0) {
869                         if (tempo.frame() == (*i)->frame()) {
870                                 if (!(*i)->initial()) {
871                                         delete (*i);
872                                         _metrics.erase (i);
873                                         return true;
874                                 }
875                         }
876                 }
877         }
878
879         return false;
880 }
881
882 void
883 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
884 {
885         bool removed = false;
886
887         {
888                 Glib::Threads::RWLock::WriterLock lm (lock);
889                 if ((removed = remove_meter_locked (tempo))) {
890                         if (complete_operation) {
891                                 recompute_map (_metrics);
892                         }
893                 }
894         }
895
896         if (removed && complete_operation) {
897                 PropertyChanged (PropertyChange ());
898         }
899 }
900
901 bool
902 TempoMap::remove_meter_locked (const MeterSection& meter)
903 {
904
905         if (meter.position_lock_style() == AudioTime) {
906                 /* remove meter-locked tempo */
907                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
908                         TempoSection* t = 0;
909                         if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
910                                 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
911                                         delete (*i);
912                                         _metrics.erase (i);
913                                         break;
914                                 }
915                         }
916                 }
917         }
918
919         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
920                 if (dynamic_cast<MeterSection*> (*i) != 0) {
921                         if (meter.frame() == (*i)->frame()) {
922                                 if (!(*i)->initial()) {
923                                         delete (*i);
924                                         _metrics.erase (i);
925                                         return true;
926                                 }
927                         }
928                 }
929         }
930
931         return false;
932 }
933
934 void
935 TempoMap::do_insert (MetricSection* section)
936 {
937         bool need_add = true;
938         /* we only allow new meters to be inserted on beat 1 of an existing
939          * measure.
940          */
941         MeterSection* m = 0;
942         if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
943
944                 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
945
946                         pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
947                         corrected.second.beats = 1;
948                         corrected.second.ticks = 0;
949                         corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
950                         warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
951                                                    m->bbt(), corrected.second) << endmsg;
952                         //m->set_pulse (corrected);
953                 }
954         }
955
956         /* Look for any existing MetricSection that is of the same type and
957            in the same bar as the new one, and remove it before adding
958            the new one. Note that this means that if we find a matching,
959            existing section, we can break out of the loop since we're
960            guaranteed that there is only one such match.
961         */
962
963         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
964
965                 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
966                 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
967                 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
968                 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
969
970                 if (tempo && insert_tempo) {
971
972                         /* Tempo sections */
973                         bool const ipm = insert_tempo->position_lock_style() == MusicTime;
974                         if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
975
976                                 if (tempo->initial()) {
977
978                                         /* can't (re)move this section, so overwrite
979                                          * its data content (but not its properties as
980                                          * a section).
981                                          */
982
983                                         *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
984                                         (*i)->set_position_lock_style (AudioTime);
985                                         need_add = false;
986                                 } else {
987                                         delete (*i);
988                                         _metrics.erase (i);
989                                 }
990                                 break;
991                         }
992
993                 } else if (meter && insert_meter) {
994
995                         /* Meter Sections */
996
997                         bool const ipm = insert_meter->position_lock_style() == MusicTime;
998
999                         if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
1000
1001                                 if (meter->initial()) {
1002
1003                                         /* can't (re)move this section, so overwrite
1004                                          * its data content (but not its properties as
1005                                          * a section
1006                                          */
1007
1008                                         *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
1009                                         (*i)->set_position_lock_style (AudioTime);
1010                                         need_add = false;
1011                                 } else {
1012                                         delete (*i);
1013                                         _metrics.erase (i);
1014                                 }
1015
1016                                 break;
1017                         }
1018                 } else {
1019                         /* non-matching types, so we don't care */
1020                 }
1021         }
1022
1023         /* Add the given MetricSection, if we didn't just reset an existing
1024          * one above
1025          */
1026
1027         if (need_add) {
1028                 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
1029                 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
1030                 Metrics::iterator i;
1031
1032                 if (insert_meter) {
1033                         TempoSection* prev_t = 0;
1034
1035                         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1036                                 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
1037                                 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1038
1039                                 if (meter) {
1040                                         if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
1041                                                 break;
1042                                         }
1043                                 } else {
1044                                         if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
1045                                                 break;
1046                                         }
1047
1048                                         prev_t = dynamic_cast<TempoSection*> (*i);
1049                                 }
1050                         }
1051                 } else if (insert_tempo) {
1052                         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1053                                 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1054
1055                                 if (tempo) {
1056                                         bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1057                                         const bool lm = insert_tempo->locked_to_meter();
1058                                         if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1059                                             || (lm && tempo->pulse() > insert_tempo->pulse())) {
1060                                                 break;
1061                                         }
1062                                 }
1063                         }
1064                 }
1065
1066                 _metrics.insert (i, section);
1067                 //dump (std::cout);
1068         }
1069 }
1070 /* user supplies the exact pulse if pls == MusicTime */
1071 TempoSection*
1072 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1073 {
1074         if (tempo.note_types_per_minute() <= 0.0) {
1075                 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1076                 return 0;
1077         }
1078
1079         TempoSection* ts = 0;
1080         TempoSection* prev_tempo = 0;
1081         {
1082                 Glib::Threads::RWLock::WriterLock lm (lock);
1083                 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true);
1084                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1085
1086                         if ((*i)->is_tempo()) {
1087                                 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1088
1089                                 bool const ipm = ts->position_lock_style() == MusicTime;
1090                                 bool const lm = ts->locked_to_meter();
1091                                 if ((ipm && this_t->pulse() == ts->pulse()) || (!ipm && this_t->frame() == ts->frame())
1092                                     || (lm && this_t->pulse() == ts->pulse())) {
1093                                         if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1094                                                 prev_tempo->set_end_note_types_per_minute (ts->note_types_per_minute());
1095                                         }
1096                                         break;
1097                                 }
1098                                 prev_tempo = this_t;
1099                         }
1100                 }
1101                 recompute_map (_metrics);
1102         }
1103
1104         PropertyChanged (PropertyChange ());
1105
1106         return ts;
1107 }
1108
1109 void
1110 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1111 {
1112         if (tempo.note_types_per_minute() <= 0.0) {
1113                 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1114                 return;
1115         }
1116
1117         bool const locked_to_meter = ts.locked_to_meter();
1118         bool const ts_clamped = ts.clamped();
1119         TempoSection* new_ts = 0;
1120
1121         {
1122                 Glib::Threads::RWLock::WriterLock lm (lock);
1123                 TempoSection& first (first_tempo());
1124                 if (!ts.initial()) {
1125                         if (locked_to_meter) {
1126                                 {
1127                                         /* cannot move a meter-locked tempo section */
1128                                         *static_cast<Tempo*>(&ts) = tempo;
1129                                         recompute_map (_metrics);
1130                                 }
1131                         } else {
1132                                 remove_tempo_locked (ts);
1133                                 new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter);
1134                                 new_ts->set_clamped (ts_clamped);
1135
1136                                 if (new_ts && new_ts->type() == TempoSection::Constant) {
1137                                         new_ts->set_end_note_types_per_minute (new_ts->note_types_per_minute());
1138                                 } else {
1139                                         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1140
1141                                                 if ((*i)->is_tempo()) {
1142                                                         TempoSection* const this_t = static_cast<TempoSection*> (*i);
1143
1144                                                         bool const ipm = new_ts->position_lock_style() == MusicTime;
1145                                                         bool const lm = new_ts->locked_to_meter();
1146                                                         if ((ipm && this_t->pulse() > new_ts->pulse()) || (!ipm && this_t->frame() > new_ts->frame())
1147                                                             || (lm && this_t->pulse() > new_ts->pulse())) {
1148                                                                 new_ts->set_end_note_types_per_minute (tempo.end_note_types_per_minute());
1149
1150                                                                 break;
1151                                                         }
1152                                                 }
1153                                         }
1154                                 }
1155                         }
1156
1157                 } else {
1158                         first.set_pulse (0.0);
1159                         first.set_minute (minute_at_frame (frame));
1160                         first.set_position_lock_style (AudioTime);
1161                         first.set_locked_to_meter (true);
1162                         first.set_clamped (ts_clamped);
1163                         {
1164                                 /* cannot move the first tempo section */
1165                                 *static_cast<Tempo*>(&first) = tempo;
1166                         }
1167                 }
1168                 recompute_map (_metrics);
1169         }
1170
1171         PropertyChanged (PropertyChange ());
1172 }
1173
1174 TempoSection*
1175 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1176                             , PositionLockStyle pls, bool recompute, bool locked_to_meter)
1177 {
1178         TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _frame_rate);
1179         t->set_locked_to_meter (locked_to_meter);
1180
1181         do_insert (t);
1182
1183         if (recompute) {
1184                 if (pls == AudioTime) {
1185                         solve_map_minute (_metrics, t, t->minute());
1186                 } else {
1187                         solve_map_pulse (_metrics, t, t->pulse());
1188                 }
1189                 recompute_meters (_metrics);
1190         }
1191
1192         return t;
1193 }
1194
1195 MeterSection*
1196 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1197 {
1198         MeterSection* m = 0;
1199         {
1200                 Glib::Threads::RWLock::WriterLock lm (lock);
1201                 m = add_meter_locked (meter, beat, where, frame, pls, true);
1202         }
1203
1204
1205 #ifndef NDEBUG
1206         if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1207                 dump (std::cerr);
1208         }
1209 #endif
1210
1211         PropertyChanged (PropertyChange ());
1212         return m;
1213 }
1214
1215 void
1216 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1217 {
1218         {
1219                 Glib::Threads::RWLock::WriterLock lm (lock);
1220                 const double beat = beat_at_bbt_locked (_metrics, where);
1221
1222                 if (!ms.initial()) {
1223                         remove_meter_locked (ms);
1224                         add_meter_locked (meter, beat, where, frame, pls, true);
1225                 } else {
1226                         MeterSection& first (first_meter());
1227                         TempoSection& first_t (first_tempo());
1228                         /* cannot move the first meter section */
1229                         *static_cast<Meter*>(&first) = meter;
1230                         first.set_position_lock_style (AudioTime);
1231                         first.set_pulse (0.0);
1232                         first.set_minute (minute_at_frame (frame));
1233                         pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1234                         first.set_beat (beat);
1235                         first_t.set_minute (first.minute());
1236                         first_t.set_locked_to_meter (true);
1237                         first_t.set_pulse (0.0);
1238                         first_t.set_position_lock_style (AudioTime);
1239                         recompute_map (_metrics);
1240                 }
1241         }
1242
1243         PropertyChanged (PropertyChange ());
1244 }
1245
1246 MeterSection*
1247 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1248 {
1249         const MeterSection& prev_m = meter_section_at_minute_locked  (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1250         const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1251         const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1252         TempoSection* mlt = 0;
1253
1254         if (pls == AudioTime) {
1255                 /* add meter-locked tempo */
1256                 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), AudioTime, true, true);
1257
1258                 if (!mlt) {
1259                         return 0;
1260                 }
1261
1262         }
1263
1264         MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1265
1266         bool solved = false;
1267
1268         do_insert (new_meter);
1269
1270         if (recompute) {
1271
1272                 if (pls == AudioTime) {
1273                         solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1274                         /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1275                            fudge frame so that the meter ends up at its BBT position instead.
1276                         */
1277                         if (!solved) {
1278                                 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1279                         }
1280                 } else {
1281                         solved = solve_map_bbt (_metrics, new_meter, where);
1282                         /* required due to resetting the pulse of meter-locked tempi above.
1283                            Arguably  solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1284                            but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1285                         */
1286                         recompute_map (_metrics);
1287                 }
1288         }
1289
1290         if (!solved && recompute) {
1291                 /* if this has failed to solve, there is little we can do other than to ensure that
1292                    the new map is recalculated.
1293                 */
1294                 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1295                 recompute_map (_metrics);
1296         }
1297
1298         return new_meter;
1299 }
1300
1301 void
1302 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1303 {
1304         Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1305         TempoSection* t;
1306
1307         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1308                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1309                         if (!t->active()) {
1310                                 continue;
1311                         }
1312                         {
1313                                 Glib::Threads::RWLock::WriterLock lm (lock);
1314                                 *((Tempo*) t) = newtempo;
1315                                 recompute_map (_metrics);
1316                         }
1317                         PropertyChanged (PropertyChange ());
1318                         break;
1319                 }
1320         }
1321 }
1322
1323 void
1324 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1325 {
1326         Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1327
1328         TempoSection* prev;
1329         TempoSection* first;
1330         Metrics::iterator i;
1331
1332         /* find the TempoSection immediately preceding "where"
1333          */
1334
1335         for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1336
1337                 if ((*i)->frame() > where) {
1338                         break;
1339                 }
1340
1341                 TempoSection* t;
1342
1343                 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1344                         if (!t->active()) {
1345                                 continue;
1346                         }
1347                         if (!first) {
1348                                 first = t;
1349                         }
1350                         prev = t;
1351                 }
1352         }
1353
1354         if (!prev) {
1355                 if (!first) {
1356                         error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1357                         return;
1358                 }
1359
1360                 prev = first;
1361         }
1362
1363         /* reset */
1364
1365         {
1366                 Glib::Threads::RWLock::WriterLock lm (lock);
1367                 /* cannot move the first tempo section */
1368                 *((Tempo*)prev) = newtempo;
1369                 recompute_map (_metrics);
1370         }
1371
1372         PropertyChanged (PropertyChange ());
1373 }
1374
1375 const MeterSection&
1376 TempoMap::first_meter () const
1377 {
1378         const MeterSection *m = 0;
1379
1380         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1381                 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1382                         return *m;
1383                 }
1384         }
1385
1386         fatal << _("programming error: no meter section in tempo map!") << endmsg;
1387         abort(); /*NOTREACHED*/
1388         return *m;
1389 }
1390
1391 MeterSection&
1392 TempoMap::first_meter ()
1393 {
1394         MeterSection *m = 0;
1395
1396         /* CALLER MUST HOLD LOCK */
1397
1398         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1399                 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1400                         return *m;
1401                 }
1402         }
1403
1404         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1405         abort(); /*NOTREACHED*/
1406         return *m;
1407 }
1408
1409 const TempoSection&
1410 TempoMap::first_tempo () const
1411 {
1412         const TempoSection *t = 0;
1413
1414         /* CALLER MUST HOLD LOCK */
1415
1416         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1417                 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1418                         if (!t->active()) {
1419                                 continue;
1420                         }
1421                         if (t->initial()) {
1422                                 return *t;
1423                         }
1424                 }
1425         }
1426
1427         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1428         abort(); /*NOTREACHED*/
1429         return *t;
1430 }
1431
1432 TempoSection&
1433 TempoMap::first_tempo ()
1434 {
1435         TempoSection *t = 0;
1436
1437         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1438                 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1439                         if (!t->active()) {
1440                                 continue;
1441                         }
1442                         if (t->initial()) {
1443                                 return *t;
1444                         }
1445                 }
1446         }
1447
1448         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1449         abort(); /*NOTREACHED*/
1450         return *t;
1451 }
1452 void
1453 TempoMap::recompute_tempi (Metrics& metrics)
1454 {
1455         TempoSection* prev_t = 0;
1456
1457         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1458                 TempoSection* t;
1459
1460                 if ((*i)->is_tempo()) {
1461                         t = static_cast<TempoSection*> (*i);
1462                         if (!t->active()) {
1463                                 continue;
1464                         }
1465                         if (t->initial()) {
1466                                 if (!prev_t) {
1467                                         t->set_pulse (0.0);
1468                                         prev_t = t;
1469                                         continue;
1470                                 }
1471                         }
1472                         if (prev_t) {
1473                                 if (t->position_lock_style() == AudioTime) {
1474                                         prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1475                                         if (!t->locked_to_meter()) {
1476                                                 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1477                                         }
1478
1479                                 } else {
1480                                         prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1481                                         t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1482
1483                                 }
1484                         }
1485                         prev_t = t;
1486                 }
1487         }
1488         assert (prev_t);
1489         prev_t->set_c (0.0);
1490 }
1491
1492 /* tempos must be positioned correctly.
1493    the current approach is to use a meter's bbt time as its base position unit.
1494    an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1495    while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1496 */
1497 void
1498 TempoMap::recompute_meters (Metrics& metrics)
1499 {
1500         MeterSection* meter = 0;
1501         MeterSection* prev_m = 0;
1502
1503         for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1504                 if (!(*mi)->is_tempo()) {
1505                         meter = static_cast<MeterSection*> (*mi);
1506                         if (meter->position_lock_style() == AudioTime) {
1507                                 double pulse = 0.0;
1508                                 pair<double, BBT_Time> b_bbt;
1509                                 TempoSection* meter_locked_tempo = 0;
1510                                 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1511                                         TempoSection* t;
1512                                         if ((*ii)->is_tempo()) {
1513                                                 t = static_cast<TempoSection*> (*ii);
1514                                                 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1515                                                         meter_locked_tempo = t;
1516                                                         break;
1517                                                 }
1518                                         }
1519                                 }
1520
1521                                 if (prev_m) {
1522                                         double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1523                                         if (beats + prev_m->beat() != meter->beat()) {
1524                                                 /* reordering caused a bbt change */
1525
1526                                                 beats = meter->beat() - prev_m->beat();
1527                                                 b_bbt = make_pair (beats + prev_m->beat()
1528                                                                    , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1529                                                 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1530
1531                                         } else if (!meter->initial()) {
1532                                                 b_bbt = make_pair (meter->beat(), meter->bbt());
1533                                                 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1534                                         }
1535                                 } else {
1536                                         b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1537                                 }
1538                                 if (meter_locked_tempo) {
1539                                         meter_locked_tempo->set_pulse (pulse);
1540                                 }
1541                                 meter->set_beat (b_bbt);
1542                                 meter->set_pulse (pulse);
1543
1544                         } else {
1545                                 /* MusicTime */
1546                                 double pulse = 0.0;
1547                                 pair<double, BBT_Time> b_bbt;
1548                                 if (prev_m) {
1549                                         const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1550                                         if (beats + prev_m->beat() != meter->beat()) {
1551                                                 /* reordering caused a bbt change */
1552                                                 b_bbt = make_pair (beats + prev_m->beat()
1553                                                                    , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1554                                         } else {
1555                                                 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1556                                         }
1557                                         pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1558                                 } else {
1559                                         /* shouldn't happen - the first is audio-locked */
1560                                         pulse = pulse_at_beat_locked (metrics, meter->beat());
1561                                         b_bbt = make_pair (meter->beat(), meter->bbt());
1562                                 }
1563
1564                                 meter->set_beat (b_bbt);
1565                                 meter->set_pulse (pulse);
1566                                 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1567                         }
1568
1569                         prev_m = meter;
1570                 }
1571         }
1572 }
1573
1574 void
1575 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1576 {
1577         /* CALLER MUST HOLD WRITE LOCK */
1578
1579         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1580
1581         if (end == 0) {
1582                 /* silly call from Session::process() during startup
1583                  */
1584                 return;
1585         }
1586
1587         recompute_tempi (metrics);
1588         recompute_meters (metrics);
1589 }
1590
1591 TempoMetric
1592 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1593 {
1594         Glib::Threads::RWLock::ReaderLock lm (lock);
1595         TempoMetric m (first_meter(), first_tempo());
1596
1597         if (last) {
1598                 *last = ++_metrics.begin();
1599         }
1600
1601         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1602            at something, because we insert the default tempo and meter during
1603            TempoMap construction.
1604
1605            now see if we can find better candidates.
1606         */
1607
1608         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1609
1610                 if ((*i)->frame() > frame) {
1611                         break;
1612                 }
1613
1614                 m.set_metric(*i);
1615
1616                 if (last) {
1617                         *last = i;
1618                 }
1619         }
1620
1621         return m;
1622 }
1623
1624 /* XX meters only */
1625 TempoMetric
1626 TempoMap::metric_at (BBT_Time bbt) const
1627 {
1628         Glib::Threads::RWLock::ReaderLock lm (lock);
1629         TempoMetric m (first_meter(), first_tempo());
1630
1631         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1632            at something, because we insert the default tempo and meter during
1633            TempoMap construction.
1634
1635            now see if we can find better candidates.
1636         */
1637
1638         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1639                 MeterSection* mw;
1640                 if (!(*i)->is_tempo()) {
1641                         mw = static_cast<MeterSection*> (*i);
1642                         BBT_Time section_start (mw->bbt());
1643
1644                         if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1645                                 break;
1646                         }
1647
1648                         m.set_metric (*i);
1649                 }
1650         }
1651
1652         return m;
1653 }
1654
1655 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1656  * @param frame The session frame position.
1657  * @return The beat duration according to the tempo map at the supplied frame.
1658  *
1659  * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1660  * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1661  *
1662  * This function uses both tempo and meter.
1663  */
1664 double
1665 TempoMap::beat_at_frame (const framecnt_t& frame) const
1666 {
1667         Glib::Threads::RWLock::ReaderLock lm (lock);
1668
1669         return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1670 }
1671
1672 /* This function uses both tempo and meter.*/
1673 double
1674 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1675 {
1676         const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1677         MeterSection* prev_m = 0;
1678         MeterSection* next_m = 0;
1679
1680         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1681                 if (!(*i)->is_tempo()) {
1682                         if (prev_m && (*i)->minute() > minute) {
1683                                 next_m = static_cast<MeterSection*> (*i);
1684                                 break;
1685                         }
1686                         prev_m = static_cast<MeterSection*> (*i);
1687                 }
1688         }
1689
1690         const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1691
1692         /* audio locked meters fake their beat */
1693         if (next_m && next_m->beat() < beat) {
1694                 return next_m->beat();
1695         }
1696
1697         return beat;
1698 }
1699
1700 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1701  * @param beat The BBT (meter-based) beat.
1702  * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1703  *
1704  * This function uses both tempo and meter.
1705  */
1706 framepos_t
1707 TempoMap::frame_at_beat (const double& beat) const
1708 {
1709         Glib::Threads::RWLock::ReaderLock lm (lock);
1710
1711         return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1712 }
1713
1714 /* meter & tempo section based */
1715 double
1716 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1717 {
1718         MeterSection* prev_m = 0;
1719         TempoSection* prev_t = 0;
1720
1721         MeterSection* m;
1722
1723         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1724                 if (!(*i)->is_tempo()) {
1725                         m = static_cast<MeterSection*> (*i);
1726                         if (prev_m && m->beat() > beat) {
1727                                 break;
1728                         }
1729                         prev_m = m;
1730                 }
1731         }
1732         assert (prev_m);
1733
1734         TempoSection* t;
1735
1736         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1737                 if ((*i)->is_tempo()) {
1738                         t = static_cast<TempoSection*> (*i);
1739
1740                         if (!t->active()) {
1741                                 continue;
1742                         }
1743
1744                         if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1745                                 break;
1746                         }
1747                         prev_t = t;
1748                 }
1749
1750         }
1751         assert (prev_t);
1752
1753         return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1754 }
1755
1756 /** Returns a Tempo corresponding to the supplied frame position.
1757  * @param frame The audio frame.
1758  * @return a Tempo according to the tempo map at the supplied frame.
1759  *
1760  */
1761 Tempo
1762 TempoMap::tempo_at_frame (const framepos_t& frame) const
1763 {
1764         Glib::Threads::RWLock::ReaderLock lm (lock);
1765
1766         return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1767 }
1768
1769 Tempo
1770 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1771 {
1772         TempoSection* prev_t = 0;
1773
1774         TempoSection* t;
1775
1776         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1777                 if ((*i)->is_tempo()) {
1778                         t = static_cast<TempoSection*> (*i);
1779                         if (!t->active()) {
1780                                 continue;
1781                         }
1782                         if ((prev_t) && t->minute() > minute) {
1783                                 /* t is the section past frame */
1784                                 return prev_t->tempo_at_minute (minute);
1785                         }
1786                         prev_t = t;
1787                 }
1788         }
1789
1790         return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1791 }
1792
1793 /** returns the frame at which the supplied tempo occurs, or
1794  *  the frame of the last tempo section (search exhausted)
1795  *  only the position of the first occurence will be returned
1796  *  (extend me)
1797 */
1798 framepos_t
1799 TempoMap::frame_at_tempo (const Tempo& tempo) const
1800 {
1801         Glib::Threads::RWLock::ReaderLock lm (lock);
1802
1803         return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1804 }
1805
1806 double
1807 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1808 {
1809         TempoSection* prev_t = 0;
1810         const double tempo_bpm = tempo.note_types_per_minute();
1811
1812         Metrics::const_iterator i;
1813
1814         for (i = metrics.begin(); i != metrics.end(); ++i) {
1815                 TempoSection* t;
1816                 if ((*i)->is_tempo()) {
1817                         t = static_cast<TempoSection*> (*i);
1818
1819                         if (!t->active()) {
1820                                 continue;
1821                         }
1822
1823
1824
1825                         if (t->note_types_per_minute() == tempo_bpm) {
1826                                 return t->minute();
1827                         }
1828
1829                         if (prev_t) {
1830                                 const double prev_t_bpm = prev_t->note_types_per_minute();
1831                                 const double prev_t_end_bpm = prev_t->end_note_types_per_minute();
1832                                 if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm)
1833                                     || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm)
1834                                     || (prev_t_end_bpm == tempo_bpm)) {
1835
1836                                         return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
1837                                 }
1838                         }
1839                         prev_t = t;
1840                 }
1841         }
1842
1843         return prev_t->minute();
1844 }
1845
1846 Tempo
1847 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1848 {
1849         TempoSection* prev_t = 0;
1850
1851         TempoSection* t;
1852
1853         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1854                 if ((*i)->is_tempo()) {
1855                         t = static_cast<TempoSection*> (*i);
1856                         if (!t->active()) {
1857                                 continue;
1858                         }
1859                         if ((prev_t) && t->pulse() > pulse) {
1860                                 /* t is the section past frame */
1861                                 return prev_t->tempo_at_pulse (pulse);
1862                         }
1863                         prev_t = t;
1864                 }
1865         }
1866
1867         return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1868 }
1869
1870 double
1871 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1872 {
1873         TempoSection* prev_t = 0;
1874         const double tempo_bpm = tempo.note_types_per_minute();
1875
1876         Metrics::const_iterator i;
1877
1878         for (i = metrics.begin(); i != metrics.end(); ++i) {
1879                 TempoSection* t;
1880                 if ((*i)->is_tempo()) {
1881                         t = static_cast<TempoSection*> (*i);
1882
1883                         if (!t->active()) {
1884                                 continue;
1885                         }
1886
1887                         const double t_bpm = t->note_types_per_minute();
1888
1889                         if (t_bpm == tempo_bpm) {
1890                                 return t->pulse();
1891                         }
1892
1893                         if (prev_t) {
1894                                 const double prev_t_bpm = prev_t->note_types_per_minute();
1895
1896                                 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1897                                         return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1898                                 }
1899                         }
1900                         prev_t = t;
1901                 }
1902         }
1903
1904         return prev_t->pulse();
1905 }
1906
1907 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1908  * @param qn the position in quarter note beats.
1909  * @return the Tempo at the supplied quarter-note.
1910  */
1911 Tempo
1912 TempoMap::tempo_at_quarter_note (const double& qn) const
1913 {
1914         Glib::Threads::RWLock::ReaderLock lm (lock);
1915
1916         return tempo_at_pulse_locked (_metrics, qn / 4.0);
1917 }
1918
1919 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1920  * @param tempo the tempo.
1921  * @return the position in quarter-note beats where the map bpm
1922  * is equal to that of the Tempo. currently ignores note_type.
1923  */
1924 double
1925 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1926 {
1927         Glib::Threads::RWLock::ReaderLock lm (lock);
1928
1929         return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1930 }
1931
1932 /** Returns the whole-note pulse corresponding to the supplied  BBT (meter-based) beat.
1933  * @param metrics the list of metric sections used to calculate the pulse.
1934  * @param beat The BBT (meter-based) beat.
1935  * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1936  *
1937  * a pulse or whole note is the base musical position of a MetricSection.
1938  * it is equivalent to four quarter notes.
1939  *
1940  */
1941 double
1942 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1943 {
1944         const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1945
1946         return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1947 }
1948
1949 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1950  * @param metrics the list of metric sections used to calculate the beat.
1951  * @param pulse the whole-note pulse.
1952  * @return the meter-based beat at the supplied whole-note pulse.
1953  *
1954  * a pulse or whole note is the base musical position of a MetricSection.
1955  * it is equivalent to four quarter notes.
1956  */
1957 double
1958 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1959 {
1960         MeterSection* prev_m = 0;
1961
1962         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1963                 MeterSection* m;
1964                 if (!(*i)->is_tempo()) {
1965                         m = static_cast<MeterSection*> (*i);
1966                         if (prev_m && m->pulse() > pulse) {
1967                                 break;
1968                         }
1969                         prev_m = m;
1970                 }
1971         }
1972         assert (prev_m);
1973
1974         double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1975         return ret;
1976 }
1977
1978 /* tempo section based */
1979 double
1980 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1981 {
1982         /* HOLD (at least) THE READER LOCK */
1983         TempoSection* prev_t = 0;
1984
1985         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1986                 TempoSection* t;
1987                 if ((*i)->is_tempo()) {
1988                         t = static_cast<TempoSection*> (*i);
1989                         if (!t->active()) {
1990                                 continue;
1991                         }
1992                         if (prev_t && t->minute() > minute) {
1993                                 /*the previous ts is the one containing the frame */
1994                                 const double ret = prev_t->pulse_at_minute (minute);
1995                                 /* audio locked section in new meter*/
1996                                 if (t->pulse() < ret) {
1997                                         return t->pulse();
1998                                 }
1999                                 return ret;
2000                         }
2001                         prev_t = t;
2002                 }
2003         }
2004
2005         /* treated as constant for this ts */
2006         const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
2007
2008         return pulses_in_section + prev_t->pulse();
2009 }
2010
2011 /* tempo section based */
2012 double
2013 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2014 {
2015         /* HOLD THE READER LOCK */
2016
2017         const TempoSection* prev_t = 0;
2018
2019         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2020                 TempoSection* t;
2021
2022                 if ((*i)->is_tempo()) {
2023                         t = static_cast<TempoSection*> (*i);
2024                         if (!t->active()) {
2025                                 continue;
2026                         }
2027                         if (prev_t && t->pulse() > pulse) {
2028                                 return prev_t->minute_at_pulse (pulse);
2029                         }
2030
2031                         prev_t = t;
2032                 }
2033         }
2034         /* must be treated as constant, irrespective of _type */
2035         double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
2036
2037         return dtime + prev_t->minute();
2038 }
2039
2040 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
2041  * @param bbt The BBT time (meter-based).
2042  * @return bbt The BBT beat (meter-based) at the supplied BBT time.
2043  *
2044  */
2045 double
2046 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
2047 {
2048         Glib::Threads::RWLock::ReaderLock lm (lock);
2049         return beat_at_bbt_locked (_metrics, bbt);
2050 }
2051
2052
2053 double
2054 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2055 {
2056         /* CALLER HOLDS READ LOCK */
2057
2058         MeterSection* prev_m = 0;
2059
2060         /* because audio-locked meters have 'fake' integral beats,
2061            there is no pulse offset here.
2062         */
2063         MeterSection* m;
2064
2065         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2066                 if (!(*i)->is_tempo()) {
2067                         m = static_cast<MeterSection*> (*i);
2068                         if (prev_m) {
2069                                 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2070                                 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2071                                         break;
2072                                 }
2073                         }
2074                         prev_m = m;
2075                 }
2076         }
2077
2078         const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2079         const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2080         const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2081
2082         return ret;
2083 }
2084
2085 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2086  * @param beat The BBT (meter-based) beat.
2087  * @return The BBT time (meter-based) at the supplied meter-based beat.
2088  *
2089  */
2090 Timecode::BBT_Time
2091 TempoMap::bbt_at_beat (const double& beat)
2092 {
2093         Glib::Threads::RWLock::ReaderLock lm (lock);
2094         return bbt_at_beat_locked (_metrics, beat);
2095 }
2096
2097 Timecode::BBT_Time
2098 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2099 {
2100         /* CALLER HOLDS READ LOCK */
2101         MeterSection* prev_m = 0;
2102         const double beats = max (0.0, b);
2103
2104         MeterSection* m = 0;
2105
2106         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2107                 if (!(*i)->is_tempo()) {
2108                         m = static_cast<MeterSection*> (*i);
2109                         if (prev_m) {
2110                                 if (m->beat() > beats) {
2111                                         /* this is the meter after the one our beat is on*/
2112                                         break;
2113                                 }
2114                         }
2115
2116                         prev_m = m;
2117                 }
2118         }
2119         assert (prev_m);
2120
2121         const double beats_in_ms = beats - prev_m->beat();
2122         const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2123         const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2124         const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2125         const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2126
2127         BBT_Time ret;
2128
2129         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2130         ret.beats = (uint32_t) floor (remaining_beats);
2131         ret.bars = total_bars;
2132
2133         /* 0 0 0 to 1 1 0 - based mapping*/
2134         ++ret.bars;
2135         ++ret.beats;
2136
2137         if (ret.ticks >= BBT_Time::ticks_per_beat) {
2138                 ++ret.beats;
2139                 ret.ticks -= BBT_Time::ticks_per_beat;
2140         }
2141
2142         if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2143                 ++ret.bars;
2144                 ret.beats = 1;
2145         }
2146
2147         return ret;
2148 }
2149
2150 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2151  * @param bbt The BBT time (meter-based).
2152  * @return the quarter note beat at the supplied BBT time
2153  *
2154  * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2155  *
2156  * while the input uses meter, the output does not.
2157  */
2158 double
2159 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2160 {
2161         Glib::Threads::RWLock::ReaderLock lm (lock);
2162
2163         return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2164 }
2165
2166 double
2167 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2168 {
2169         Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2170
2171         if (!lm.locked()) {
2172                 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2173         }
2174
2175         return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2176 }
2177
2178 double
2179 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2180 {
2181         /* CALLER HOLDS READ LOCK */
2182
2183         MeterSection* prev_m = 0;
2184
2185         /* because audio-locked meters have 'fake' integral beats,
2186            there is no pulse offset here.
2187         */
2188         MeterSection* m;
2189
2190         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2191                 if (!(*i)->is_tempo()) {
2192                         m = static_cast<MeterSection*> (*i);
2193                         if (prev_m) {
2194                                 if (m->bbt().bars > bbt.bars) {
2195                                         break;
2196                                 }
2197                         }
2198                         prev_m = m;
2199                 }
2200         }
2201
2202         const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2203         const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2204         const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2205
2206         return ret;
2207 }
2208
2209 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2210  * @param qn the quarter-note beat.
2211  * @return The BBT time (meter-based) at the supplied meter-based beat.
2212  *
2213  * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2214  *
2215  */
2216 Timecode::BBT_Time
2217 TempoMap::bbt_at_quarter_note (const double& qn)
2218 {
2219         Glib::Threads::RWLock::ReaderLock lm (lock);
2220
2221         return bbt_at_pulse_locked (_metrics, qn / 4.0);
2222 }
2223
2224 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2225  * @param metrics The list of metric sections used to determine the result.
2226  * @param pulse The whole-note pulse.
2227  * @return The BBT time at the supplied whole-note pulse.
2228  *
2229  * a pulse or whole note is the basic musical position of a MetricSection.
2230  * it is equivalent to four quarter notes.
2231  * while the output uses meter, the input does not.
2232  */
2233 Timecode::BBT_Time
2234 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2235 {
2236         MeterSection* prev_m = 0;
2237
2238         MeterSection* m = 0;
2239
2240         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2241
2242                 if (!(*i)->is_tempo()) {
2243                         m = static_cast<MeterSection*> (*i);
2244
2245                         if (prev_m) {
2246                                 double const pulses_to_m = m->pulse() - prev_m->pulse();
2247                                 if (prev_m->pulse() + pulses_to_m > pulse) {
2248                                         /* this is the meter after the one our beat is on*/
2249                                         break;
2250                                 }
2251                         }
2252
2253                         prev_m = m;
2254                 }
2255         }
2256
2257         assert (prev_m);
2258
2259         const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2260         const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2261         const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2262         const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2263         const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2264
2265         BBT_Time ret;
2266
2267         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2268         ret.beats = (uint32_t) floor (remaining_beats);
2269         ret.bars = total_bars;
2270
2271         /* 0 0 0 to 1 1 0 mapping*/
2272         ++ret.bars;
2273         ++ret.beats;
2274
2275         if (ret.ticks >= BBT_Time::ticks_per_beat) {
2276                 ++ret.beats;
2277                 ret.ticks -= BBT_Time::ticks_per_beat;
2278         }
2279
2280         if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2281                 ++ret.bars;
2282                 ret.beats = 1;
2283         }
2284
2285         return ret;
2286 }
2287
2288 /** Returns the BBT time corresponding to the supplied frame position.
2289  * @param frame the position in audio samples.
2290  * @return the BBT time at the frame position .
2291  *
2292  */
2293 BBT_Time
2294 TempoMap::bbt_at_frame (framepos_t frame)
2295 {
2296         if (frame < 0) {
2297                 BBT_Time bbt;
2298                 bbt.bars = 1;
2299                 bbt.beats = 1;
2300                 bbt.ticks = 0;
2301 #ifndef NDEBUG
2302                 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2303 #endif
2304                 return bbt;
2305         }
2306
2307         const double minute =  minute_at_frame (frame);
2308
2309         Glib::Threads::RWLock::ReaderLock lm (lock);
2310
2311         return bbt_at_minute_locked (_metrics, minute);
2312 }
2313
2314 BBT_Time
2315 TempoMap::bbt_at_frame_rt (framepos_t frame)
2316 {
2317         const double minute =  minute_at_frame (frame);
2318
2319         Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2320
2321         if (!lm.locked()) {
2322                 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2323         }
2324
2325         return bbt_at_minute_locked (_metrics, minute);
2326 }
2327
2328 Timecode::BBT_Time
2329 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2330 {
2331         if (minute < 0) {
2332                 BBT_Time bbt;
2333                 bbt.bars = 1;
2334                 bbt.beats = 1;
2335                 bbt.ticks = 0;
2336                 return bbt;
2337         }
2338
2339         const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2340         MeterSection* prev_m = 0;
2341         MeterSection* next_m = 0;
2342
2343         MeterSection* m;
2344
2345         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2346                 if (!(*i)->is_tempo()) {
2347                         m = static_cast<MeterSection*> (*i);
2348                         if (prev_m && m->minute() > minute) {
2349                                 next_m = m;
2350                                 break;
2351                         }
2352                         prev_m = m;
2353                 }
2354         }
2355
2356         double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2357
2358         /* handle frame before first meter */
2359         if (minute < prev_m->minute()) {
2360                 beat = 0.0;
2361         }
2362         /* audio locked meters fake their beat */
2363         if (next_m && next_m->beat() < beat) {
2364                 beat = next_m->beat();
2365         }
2366
2367         beat = max (0.0, beat);
2368
2369         const double beats_in_ms = beat - prev_m->beat();
2370         const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2371         const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2372         const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2373         const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2374
2375         BBT_Time ret;
2376
2377         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2378         ret.beats = (uint32_t) floor (remaining_beats);
2379         ret.bars = total_bars;
2380
2381         /* 0 0 0 to 1 1 0 - based mapping*/
2382         ++ret.bars;
2383         ++ret.beats;
2384
2385         if (ret.ticks >= BBT_Time::ticks_per_beat) {
2386                 ++ret.beats;
2387                 ret.ticks -= BBT_Time::ticks_per_beat;
2388         }
2389
2390         if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2391                 ++ret.bars;
2392                 ret.beats = 1;
2393         }
2394
2395         return ret;
2396 }
2397
2398 /** Returns the frame position corresponding to the supplied BBT time.
2399  * @param bbt the position in BBT time.
2400  * @return the frame position at bbt.
2401  *
2402  */
2403 framepos_t
2404 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2405 {
2406         if (bbt.bars < 1) {
2407 #ifndef NDEBUG
2408                 warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
2409 #endif
2410                 return 0;
2411         }
2412
2413         if (bbt.beats < 1) {
2414                 throw std::logic_error ("beats are counted from one");
2415         }
2416
2417         double minute;
2418         {
2419                 Glib::Threads::RWLock::ReaderLock lm (lock);
2420                 minute = minute_at_bbt_locked (_metrics, bbt);
2421         }
2422
2423         return frame_at_minute (minute);
2424 }
2425
2426 /* meter & tempo section based */
2427 double
2428 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2429 {
2430         /* HOLD THE READER LOCK */
2431
2432         const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2433         return ret;
2434 }
2435
2436 /**
2437  * Returns the quarter-note beat position corresponding to the supplied frame.
2438  *
2439  * @param frame the position in frames.
2440  * @return The quarter-note position of the supplied frame. Ignores meter.
2441  *
2442 */
2443 double
2444 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2445 {
2446         const double minute =  minute_at_frame (frame);
2447
2448         Glib::Threads::RWLock::ReaderLock lm (lock);
2449
2450         return pulse_at_minute_locked (_metrics, minute) * 4.0;
2451 }
2452
2453 double
2454 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2455 {
2456         const double minute =  minute_at_frame (frame);
2457
2458         Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2459
2460         if (!lm.locked()) {
2461                 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2462         }
2463
2464         return pulse_at_minute_locked (_metrics, minute) * 4.0;
2465 }
2466
2467 /**
2468  * Returns the frame position corresponding to the supplied quarter-note beat.
2469  *
2470  * @param quarter_note the quarter-note position.
2471  * @return the frame position of the supplied quarter-note. Ignores meter.
2472  *
2473  *
2474 */
2475 framepos_t
2476 TempoMap::frame_at_quarter_note (const double quarter_note) const
2477 {
2478         double minute;
2479         {
2480                 Glib::Threads::RWLock::ReaderLock lm (lock);
2481
2482                 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2483         }
2484
2485         return frame_at_minute (minute);
2486 }
2487
2488 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2489  * @param beat The BBT (meter-based) beat.
2490  * @return The quarter-note position of the supplied BBT (meter-based) beat.
2491  *
2492  * a quarter-note may be compared with and assigned to Evoral::Beats.
2493  *
2494  */
2495 double
2496 TempoMap::quarter_note_at_beat (const double beat) const
2497 {
2498         Glib::Threads::RWLock::ReaderLock lm (lock);
2499
2500         return pulse_at_beat_locked (_metrics, beat) * 4.0;
2501 }
2502
2503 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2504  * @param quarter_note The position in quarter-note beats.
2505  * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2506  *
2507  * a quarter-note is the musical unit of Evoral::Beats.
2508  *
2509  */
2510 double
2511 TempoMap::beat_at_quarter_note (const double quarter_note) const
2512 {
2513         Glib::Threads::RWLock::ReaderLock lm (lock);
2514
2515         return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2516 }
2517
2518 /** Returns the duration in frames between two supplied quarter-note beat positions.
2519  * @param start the first position in quarter-note beats.
2520  * @param end the end position in quarter-note beats.
2521  * @return the frame distance ober the quarter-note beats duration.
2522  *
2523  * use this rather than e.g.
2524  * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2525  * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2526  *
2527  */
2528 framecnt_t
2529 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2530 {
2531         double minutes;
2532
2533         {
2534                 Glib::Threads::RWLock::ReaderLock lm (lock);
2535                 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2536         }
2537
2538         return frame_at_minute (minutes);
2539 }
2540
2541 double
2542 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2543 {
2544
2545         return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2546 }
2547
2548 double
2549 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2550 {
2551         Glib::Threads::RWLock::ReaderLock lm (lock);
2552
2553         return quarter_notes_between_frames_locked (_metrics, start, end);
2554 }
2555
2556 double
2557 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2558 {
2559         const TempoSection* prev_t = 0;
2560
2561         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2562                 TempoSection* t;
2563
2564                 if ((*i)->is_tempo()) {
2565                         t = static_cast<TempoSection*> (*i);
2566                         if (!t->active()) {
2567                                 continue;
2568                         }
2569                         if (prev_t && t->frame() > start) {
2570                                 break;
2571                         }
2572                         prev_t = t;
2573                 }
2574         }
2575         assert (prev_t);
2576         const double start_qn = prev_t->pulse_at_frame (start);
2577
2578         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2579                 TempoSection* t;
2580
2581                 if ((*i)->is_tempo()) {
2582                         t = static_cast<TempoSection*> (*i);
2583                         if (!t->active()) {
2584                                 continue;
2585                         }
2586                         if (prev_t && t->frame() > end) {
2587                                 break;
2588                         }
2589                         prev_t = t;
2590                 }
2591         }
2592         const double end_qn = prev_t->pulse_at_frame (end);
2593
2594         return (end_qn - start_qn) * 4.0;
2595 }
2596
2597 bool
2598 TempoMap::check_solved (const Metrics& metrics) const
2599 {
2600         TempoSection* prev_t = 0;
2601         MeterSection* prev_m = 0;
2602
2603         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2604                 TempoSection* t;
2605                 MeterSection* m;
2606                 if ((*i)->is_tempo()) {
2607                         t = static_cast<TempoSection*> (*i);
2608                         if (!t->active()) {
2609                                 continue;
2610                         }
2611                         if (prev_t) {
2612                                 /* check ordering */
2613                                 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2614                                         return false;
2615                                 }
2616
2617                                 /* precision check ensures tempo and frames align.*/
2618                                 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2619                                         if (!t->locked_to_meter()) {
2620                                                 return false;
2621                                         }
2622                                 }
2623
2624                                 /* gradient limit - who knows what it should be?
2625                                    things are also ok (if a little chaotic) without this
2626                                 */
2627                                 if (fabs (prev_t->c()) > 1000.0) {
2628                                         //std::cout << "c : " << prev_t->c() << std::endl;
2629                                         return false;
2630                                 }
2631                         }
2632                         prev_t = t;
2633                 }
2634
2635                 if (!(*i)->is_tempo()) {
2636                         m = static_cast<MeterSection*> (*i);
2637                         if (prev_m && m->position_lock_style() == AudioTime) {
2638                                 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2639                                 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2640                                 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2641                                 */
2642                                 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2643                                         return false;
2644                                 }
2645                         }
2646
2647                         prev_m = m;
2648                 }
2649
2650         }
2651
2652         return true;
2653 }
2654
2655 bool
2656 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2657 {
2658         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2659                 TempoSection* t;
2660                 if ((*i)->is_tempo()) {
2661                         t = static_cast<TempoSection*> (*i);
2662                         if (t->locked_to_meter()) {
2663                                 t->set_active (true);
2664                         } else if (t->position_lock_style() == AudioTime) {
2665                                 if (t->frame() < frame) {
2666                                         t->set_active (false);
2667                                         t->set_pulse (-1.0);
2668                                 } else if (t->frame() > frame) {
2669                                         t->set_active (true);
2670                                 } else if (t->frame() == frame) {
2671                                         return false;
2672                                 }
2673                         }
2674                 }
2675         }
2676         return true;
2677 }
2678
2679 bool
2680 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2681 {
2682         TempoSection* prev_t = 0;
2683         TempoSection* section_prev = 0;
2684         double first_m_minute = 0.0;
2685         const bool sml = section->locked_to_meter();
2686
2687         /* can't move a tempo before the first meter */
2688         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2689                 MeterSection* m;
2690                 if (!(*i)->is_tempo()) {
2691                         m = static_cast<MeterSection*> (*i);
2692                         if (m->initial()) {
2693                                 first_m_minute = m->minute();
2694                                 break;
2695                         }
2696                 }
2697         }
2698         if (!section->initial() && minute <= first_m_minute) {
2699                 return false;
2700         }
2701
2702         section->set_active (true);
2703         section->set_minute (minute);
2704
2705         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2706                 TempoSection* t;
2707                 if ((*i)->is_tempo()) {
2708                         t = static_cast<TempoSection*> (*i);
2709
2710                         if (!t->active()) {
2711                                 continue;
2712                         }
2713
2714                         if (prev_t) {
2715
2716                                 if (t == section) {
2717                                         continue;
2718                                 }
2719
2720                                 if (t->frame() == frame_at_minute (minute)) {
2721                                         return false;
2722                                 }
2723
2724                                 const bool tlm = t->position_lock_style() == MusicTime;
2725
2726                                 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2727                                         section_prev = prev_t;
2728
2729                                         section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2730                                         if (!section->locked_to_meter()) {
2731                                                 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2732                                         }
2733                                         prev_t = section;
2734                                 }
2735
2736                                 if (t->position_lock_style() == MusicTime) {
2737                                         prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2738                                         t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2739                                 } else {
2740                                         prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2741                                         if (!t->locked_to_meter()) {
2742                                                 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2743                                         }
2744                                 }
2745                         }
2746                         prev_t = t;
2747                 }
2748         }
2749
2750 #if (0)
2751         recompute_tempi (imaginary);
2752
2753         if (check_solved (imaginary)) {
2754                 return true;
2755         } else {
2756                 dunp (imaginary, std::cout);
2757         }
2758 #endif
2759
2760         MetricSectionFrameSorter fcmp;
2761         imaginary.sort (fcmp);
2762
2763         recompute_tempi (imaginary);
2764
2765         if (check_solved (imaginary)) {
2766                 return true;
2767         }
2768
2769         return false;
2770 }
2771
2772 bool
2773 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2774 {
2775         TempoSection* prev_t = 0;
2776         TempoSection* section_prev = 0;
2777
2778         section->set_pulse (pulse);
2779
2780         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2781                 TempoSection* t;
2782                 if ((*i)->is_tempo()) {
2783                         t = static_cast<TempoSection*> (*i);
2784                         if (!t->active()) {
2785                                 continue;
2786                         }
2787                         if (t->initial()) {
2788                                 t->set_pulse (0.0);
2789                                 prev_t = t;
2790                                 continue;
2791                         }
2792                         if (prev_t) {
2793                                 if (t == section) {
2794                                         section_prev = prev_t;
2795                                         continue;
2796                                 }
2797
2798                                 if (t->position_lock_style() == MusicTime) {
2799                                         prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2800                                         t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2801                                 } else {
2802                                         prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2803                                         if (!t->locked_to_meter()) {
2804                                                 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2805                                         }
2806                                 }
2807                         }
2808                         prev_t = t;
2809                 }
2810         }
2811
2812         if (section_prev) {
2813                 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2814                 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2815         }
2816
2817 #if (0)
2818         recompute_tempi (imaginary);
2819
2820         if (check_solved (imaginary)) {
2821                 return true;
2822         } else {
2823                 dunp (imaginary, std::cout);
2824         }
2825 #endif
2826
2827         MetricSectionSorter cmp;
2828         imaginary.sort (cmp);
2829
2830         recompute_tempi (imaginary);
2831         /* Reordering
2832          * XX need a restriction here, but only for this case,
2833          * as audio locked tempos don't interact in the same way.
2834          *
2835          * With music-locked tempos, the solution to cross-dragging can fly off the screen
2836          * e.g.
2837          * |50 bpm                        |250 bpm |60 bpm
2838          *                drag 250 to the pulse after 60->
2839          * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2840          */
2841         if (check_solved (imaginary)) {
2842                 return true;
2843         }
2844
2845         return false;
2846 }
2847
2848 bool
2849 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2850 {
2851         /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2852         const MeterSection* other =  &meter_section_at_minute_locked (imaginary, minute);
2853         if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2854                 return false;
2855         }
2856
2857         if (section->initial()) {
2858                 /* lock the first tempo to our first meter */
2859                 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2860                         return false;
2861                 }
2862         }
2863
2864         TempoSection* meter_locked_tempo = 0;
2865
2866         for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2867                 TempoSection* t;
2868                 if ((*ii)->is_tempo()) {
2869                         t = static_cast<TempoSection*> (*ii);
2870                         if (t->locked_to_meter() && t->frame() == section->frame()) {
2871                                 meter_locked_tempo = t;
2872                                 break;
2873                         }
2874                 }
2875         }
2876
2877         if (!meter_locked_tempo) {
2878                 return false;
2879         }
2880
2881         MeterSection* prev_m = 0;
2882         Metrics future_map;
2883         TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2884         bool solved = false;
2885
2886         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2887                 MeterSection* m;
2888                 if (!(*i)->is_tempo()) {
2889                         m = static_cast<MeterSection*> (*i);
2890                         if (m == section){
2891                                 if (prev_m && !section->initial()) {
2892                                         const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2893                                         if (beats + prev_m->beat() < section->beat()) {
2894                                                 /* set the section pulse according to its musical position,
2895                                                  * as an earlier time than this has been requested.
2896                                                 */
2897                                                 const double new_pulse = ((section->beat() - prev_m->beat())
2898                                                                           / prev_m->note_divisor()) + prev_m->pulse();
2899
2900                                                 tempo_copy->set_position_lock_style (MusicTime);
2901                                                 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2902                                                         meter_locked_tempo->set_position_lock_style (MusicTime);
2903                                                         section->set_position_lock_style (MusicTime);
2904                                                         section->set_pulse (new_pulse);
2905                                                         solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2906                                                         meter_locked_tempo->set_position_lock_style (AudioTime);
2907                                                         section->set_position_lock_style (AudioTime);
2908                                                         section->set_minute (meter_locked_tempo->minute());
2909
2910                                                 } else {
2911                                                         solved = false;
2912                                                 }
2913
2914                                                 Metrics::const_iterator d = future_map.begin();
2915                                                 while (d != future_map.end()) {
2916                                                         delete (*d);
2917                                                         ++d;
2918                                                 }
2919
2920                                                 if (!solved) {
2921                                                         return false;
2922                                                 }
2923                                         } else {
2924                                                 /* all is ok. set section's locked tempo if allowed.
2925                                                    possibly disallow if there is an adjacent audio-locked tempo.
2926                                                    XX this check could possibly go. its never actually happened here.
2927                                                 */
2928                                                 MeterSection* meter_copy = const_cast<MeterSection*>
2929                                                         (&meter_section_at_minute_locked (future_map, section->minute()));
2930
2931                                                 meter_copy->set_minute (minute);
2932
2933                                                 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2934                                                         section->set_minute (minute);
2935                                                         meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2936                                                                                                 / prev_m->note_divisor()) + prev_m->pulse());
2937                                                         solve_map_minute (imaginary, meter_locked_tempo, minute);
2938                                                 } else {
2939                                                         solved = false;
2940                                                 }
2941
2942                                                 Metrics::const_iterator d = future_map.begin();
2943                                                 while (d != future_map.end()) {
2944                                                         delete (*d);
2945                                                         ++d;
2946                                                 }
2947
2948                                                 if (!solved) {
2949                                                         return false;
2950                                                 }
2951                                         }
2952                                 } else {
2953                                         /* initial (first meter atm) */
2954
2955                                         tempo_copy->set_minute (minute);
2956                                         tempo_copy->set_pulse (0.0);
2957
2958                                         if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2959                                                 section->set_minute (minute);
2960                                                 meter_locked_tempo->set_minute (minute);
2961                                                 meter_locked_tempo->set_pulse (0.0);
2962                                                 solve_map_minute (imaginary, meter_locked_tempo, minute);
2963                                         } else {
2964                                                 solved = false;
2965                                         }
2966
2967                                         Metrics::const_iterator d = future_map.begin();
2968                                         while (d != future_map.end()) {
2969                                                 delete (*d);
2970                                                 ++d;
2971                                         }
2972
2973                                         if (!solved) {
2974                                                 return false;
2975                                         }
2976
2977                                         pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2978                                         section->set_beat (b_bbt);
2979                                         section->set_pulse (0.0);
2980
2981                                 }
2982                                 break;
2983                         }
2984
2985                         prev_m = m;
2986                 }
2987         }
2988
2989         MetricSectionFrameSorter fcmp;
2990         imaginary.sort (fcmp);
2991
2992         recompute_meters (imaginary);
2993
2994         return true;
2995 }
2996
2997 bool
2998 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2999 {
3000         /* disallow setting section to an existing meter's bbt */
3001         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
3002                 MeterSection* m;
3003                 if (!(*i)->is_tempo()) {
3004                         m = static_cast<MeterSection*> (*i);
3005                         if (m != section && m->bbt().bars == when.bars) {
3006                                 return false;
3007                         }
3008                 }
3009         }
3010
3011         MeterSection* prev_m = 0;
3012         MeterSection* section_prev = 0;
3013
3014         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
3015                 MeterSection* m;
3016                 if (!(*i)->is_tempo()) {
3017                         m = static_cast<MeterSection*> (*i);
3018
3019                         if (m == section) {
3020                                 continue;
3021                         }
3022
3023                         pair<double, BBT_Time> b_bbt;
3024                         double new_pulse = 0.0;
3025
3026                         if (prev_m && m->bbt().bars > when.bars && !section_prev){
3027                                 section_prev = prev_m;
3028
3029                                 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
3030                                 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
3031                                 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
3032
3033                                 section->set_beat (b_bbt);
3034                                 section->set_pulse (pulse);
3035                                 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3036                                 prev_m = section;
3037                         }
3038
3039                         if (m->position_lock_style() == AudioTime) {
3040                                 TempoSection* meter_locked_tempo = 0;
3041
3042                                 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
3043                                         TempoSection* t;
3044                                         if ((*ii)->is_tempo()) {
3045                                                 t = static_cast<TempoSection*> (*ii);
3046                                                 if (t->locked_to_meter() && t->frame() == m->frame()) {
3047                                                         meter_locked_tempo = t;
3048                                                         break;
3049                                                 }
3050                                         }
3051                                 }
3052
3053                                 if (!meter_locked_tempo) {
3054                                         return false;
3055                                 }
3056
3057                                 if (prev_m) {
3058                                         double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3059
3060                                         if (beats + prev_m->beat() != m->beat()) {
3061                                                 /* tempo/ meter change caused a change in beat (bar). */
3062
3063                                                 /* the user has requested that the previous section of music overlaps this one.
3064                                                    we have no choice but to change the bar number here, as being locked to audio means
3065                                                    we must stay where we are on the timeline.
3066                                                 */
3067                                                 beats = m->beat() - prev_m->beat();
3068                                                 b_bbt = make_pair (beats + prev_m->beat()
3069                                                                    , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3070                                                 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3071
3072                                         } else if (!m->initial()) {
3073                                                 b_bbt = make_pair (m->beat(), m->bbt());
3074                                                 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3075                                         }
3076                                 } else {
3077                                         b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3078                                 }
3079
3080                                 meter_locked_tempo->set_pulse (new_pulse);
3081                                 m->set_beat (b_bbt);
3082                                 m->set_pulse (new_pulse);
3083
3084                         } else {
3085                                 /* MusicTime */
3086                                 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3087                                 if (beats + prev_m->beat() != m->beat()) {
3088                                         /* tempo/ meter change caused a change in beat (bar). */
3089                                         b_bbt = make_pair (beats + prev_m->beat()
3090                                                            , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3091                                 } else {
3092                                         b_bbt = make_pair (beats + prev_m->beat()
3093                                                            , m->bbt());
3094                                 }
3095                                 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3096                                 m->set_beat (b_bbt);
3097                                 m->set_pulse (new_pulse);
3098                                 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3099                         }
3100
3101                         prev_m = m;
3102                 }
3103         }
3104
3105         if (!section_prev) {
3106
3107                 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3108                 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3109                 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3110
3111                 section->set_beat (b_bbt);
3112                 section->set_pulse (pulse);
3113                 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3114         }
3115
3116         MetricSectionSorter cmp;
3117         imaginary.sort (cmp);
3118
3119         recompute_meters (imaginary);
3120
3121         return true;
3122 }
3123
3124 /** places a copy of _metrics into copy and returns a pointer
3125  *  to section's equivalent in copy.
3126  */
3127 TempoSection*
3128 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section) const
3129 {
3130         TempoSection* ret = 0;
3131
3132         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3133                 if ((*i)->is_tempo()) {
3134                         TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3135                         if (t == section) {
3136                                 ret = new TempoSection (*t);
3137                                 copy.push_back (ret);
3138                                 continue;
3139                         }
3140
3141                         TempoSection* cp = new TempoSection (*t);
3142                         copy.push_back (cp);
3143                 } else {
3144                         MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3145                         MeterSection* cp = new MeterSection (*m);
3146                         copy.push_back (cp);
3147                 }
3148         }
3149
3150         return ret;
3151 }
3152
3153 MeterSection*
3154 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section) const
3155 {
3156         MeterSection* ret = 0;
3157
3158         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3159                 if ((*i)->is_tempo()) {
3160                         TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3161                         TempoSection* cp = new TempoSection (*t);
3162                         copy.push_back (cp);
3163                 } else {
3164                         MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3165                         if (m == section) {
3166                                 ret = new MeterSection (*m);
3167                                 copy.push_back (ret);
3168                                 continue;
3169                         }
3170                         MeterSection* cp = new MeterSection (*m);
3171                         copy.push_back (cp);
3172                 }
3173         }
3174
3175         return ret;
3176 }
3177
3178 /** answers the question "is this a valid beat position for this tempo section?".
3179  *  it returns true if the tempo section can be moved to the requested bbt position,
3180  *  leaving the tempo map in a solved state.
3181  * @param ts the tempo section to be moved
3182  * @param bbt the requested new position for the tempo section
3183  * @return true if the tempo section can be moved to the position, otherwise false.
3184  */
3185 bool
3186 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3187 {
3188         Metrics copy;
3189         TempoSection* tempo_copy = 0;
3190
3191         {
3192                 Glib::Threads::RWLock::ReaderLock lm (lock);
3193                 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3194                 if (!tempo_copy) {
3195                         return false;
3196                 }
3197         }
3198
3199         const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3200
3201         Metrics::const_iterator d = copy.begin();
3202         while (d != copy.end()) {
3203                 delete (*d);
3204                 ++d;
3205         }
3206
3207         return ret;
3208 }
3209
3210 /**
3211 * This is for a gui that needs to know the pulse or frame of a tempo section if it were to be moved to some bbt time,
3212 * taking any possible reordering as a consequence of this into account.
3213 * @param section - the section to be altered
3214 * @param bbt - the BBT time  where the altered tempo will fall
3215 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3216 */
3217 pair<double, framepos_t>
3218 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3219 {
3220         Metrics future_map;
3221         pair<double, framepos_t> ret = make_pair (0.0, 0);
3222
3223         Glib::Threads::RWLock::ReaderLock lm (lock);
3224
3225         TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3226
3227         const double beat = beat_at_bbt_locked (future_map, bbt);
3228
3229         if (section->position_lock_style() == AudioTime) {
3230                 tempo_copy->set_position_lock_style (MusicTime);
3231         }
3232
3233         if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3234                 ret.first = tempo_copy->pulse();
3235                 ret.second = tempo_copy->frame();
3236         } else {
3237                 ret.first = section->pulse();
3238                 ret.second = section->frame();
3239         }
3240
3241         Metrics::const_iterator d = future_map.begin();
3242         while (d != future_map.end()) {
3243                 delete (*d);
3244                 ++d;
3245         }
3246         return ret;
3247 }
3248
3249 /** moves a TempoSection to a specified position.
3250  * @param ts - the section to be moved
3251  * @param frame - the new position in frames for the tempo
3252  * @param sub_num - the snap division to use if using musical time.
3253  *
3254  * if sub_num is non-zero, the frame position is used to calculate an exact
3255  * musical position.
3256  * sub_num   | effect
3257  * -1        | snap to bars (meter-based)
3258  *  0        | no snap - use audio frame for musical position
3259  *  1        | snap to meter-based (BBT) beat
3260  * >1        | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3261  *
3262  * this follows the snap convention in the gui.
3263  * if sub_num is zero, the musical position will be taken from the supplied frame.
3264  */
3265 void
3266 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3267 {
3268         Metrics future_map;
3269
3270         if (ts->position_lock_style() == MusicTime) {
3271                 {
3272                         /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3273                         Glib::Threads::RWLock::WriterLock lm (lock);
3274                         TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3275
3276                         tempo_copy->set_position_lock_style (AudioTime);
3277
3278                         if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3279                                 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3280                                 const double pulse = pulse_at_beat_locked (future_map, beat);
3281
3282                                 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3283                                         solve_map_pulse (_metrics, ts, pulse);
3284                                         recompute_meters (_metrics);
3285                                 }
3286                         }
3287                 }
3288
3289         } else {
3290
3291                 {
3292                         Glib::Threads::RWLock::WriterLock lm (lock);
3293                         TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3294
3295                         if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3296                                 if (sub_num != 0) {
3297                                         /* We're moving the object that defines the grid while snapping to it...
3298                                          * Placing the ts at the beat corresponding to the requested frame may shift the
3299                                          * grid in such a way that the mouse is left hovering over a completerly different division,
3300                                          * causing jittering when the mouse next moves (esp. large tempo deltas).
3301                                          *
3302                                          * This alters the snap behaviour slightly in that we snap to beat divisions
3303                                          * in the future map rather than the existing one.
3304                                          */
3305                                         const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3306                                         const framepos_t snapped_frame = frame_at_minute (minute_at_pulse_locked (future_map, qn / 4.0));
3307
3308                                         if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3309                                                 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3310                                                 ts->set_pulse (qn / 4.0);
3311                                                 recompute_meters (_metrics);
3312                                         }
3313                                 } else {
3314                                         solve_map_minute (_metrics, ts, minute_at_frame (frame));
3315                                         recompute_meters (_metrics);
3316                                 }
3317                         }
3318                 }
3319         }
3320
3321         Metrics::const_iterator d = future_map.begin();
3322         while (d != future_map.end()) {
3323                 delete (*d);
3324                 ++d;
3325         }
3326
3327         MetricPositionChanged (PropertyChange ()); // Emit Signal
3328 }
3329
3330 /** moves a MeterSection to a specified position.
3331  * @param ms - the section to be moved
3332  * @param frame - the new position in frames for the meter
3333  *
3334  * as a meter cannot snap to anything but bars,
3335  * the supplied frame is rounded to the nearest bar, possibly
3336  * leaving the meter position unchanged.
3337  */
3338 void
3339 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3340 {
3341         Metrics future_map;
3342
3343         if (ms->position_lock_style() == AudioTime) {
3344
3345                 {
3346                         Glib::Threads::RWLock::WriterLock lm (lock);
3347                         MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3348
3349                         if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3350                                 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3351                                 recompute_tempi (_metrics);
3352                         }
3353                 }
3354         } else {
3355                 {
3356                         Glib::Threads::RWLock::WriterLock lm (lock);
3357                         MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3358
3359                         const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3360                         const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3361
3362                         if (solve_map_bbt (future_map, copy, bbt)) {
3363                                 solve_map_bbt (_metrics, ms, bbt);
3364                                 recompute_tempi (_metrics);
3365                         }
3366                 }
3367         }
3368
3369         Metrics::const_iterator d = future_map.begin();
3370         while (d != future_map.end()) {
3371                 delete (*d);
3372                 ++d;
3373         }
3374
3375         MetricPositionChanged (PropertyChange ()); // Emit Signal
3376 }
3377
3378 bool
3379 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3380 {
3381         Metrics future_map;
3382         bool can_solve = false;
3383         {
3384                 Glib::Threads::RWLock::WriterLock lm (lock);
3385                 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3386
3387                 if (tempo_copy->type() == TempoSection::Constant) {
3388                         tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3389                         tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3390                 } else {
3391                         tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3392                         tempo_copy->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3393                 }
3394
3395                 if (ts->clamped()) {
3396                         TempoSection* prev = 0;
3397                         if ((prev = previous_tempo_section_locked (future_map, tempo_copy)) != 0) {
3398                                 prev->set_end_note_types_per_minute (tempo_copy->note_types_per_minute());
3399                         }
3400                 }
3401
3402                 recompute_tempi (future_map);
3403
3404                 if (check_solved (future_map)) {
3405                         if (ts->type() == TempoSection::Constant) {
3406                                 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3407                                 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3408                         } else {
3409                                 ts->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3410                                 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3411                         }
3412
3413                         if (ts->clamped()) {
3414                                 TempoSection* prev = 0;
3415                                 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3416                                         prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3417                                 }
3418                         }
3419
3420                         recompute_map (_metrics);
3421                         can_solve = true;
3422                 }
3423         }
3424
3425         Metrics::const_iterator d = future_map.begin();
3426         while (d != future_map.end()) {
3427                 delete (*d);
3428                 ++d;
3429         }
3430         if (can_solve) {
3431                 MetricPositionChanged (PropertyChange ()); // Emit Signal
3432         }
3433
3434         return can_solve;
3435 }
3436
3437 void
3438 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3439 {
3440         /*
3441           Ts (future prev_t)   Tnext
3442           |                    |
3443           |     [drag^]        |
3444           |----------|----------
3445                 e_f  qn_beats(frame)
3446         */
3447
3448         Metrics future_map;
3449
3450         {
3451                 Glib::Threads::RWLock::WriterLock lm (lock);
3452
3453                 if (!ts) {
3454                         return;
3455                 }
3456
3457                 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3458
3459                 if (!prev_t) {
3460                         return;
3461                 }
3462
3463                 /* minimum allowed measurement distance in frames */
3464                 framepos_t const min_dframe = 2;
3465
3466                 double new_bpm;
3467                 if (prev_t->clamped()) {
3468                         TempoSection* next_t = next_tempo_section_locked (future_map, prev_t);
3469                         TempoSection* prev_to_prev_t = previous_tempo_section_locked (future_map, prev_t);
3470                         /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3471                            constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3472                         */
3473                         double contribution = 0.0;
3474                         if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3475                                 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3476                         }
3477                         framepos_t const fr_off = (end_frame - frame);
3478                         const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3479
3480                         if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3481                                 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3482                                                                                      / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3483                         } else {
3484                                 new_bpm = prev_t->note_types_per_minute();
3485                         }
3486                 } else {
3487                         if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3488
3489                                 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
3490                                                                              / (double) (end_frame - prev_t->frame()));
3491                         } else {
3492                                 new_bpm = prev_t->note_types_per_minute();
3493                         }
3494
3495                         new_bpm = min (new_bpm, (double) 1000.0);
3496                 }
3497                 /* don't clamp and proceed here.
3498                    testing has revealed that this can go negative,
3499                    which is an entirely different thing to just being too low.
3500                 */
3501
3502                 if (new_bpm < 0.5) {
3503                         goto out;
3504                 }
3505
3506                 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3507                         prev_t->set_note_types_per_minute (new_bpm);
3508                 } else {
3509                         prev_t->set_end_note_types_per_minute (new_bpm);
3510                         prev_t->set_note_types_per_minute (new_bpm);
3511                 }
3512
3513                 if (prev_t->clamped()) {
3514                         TempoSection* prev = 0;
3515                         if ((prev = previous_tempo_section_locked (future_map, prev_t)) != 0) {
3516                                 prev->set_end_note_types_per_minute (prev_t->note_types_per_minute());
3517                         }
3518                 }
3519
3520                 recompute_tempi (future_map);
3521                 recompute_meters (future_map);
3522
3523                 if (check_solved (future_map)) {
3524                         if (prev_t && prev_t->type() == TempoSection::Ramp) {
3525                                 ts->set_note_types_per_minute (new_bpm);
3526                         } else {
3527                                 ts->set_end_note_types_per_minute (new_bpm);
3528                                 ts->set_note_types_per_minute (new_bpm);
3529                         }
3530                         if (ts->clamped()) {
3531                                 TempoSection* prev = 0;
3532                                 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3533                                         prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3534                                 }
3535                         }
3536                         recompute_tempi (_metrics);
3537                         recompute_meters (_metrics);
3538                 }
3539         }
3540
3541
3542 out:
3543         Metrics::const_iterator d = future_map.begin();
3544         while (d != future_map.end()) {
3545                 delete (*d);
3546                 ++d;
3547         }
3548         MetricPositionChanged (PropertyChange ()); // Emit Signal
3549
3550
3551 }
3552 void
3553 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3554 {
3555         /*
3556           Ts (future prev_t)   Tnext
3557           |                    |
3558           |     [drag^]        |
3559           |----------|----------
3560                 e_f  qn_beats(frame)
3561         */
3562
3563         Metrics future_map;
3564
3565         {
3566                 Glib::Threads::RWLock::WriterLock lm (lock);
3567
3568                 if (!ts) {
3569                         return;
3570                 }
3571
3572                 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3573
3574                 if (!prev_t) {
3575                         return;
3576                 }
3577
3578                 /* minimum allowed measurement distance in frames */
3579                 framepos_t const min_dframe = 2;
3580                 double new_bpm;
3581
3582                 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3583                         new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->frame() - frame)
3584                                                                                  / (double) (prev_t->frame() - end_frame));
3585                 } else {
3586                         new_bpm = prev_t->end_note_types_per_minute();
3587                 }
3588
3589                 new_bpm = min (new_bpm, (double) 1000.0);
3590
3591                 if (new_bpm < 0.5) {
3592                         goto out;
3593                 }
3594
3595                 prev_t->set_end_note_types_per_minute (new_bpm);
3596
3597                 TempoSection* next = 0;
3598                 if ((next = next_tempo_section_locked (future_map, prev_t)) != 0) {
3599                         if (next->clamped()) {
3600                                 next->set_note_types_per_minute (prev_t->end_note_types_per_minute());
3601                         }
3602                 }
3603
3604                 recompute_tempi (future_map);
3605                 recompute_meters (future_map);
3606
3607                 if (check_solved (future_map)) {
3608                         ts->set_end_note_types_per_minute (new_bpm);
3609
3610                         TempoSection* true_next = 0;
3611                         if ((true_next = next_tempo_section_locked (_metrics, ts)) != 0) {
3612                                 if (true_next->clamped()) {
3613                                         true_next->set_note_types_per_minute (ts->end_note_types_per_minute());
3614                                 }
3615                         }
3616
3617                         recompute_tempi (_metrics);
3618                         recompute_meters (_metrics);
3619                 }
3620         }
3621
3622
3623 out:
3624         Metrics::const_iterator d = future_map.begin();
3625         while (d != future_map.end()) {
3626                 delete (*d);
3627                 ++d;
3628         }
3629
3630         MetricPositionChanged (PropertyChange ()); // Emit Signal
3631 }
3632
3633 bool
3634 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
3635 {
3636         TempoSection* next_t = 0;
3637         TempoSection* next_to_next_t = 0;
3638         Metrics future_map;
3639         bool can_solve = false;
3640
3641         /* minimum allowed measurement distance in frames */
3642         framepos_t const min_dframe = 2;
3643
3644         {
3645                 Glib::Threads::RWLock::WriterLock lm (lock);
3646                 if (!ts) {
3647                         return false;
3648                 }
3649
3650                 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3651                 TempoSection* prev_to_prev_t = 0;
3652                 const frameoffset_t fr_off = end_frame - frame;
3653
3654                 if (!tempo_copy) {
3655                         return false;
3656                 }
3657
3658                 if (tempo_copy->pulse() > 0.0) {
3659                         prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
3660                 }
3661
3662                 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3663                         if ((*i)->is_tempo() && (*i)->minute() >  tempo_copy->minute()) {
3664                                 next_t = static_cast<TempoSection*> (*i);
3665                                 break;
3666                         }
3667                 }
3668
3669                 if (!next_t) {
3670                         return false;
3671                 }
3672
3673                 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3674                         if ((*i)->is_tempo() && (*i)->minute() >  next_t->minute()) {
3675                                 next_to_next_t = static_cast<TempoSection*> (*i);
3676                                 break;
3677                         }
3678                 }
3679
3680                 if (!next_to_next_t) {
3681                         return false;
3682                 }
3683
3684                 double prev_contribution = 0.0;
3685
3686                 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3687                         prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3688                 }
3689
3690                 const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
3691
3692
3693                 framepos_t old_tc_minute = tempo_copy->minute();
3694                 double old_next_minute = next_t->minute();
3695                 double old_next_to_next_minute = next_to_next_t->minute();
3696
3697                 double new_bpm;
3698                 double new_next_bpm;
3699                 double new_copy_end_bpm;
3700
3701                 if (frame > tempo_copy->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > tempo_copy->frame() + min_dframe) {
3702                         new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
3703                                                                                        / (double) (end_frame - tempo_copy->frame()));
3704                 } else {
3705                         new_bpm = tempo_copy->note_types_per_minute();
3706                 }
3707
3708                 /* don't clamp and proceed here.
3709                    testing has revealed that this can go negative,
3710                    which is an entirely different thing to just being too low.
3711                 */
3712                 if (new_bpm < 0.5) {
3713                         return false;
3714                 }
3715
3716                 new_bpm = min (new_bpm, (double) 1000.0);
3717
3718                 tempo_copy->set_note_types_per_minute (new_bpm);
3719                 if (tempo_copy->type() == TempoSection::Constant) {
3720                         tempo_copy->set_end_note_types_per_minute (new_bpm);
3721                 }
3722
3723                 recompute_tempi (future_map);
3724
3725                 if (check_solved (future_map)) {
3726
3727                         if (!next_t) {
3728                                 return false;
3729                         }
3730
3731                         ts->set_note_types_per_minute (new_bpm);
3732                         if (ts->type() == TempoSection::Constant) {
3733                                 ts->set_end_note_types_per_minute (new_bpm);
3734                         }
3735
3736                         recompute_map (_metrics);
3737
3738                         can_solve = true;
3739                 }
3740
3741                 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3742                         if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) {
3743
3744                                 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3745                                                                                   / (double) ((old_next_to_next_minute) - old_next_minute));
3746
3747                         } else {
3748                                 new_next_bpm = next_t->note_types_per_minute();
3749                         }
3750
3751                         next_t->set_note_types_per_minute (new_next_bpm);
3752                         recompute_tempi (future_map);
3753
3754                         if (check_solved (future_map)) {
3755                                 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3756                                         if ((*i)->is_tempo() && (*i)->minute() >  ts->minute()) {
3757                                                 next_t = static_cast<TempoSection*> (*i);
3758                                                 break;
3759                                         }
3760                                 }
3761
3762                                 if (!next_t) {
3763                                         return false;
3764                                 }
3765                                 next_t->set_note_types_per_minute (new_next_bpm);
3766                                 recompute_map (_metrics);
3767                                 can_solve = true;
3768                         }
3769                 } else {
3770                         double next_frame_ratio = 1.0;
3771                         double copy_frame_ratio = 1.0;
3772
3773                         if (next_to_next_t) {
3774                                 next_frame_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute -  old_next_minute);
3775
3776                                 copy_frame_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute));
3777                         }
3778
3779                         new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
3780                         new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
3781
3782                         tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3783
3784                         if (next_t->clamped()) {
3785                                 next_t->set_note_types_per_minute (new_copy_end_bpm);
3786                         } else {
3787                                 next_t->set_note_types_per_minute (new_next_bpm);
3788                         }
3789
3790                         recompute_tempi (future_map);
3791
3792                         if (check_solved (future_map)) {
3793                                 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3794                                         if ((*i)->is_tempo() && (*i)->minute() >  ts->minute()) {
3795                                                 next_t = static_cast<TempoSection*> (*i);
3796                                                 break;
3797                                         }
3798                                 }
3799
3800                                 if (!next_t) {
3801                                         return false;
3802                                 }
3803
3804                                 if (next_t->clamped()) {
3805                                         next_t->set_note_types_per_minute (new_copy_end_bpm);
3806                                 } else {
3807                                         next_t->set_note_types_per_minute (new_next_bpm);
3808                                 }
3809
3810                                 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3811                                 recompute_map (_metrics);
3812                                 can_solve = true;
3813                         }
3814                 }
3815         }
3816
3817         Metrics::const_iterator d = future_map.begin();
3818         while (d != future_map.end()) {
3819                 delete (*d);
3820                 ++d;
3821         }
3822
3823         MetricPositionChanged (PropertyChange ()); // Emit Signal
3824
3825         return can_solve;
3826 }
3827 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3828  * the supplied frame, possibly returning a negative value.
3829  *
3830  * @param frame  The session frame position.
3831  * @param sub_num The subdivision to use when rounding the beat.
3832  *                A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3833  *                Positive integers indicate quarter note (non BBT) divisions.
3834  *                0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3835  * @return The beat position of the supplied frame.
3836  *
3837  * when working to a musical grid, the use of sub_nom indicates that
3838  * the position should be interpreted musically.
3839  *
3840  * it effectively snaps to meter bars, meter beats or quarter note divisions
3841  * (as per current gui convention) and returns a musical position independent of frame rate.
3842  *
3843  * If the supplied frame lies before the first meter, the return will be negative,
3844  * in which case the returned beat uses the first meter (for BBT subdivisions) and
3845  * the continuation of the tempo curve (backwards).
3846  *
3847  * This function is sensitive to tempo and meter.
3848  */
3849 double
3850 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3851 {
3852         Glib::Threads::RWLock::ReaderLock lm (lock);
3853
3854         return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3855 }
3856
3857 double
3858 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3859 {
3860         return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3861 }
3862
3863 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3864  * the supplied frame, possibly returning a negative value.
3865  *
3866  * @param frame  The session frame position.
3867  * @param sub_num The subdivision to use when rounding the quarter note.
3868  *                A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3869  *                Positive integers indicate quarter note (non BBT) divisions.
3870  *                0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3871  * @return The quarter note position of the supplied frame.
3872  *
3873  * When working to a musical grid, the use of sub_nom indicates that
3874  * the frame position should be interpreted musically.
3875  *
3876  * it effectively snaps to meter bars, meter beats or quarter note divisions
3877  * (as per current gui convention) and returns a musical position independent of frame rate.
3878  *
3879  * If the supplied frame lies before the first meter, the return will be negative,
3880  * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3881  * the continuation of the tempo curve (backwards).
3882  *
3883  * This function is tempo-sensitive.
3884  */
3885 double
3886 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3887 {
3888         Glib::Threads::RWLock::ReaderLock lm (lock);
3889
3890         return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3891 }
3892
3893 double
3894 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3895 {
3896         double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3897
3898         if (sub_num > 1) {
3899                 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3900         } else if (sub_num == 1) {
3901                 /* the gui requested exact musical (BBT) beat */
3902                 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3903         } else if (sub_num == -1) {
3904                 /* snap to  bar */
3905                 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3906                 bbt.beats = 1;
3907                 bbt.ticks = 0;
3908
3909                 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3910                 ++bbt.bars;
3911                 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3912
3913                 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3914                         qn = next_b;
3915                 } else {
3916                         qn = prev_b;
3917                 }
3918         }
3919
3920         return qn;
3921 }
3922
3923 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3924  * @param pos the frame position in the tempo map.
3925  * @param bbt the distance in BBT time from pos to calculate.
3926  * @param dir the rounding direction..
3927  * @return the duration in frames between pos and bbt
3928 */
3929 framecnt_t
3930 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3931 {
3932         Glib::Threads::RWLock::ReaderLock lm (lock);
3933
3934         BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3935
3936         const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3937
3938         if (dir > 0) {
3939                 pos_bbt.bars += bbt.bars;
3940
3941                 pos_bbt.ticks += bbt.ticks;
3942                 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3943                         pos_bbt.beats += 1;
3944                         pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3945                 }
3946
3947                 pos_bbt.beats += bbt.beats;
3948                 if ((double) pos_bbt.beats > divisions) {
3949                         pos_bbt.bars += 1;
3950                         pos_bbt.beats -= divisions;
3951                 }
3952                 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3953
3954                 return pos_bbt_frame - pos;
3955
3956         } else {
3957
3958                 if (pos_bbt.bars <= bbt.bars) {
3959                         pos_bbt.bars = 1;
3960                 } else {
3961                         pos_bbt.bars -= bbt.bars;
3962                 }
3963
3964                 if (pos_bbt.ticks < bbt.ticks) {
3965                         if (pos_bbt.bars > 1) {
3966                                 if (pos_bbt.beats == 1) {
3967                                         pos_bbt.bars--;
3968                                         pos_bbt.beats = divisions;
3969                                 } else {
3970                                         pos_bbt.beats--;
3971                                 }
3972                                 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3973                         } else {
3974                                 pos_bbt.beats = 1;
3975                                 pos_bbt.ticks = 0;
3976                         }
3977                 } else {
3978                         pos_bbt.ticks -= bbt.ticks;
3979                 }
3980
3981                 if (pos_bbt.beats <= bbt.beats) {
3982                         if (pos_bbt.bars > 1) {
3983                                 pos_bbt.bars--;
3984                                 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3985                         } else {
3986                                 pos_bbt.beats = 1;
3987                         }
3988                 } else {
3989                         pos_bbt.beats -= bbt.beats;
3990                 }
3991
3992                 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3993         }
3994
3995         return 0;
3996 }
3997
3998 MusicFrame
3999 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
4000 {
4001         return round_to_type (fr, dir, Bar);
4002 }
4003
4004 MusicFrame
4005 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
4006 {
4007         return round_to_type (fr, dir, Beat);
4008 }
4009
4010 MusicFrame
4011 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
4012 {
4013         Glib::Threads::RWLock::ReaderLock lm (lock);
4014         uint32_t ticks = (uint32_t) floor (max (0.0, pulse_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat * 4.0);
4015         uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
4016         uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
4017
4018         ticks -= beats * BBT_Time::ticks_per_beat;
4019
4020         if (dir > 0) {
4021                 /* round to next (or same iff dir == RoundUpMaybe) */
4022
4023                 uint32_t mod = ticks % ticks_one_subdivisions_worth;
4024
4025                 if (mod == 0 && dir == RoundUpMaybe) {
4026                         /* right on the subdivision, which is fine, so do nothing */
4027
4028                 } else if (mod == 0) {
4029                         /* right on the subdivision, so the difference is just the subdivision ticks */
4030                         ticks += ticks_one_subdivisions_worth;
4031
4032                 } else {
4033                         /* not on subdivision, compute distance to next subdivision */
4034
4035                         ticks += ticks_one_subdivisions_worth - mod;
4036                 }
4037
4038 //NOTE:  this code intentionally limits the rounding so we don't advance to the next beat.
4039 //  For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
4040 //      And since the "prev" direction DOES move beats, I assume this code is unintended.
4041 //  But I'm keeping it around, until we determine there are no terrible consequences.
4042 //              if (ticks >= BBT_Time::ticks_per_beat) {
4043 //                      ticks -= BBT_Time::ticks_per_beat;
4044 //              }
4045
4046         } else if (dir < 0) {
4047
4048                 /* round to previous (or same iff dir == RoundDownMaybe) */
4049
4050                 uint32_t difference = ticks % ticks_one_subdivisions_worth;
4051
4052                 if (difference == 0 && dir == RoundDownAlways) {
4053                         /* right on the subdivision, but force-rounding down,
4054                            so the difference is just the subdivision ticks */
4055                         difference = ticks_one_subdivisions_worth;
4056                 }
4057
4058                 if (ticks < difference) {
4059                         ticks = BBT_Time::ticks_per_beat - ticks;
4060                 } else {
4061                         ticks -= difference;
4062                 }
4063
4064         } else {
4065                 /* round to nearest */
4066                 double rem;
4067
4068                 /* compute the distance to the previous and next subdivision */
4069
4070                 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
4071
4072                         /* closer to the next subdivision, so shift forward */
4073
4074                         ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
4075
4076                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
4077
4078                         if (ticks > BBT_Time::ticks_per_beat) {
4079                                 ++beats;
4080                                 ticks -= BBT_Time::ticks_per_beat;
4081                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
4082                         }
4083
4084                 } else if (rem > 0) {
4085
4086                         /* closer to previous subdivision, so shift backward */
4087
4088                         if (rem > ticks) {
4089                                 if (beats == 0) {
4090                                         /* can't go backwards past zero, so ... */
4091                                         return MusicFrame (0, 0);
4092                                 }
4093                                 /* step back to previous beat */
4094                                 --beats;
4095                                 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4096                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4097                         } else {
4098                                 ticks = lrint (ticks - rem);
4099                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4100                         }
4101                 } else {
4102                         /* on the subdivision, do nothing */
4103                 }
4104         }
4105
4106         MusicFrame ret (0, 0);
4107         ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4108         ret.division = sub_num;
4109
4110         return ret;
4111 }
4112
4113 MusicFrame
4114 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
4115 {
4116         Glib::Threads::RWLock::ReaderLock lm (lock);
4117         const double minute = minute_at_frame (frame);
4118         const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4119         BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
4120         MusicFrame ret (0, 0);
4121
4122         switch (type) {
4123         case Bar:
4124                 ret.division = -1;
4125
4126                 if (dir < 0) {
4127                         /* find bar previous to 'frame' */
4128                         if (bbt.bars > 0)
4129                                 --bbt.bars;
4130                         bbt.beats = 1;
4131                         bbt.ticks = 0;
4132
4133                         ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4134
4135                         return ret;
4136
4137                 } else if (dir > 0) {
4138                         /* find bar following 'frame' */
4139                         ++bbt.bars;
4140                         bbt.beats = 1;
4141                         bbt.ticks = 0;
4142
4143                         ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4144
4145                         return ret;
4146                 } else {
4147                         /* true rounding: find nearest bar */
4148                         framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4149                         bbt.beats = 1;
4150                         bbt.ticks = 0;
4151                         framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4152                         ++bbt.bars;
4153                         framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4154
4155                         if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4156                                 ret.frame = next_ft;
4157
4158                                 return ret;
4159                         } else {
4160                                 --bbt.bars;
4161                                 ret.frame = prev_ft;
4162
4163                                 return ret;
4164                         }
4165                 }
4166
4167                 break;
4168
4169         case Beat:
4170                 ret.division = 1;
4171
4172                 if (dir < 0) {
4173                         ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
4174
4175                         return ret;
4176                 } else if (dir > 0) {
4177                         ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
4178
4179                         return ret;
4180                 } else {
4181                         ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
4182
4183                         return ret;
4184                 }
4185                 break;
4186         }
4187
4188         return MusicFrame (0, 0);
4189 }
4190
4191 void
4192 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4193                     framepos_t lower, framepos_t upper, uint32_t bar_mod)
4194 {
4195         Glib::Threads::RWLock::ReaderLock lm (lock);
4196         int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
4197         framecnt_t pos = 0;
4198         /* although the map handles negative beats, bbt doesn't. */
4199         if (cnt < 0.0) {
4200                 cnt = 0.0;
4201         }
4202
4203         if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
4204                 return;
4205         }
4206         if (bar_mod == 0) {
4207                 while (pos >= 0 && pos < upper) {
4208                         pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
4209                         const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4210                         const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4211                         const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4212
4213                         points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4214                         ++cnt;
4215                 }
4216         } else {
4217                 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
4218                 bbt.beats = 1;
4219                 bbt.ticks = 0;
4220
4221                 if (bar_mod != 1) {
4222                         bbt.bars -= bbt.bars % bar_mod;
4223                         ++bbt.bars;
4224                 }
4225
4226                 while (pos >= 0 && pos < upper) {
4227                         pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4228                         const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4229                         const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4230                         points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4231                         bbt.bars += bar_mod;
4232                 }
4233         }
4234 }
4235
4236 const TempoSection&
4237 TempoMap::tempo_section_at_frame (framepos_t frame) const
4238 {
4239         Glib::Threads::RWLock::ReaderLock lm (lock);
4240
4241         return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4242 }
4243
4244 TempoSection&
4245 TempoMap::tempo_section_at_frame (framepos_t frame)
4246 {
4247         Glib::Threads::RWLock::ReaderLock lm (lock);
4248
4249         return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4250 }
4251
4252 const TempoSection&
4253 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4254 {
4255         TempoSection* prev = 0;
4256
4257         TempoSection* t;
4258
4259         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4260
4261                 if ((*i)->is_tempo()) {
4262                         t = static_cast<TempoSection*> (*i);
4263                         if (!t->active()) {
4264                                 continue;
4265                         }
4266                         if (prev && t->minute() > minute) {
4267                                 break;
4268                         }
4269
4270                         prev = t;
4271                 }
4272         }
4273
4274         if (prev == 0) {
4275                 fatal << endmsg;
4276                 abort(); /*NOTREACHED*/
4277         }
4278
4279         return *prev;
4280 }
4281 TempoSection&
4282 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4283 {
4284         TempoSection* prev = 0;
4285
4286         TempoSection* t;
4287
4288         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4289
4290                 if ((*i)->is_tempo()) {
4291                         t = static_cast<TempoSection*> (*i);
4292                         if (!t->active()) {
4293                                 continue;
4294                         }
4295                         if (prev && t->minute() > minute) {
4296                                 break;
4297                         }
4298
4299                         prev = t;
4300                 }
4301         }
4302
4303         if (prev == 0) {
4304                 fatal << endmsg;
4305                 abort(); /*NOTREACHED*/
4306         }
4307
4308         return *prev;
4309 }
4310 const TempoSection&
4311 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4312 {
4313         TempoSection* prev_t = 0;
4314         const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4315
4316         TempoSection* t;
4317
4318         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4319                 if ((*i)->is_tempo()) {
4320                         t = static_cast<TempoSection*> (*i);
4321
4322                         if (!t->active()) {
4323                                 continue;
4324                         }
4325
4326                         if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4327                                 break;
4328                         }
4329                         prev_t = t;
4330                 }
4331
4332         }
4333         return *prev_t;
4334 }
4335
4336 TempoSection*
4337 TempoMap::previous_tempo_section (TempoSection* ts) const
4338 {
4339         Glib::Threads::RWLock::ReaderLock lm (lock);
4340
4341         return previous_tempo_section_locked (_metrics, ts);
4342
4343 }
4344
4345 TempoSection*
4346 TempoMap::previous_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4347 {
4348         if (!ts) {
4349                 return 0;
4350         }
4351
4352         TempoSection* prev = 0;
4353
4354         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4355
4356                 if ((*i)->is_tempo()) {
4357                         TempoSection* t = static_cast<TempoSection*> (*i);
4358
4359                         if (!t->active()) {
4360                                 continue;
4361                         }
4362
4363                         if (prev && t == ts) {
4364
4365                                 return prev;
4366                         }
4367
4368                         prev = t;
4369                 }
4370         }
4371
4372         if (prev == 0) {
4373                 fatal << endmsg;
4374                 abort(); /*NOTREACHED*/
4375         }
4376
4377         return 0;
4378 }
4379
4380 TempoSection*
4381 TempoMap::next_tempo_section (TempoSection* ts) const
4382 {
4383         Glib::Threads::RWLock::ReaderLock lm (lock);
4384
4385         return next_tempo_section_locked (_metrics, ts);
4386 }
4387
4388 TempoSection*
4389 TempoMap::next_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4390 {
4391         if (!ts) {
4392                 return 0;
4393         }
4394
4395         TempoSection* prev = 0;
4396
4397         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4398
4399                 if ((*i)->is_tempo()) {
4400                         TempoSection* t = static_cast<TempoSection*> (*i);
4401
4402                         if (!t->active()) {
4403                                 continue;
4404                         }
4405
4406                         if (prev && prev == ts) {
4407
4408                                 return t;
4409                         }
4410
4411                         prev = t;
4412                 }
4413         }
4414
4415         if (prev == 0) {
4416                 fatal << endmsg;
4417                 abort(); /*NOTREACHED*/
4418         }
4419
4420         return 0;
4421 }
4422 /* don't use this to calculate length (the tempo is only correct for this frame).
4423    do that stuff based on the beat_at_frame and frame_at_beat api
4424 */
4425 double
4426 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4427 {
4428         Glib::Threads::RWLock::ReaderLock lm (lock);
4429
4430         const TempoSection* ts_at = 0;
4431         const TempoSection* ts_after = 0;
4432         Metrics::const_iterator i;
4433         TempoSection* t;
4434
4435         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4436
4437                 if ((*i)->is_tempo()) {
4438                         t = static_cast<TempoSection*> (*i);
4439                         if (!t->active()) {
4440                                 continue;
4441                         }
4442                         if (ts_at && (*i)->frame() > frame) {
4443                                 ts_after = t;
4444                                 break;
4445                         }
4446                         ts_at = t;
4447                 }
4448         }
4449         assert (ts_at);
4450
4451         if (ts_after) {
4452                 return  (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4453         }
4454         /* must be treated as constant tempo */
4455         return ts_at->frames_per_quarter_note (_frame_rate);
4456 }
4457
4458 const MeterSection&
4459 TempoMap::meter_section_at_frame (framepos_t frame) const
4460 {
4461         Glib::Threads::RWLock::ReaderLock lm (lock);
4462         return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4463 }
4464
4465 const MeterSection&
4466 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4467 {
4468         Metrics::const_iterator i;
4469         MeterSection* prev = 0;
4470
4471         MeterSection* m;
4472
4473         for (i = metrics.begin(); i != metrics.end(); ++i) {
4474
4475                 if (!(*i)->is_tempo()) {
4476                         m = static_cast<MeterSection*> (*i);
4477
4478                         if (prev && (*i)->minute() > minute) {
4479                                 break;
4480                         }
4481
4482                         prev = m;
4483                 }
4484         }
4485
4486         if (prev == 0) {
4487                 fatal << endmsg;
4488                 abort(); /*NOTREACHED*/
4489         }
4490
4491         return *prev;
4492 }
4493
4494 const MeterSection&
4495 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4496 {
4497         MeterSection* prev_m = 0;
4498
4499         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4500                 MeterSection* m;
4501                 if (!(*i)->is_tempo()) {
4502                         m = static_cast<MeterSection*> (*i);
4503                         if (prev_m && m->beat() > beat) {
4504                                 break;
4505                         }
4506                         prev_m = m;
4507                 }
4508
4509         }
4510         return *prev_m;
4511 }
4512
4513 const MeterSection&
4514 TempoMap::meter_section_at_beat (double beat) const
4515 {
4516         Glib::Threads::RWLock::ReaderLock lm (lock);
4517         return meter_section_at_beat_locked (_metrics, beat);
4518 }
4519
4520 const Meter&
4521 TempoMap::meter_at_frame (framepos_t frame) const
4522 {
4523         TempoMetric m (metric_at (frame));
4524         return m.meter();
4525 }
4526
4527 void
4528 TempoMap::fix_legacy_session ()
4529 {
4530         MeterSection* prev_m = 0;
4531         TempoSection* prev_t = 0;
4532         bool have_initial_t = false;
4533
4534         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4535                 MeterSection* m;
4536                 TempoSection* t;
4537
4538                 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4539                         if (m->initial()) {
4540                                 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4541                                 m->set_beat (bbt);
4542                                 m->set_pulse (0.0);
4543                                 m->set_minute (0.0);
4544                                 m->set_position_lock_style (AudioTime);
4545                                 prev_m = m;
4546                                 continue;
4547                         }
4548                         if (prev_m) {
4549                                 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4550                                                                           + (m->bbt().beats - 1)
4551                                                                           + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4552                                                                           , m->bbt());
4553                                 m->set_beat (start);
4554                                 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4555                                         + (m->bbt().beats - 1)
4556                                         + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4557                                 m->set_pulse (start_beat / prev_m->note_divisor());
4558                         }
4559                         prev_m = m;
4560                 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4561
4562                         if (!t->active()) {
4563                                 continue;
4564                         }
4565
4566                         if (t->initial()) {
4567                                 t->set_pulse (0.0);
4568                                 t->set_minute (0.0);
4569                                 t->set_position_lock_style (AudioTime);
4570                                 prev_t = t;
4571                                 have_initial_t = true;
4572                                 continue;
4573                         }
4574
4575                         if (prev_t) {
4576                                 /* some 4.x sessions have no initial (non-movable) tempo. */
4577                                 if (!have_initial_t) {
4578                                         prev_t->set_pulse (0.0);
4579                                         prev_t->set_minute (0.0);
4580                                         prev_t->set_position_lock_style (AudioTime);
4581                                         prev_t->set_initial (true);
4582                                         prev_t->set_locked_to_meter (true);
4583                                         have_initial_t = true;
4584                                 }
4585
4586                                 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4587                                         + (t->legacy_bbt().beats - 1)
4588                                         + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4589                                 if (prev_m) {
4590                                         t->set_pulse (beat / prev_m->note_divisor());
4591                                 } else {
4592                                         /* really shouldn't happen but.. */
4593                                         t->set_pulse (beat / 4.0);
4594                                 }
4595                         }
4596                         prev_t = t;
4597                 }
4598         }
4599 }
4600 void
4601 TempoMap::fix_legacy_end_session ()
4602 {
4603         TempoSection* prev_t = 0;
4604
4605         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4606                 TempoSection* t;
4607
4608                 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4609
4610                         if (!t->active()) {
4611                                 continue;
4612                         }
4613
4614                         if (prev_t) {
4615                                 if (prev_t->type() != TempoSection::Constant) {
4616                                         prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4617                                 }
4618                         }
4619
4620                         prev_t = t;
4621                 }
4622         }
4623 }
4624
4625 XMLNode&
4626 TempoMap::get_state ()
4627 {
4628         Metrics::const_iterator i;
4629         XMLNode *root = new XMLNode ("TempoMap");
4630
4631         {
4632                 Glib::Threads::RWLock::ReaderLock lm (lock);
4633                 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4634                         root->add_child_nocopy ((*i)->get_state());
4635                 }
4636         }
4637
4638         return *root;
4639 }
4640
4641 int
4642 TempoMap::set_state (const XMLNode& node, int /*version*/)
4643 {
4644         {
4645                 Glib::Threads::RWLock::WriterLock lm (lock);
4646
4647                 XMLNodeList nlist;
4648                 XMLNodeConstIterator niter;
4649                 Metrics old_metrics (_metrics);
4650                 _metrics.clear();
4651
4652                 nlist = node.children();
4653
4654                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4655                         XMLNode* child = *niter;
4656
4657                         if (child->name() == TempoSection::xml_state_node_name) {
4658
4659                                 try {
4660                                         TempoSection* ts = new TempoSection (*child, _frame_rate);
4661                                         _metrics.push_back (ts);
4662                                 }
4663
4664                                 catch (failed_constructor& err){
4665                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4666                                         _metrics = old_metrics;
4667                                         old_metrics.clear();
4668                                         break;
4669                                 }
4670
4671                         } else if (child->name() == MeterSection::xml_state_node_name) {
4672
4673                                 try {
4674                                         MeterSection* ms = new MeterSection (*child, _frame_rate);
4675                                         _metrics.push_back (ms);
4676                                 }
4677
4678                                 catch (failed_constructor& err) {
4679                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4680                                         _metrics = old_metrics;
4681                                         old_metrics.clear();
4682                                         break;
4683                                 }
4684                         }
4685                 }
4686
4687                 if (niter == nlist.end()) {
4688                         MetricSectionSorter cmp;
4689                         _metrics.sort (cmp);
4690                 }
4691
4692                 /* check for legacy sessions where bbt was the base musical unit for tempo */
4693                 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4694                         TempoSection* t;
4695                         if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4696                                 if (t->legacy_bbt().bars != 0) {
4697                                         fix_legacy_session();
4698                                         break;
4699                                 }
4700
4701                                 if (t->legacy_end()) {
4702                                         fix_legacy_end_session();
4703                                         break;
4704                                 }
4705
4706                                 break;
4707                         }
4708                 }
4709
4710                 /* check for multiple tempo/meters at the same location, which
4711                    ardour2 somehow allowed.
4712                 */
4713
4714                 Metrics::iterator prev = _metrics.end();
4715                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4716                         if (prev != _metrics.end()) {
4717                                 MeterSection* ms;
4718                                 MeterSection* prev_m;
4719                                 TempoSection* ts;
4720                                 TempoSection* prev_t;
4721                                 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4722                                         if (prev_m->pulse() == ms->pulse()) {
4723                                                 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4724                                                 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4725                                                 return -1;
4726                                         }
4727                                 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4728                                         if (prev_t->pulse() == ts->pulse()) {
4729                                                 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4730                                                 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4731                                                 return -1;
4732                                         }
4733                                 }
4734                         }
4735                         prev = i;
4736                 }
4737
4738                 recompute_map (_metrics);
4739
4740                 Metrics::const_iterator d = old_metrics.begin();
4741                 while (d != old_metrics.end()) {
4742                         delete (*d);
4743                         ++d;
4744                 }
4745                 old_metrics.clear ();
4746         }
4747
4748         PropertyChanged (PropertyChange ());
4749
4750         return 0;
4751 }
4752
4753 void
4754 TempoMap::dump (std::ostream& o) const
4755 {
4756         Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4757         const MeterSection* m;
4758         const TempoSection* t;
4759         const TempoSection* prev_t = 0;
4760
4761         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4762
4763                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4764                         o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4765                           << " type= " << enum_2_string (t->type()) << ") "  << " at pulse= " << t->pulse()
4766                           << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4767                           << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4768                         if (prev_t) {
4769                                 o <<  "  current start  : " << t->note_types_per_minute()
4770                                   <<  "  current end  : " << t->end_note_types_per_minute()
4771                                   << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4772                                 o << "  previous     : " << prev_t->note_types_per_minute()
4773                                   << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4774                                 o << "  calculated   : " << prev_t->tempo_at_pulse (t->pulse())
4775                                   << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4776                                   << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4777                                   << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4778                         }
4779                         prev_t = t;
4780                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4781                         o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4782                           << " frame= " << m->frame() << " pulse: " << m->pulse() <<  " beat : " << m->beat()
4783                           << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4784                 }
4785         }
4786         o << "------" << std::endl;
4787 }
4788
4789 int
4790 TempoMap::n_tempos() const
4791 {
4792         Glib::Threads::RWLock::ReaderLock lm (lock);
4793         int cnt = 0;
4794
4795         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4796                 if ((*i)->is_tempo()) {
4797                         cnt++;
4798                 }
4799         }
4800
4801         return cnt;
4802 }
4803
4804 int
4805 TempoMap::n_meters() const
4806 {
4807         Glib::Threads::RWLock::ReaderLock lm (lock);
4808         int cnt = 0;
4809
4810         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4811                 if (!(*i)->is_tempo()) {
4812                         cnt++;
4813                 }
4814         }
4815
4816         return cnt;
4817 }
4818
4819 void
4820 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4821 {
4822         for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4823                 if ((*i)->frame() >= where && !(*i)->initial ()) {
4824                         MeterSection* ms;
4825                         TempoSection* ts;
4826
4827                         if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4828                                 gui_set_meter_position (ms, (*i)->frame() + amount);
4829                         }
4830
4831                         if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4832                                 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4833                         }
4834                 }
4835         }
4836
4837         PropertyChanged (PropertyChange ());
4838 }
4839
4840 bool
4841 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4842 {
4843         bool moved = false;
4844
4845         std::list<MetricSection*> metric_kill_list;
4846
4847         TempoSection* last_tempo = NULL;
4848         MeterSection* last_meter = NULL;
4849         bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4850         bool meter_after = false; // is there a meter marker likewise?
4851         {
4852                 Glib::Threads::RWLock::WriterLock lm (lock);
4853                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4854                         if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4855                                 metric_kill_list.push_back(*i);
4856                                 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4857                                 if (lt)
4858                                         last_tempo = lt;
4859                                 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4860                                 if (lm)
4861                                         last_meter = lm;
4862                         }
4863                         else if ((*i)->frame() >= where) {
4864                                 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4865                                 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4866                                 if ((*i)->frame() == where) {
4867                                         // marker was immediately after end of range
4868                                         tempo_after = dynamic_cast<TempoSection*> (*i);
4869                                         meter_after = dynamic_cast<MeterSection*> (*i);
4870                                 }
4871                                 moved = true;
4872                         }
4873                 }
4874
4875                 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4876                 if (last_tempo && !tempo_after) {
4877                         metric_kill_list.remove(last_tempo);
4878                         last_tempo->set_minute (minute_at_frame (where));
4879                         moved = true;
4880                 }
4881                 if (last_meter && !meter_after) {
4882                         metric_kill_list.remove(last_meter);
4883                         last_meter->set_minute (minute_at_frame (where));
4884                         moved = true;
4885                 }
4886
4887                 //remove all the remaining metrics
4888                 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4889                         _metrics.remove(*i);
4890                         moved = true;
4891                 }
4892
4893                 if (moved) {
4894                         recompute_map (_metrics);
4895                 }
4896         }
4897         PropertyChanged (PropertyChange ());
4898         return moved;
4899 }
4900
4901 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4902  *  pos can be -ve, if required.
4903  */
4904 framepos_t
4905 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4906 {
4907         Glib::Threads::RWLock::ReaderLock lm (lock);
4908         const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4909
4910         return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4911 }
4912
4913 framepos_t
4914 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4915 {
4916         Glib::Threads::RWLock::ReaderLock lm (lock);
4917
4918         BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4919         pos_bbt.ticks += op.ticks;
4920         if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4921                 ++pos_bbt.beats;
4922                 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4923         }
4924         pos_bbt.beats += op.beats;
4925         /* the meter in effect will start on the bar */
4926         double divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4927         while (pos_bbt.beats >= divisions_per_bar + 1) {
4928                 ++pos_bbt.bars;
4929                 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4930                 pos_bbt.beats -= divisions_per_bar;
4931         }
4932         pos_bbt.bars += op.bars;
4933
4934         return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4935 }
4936
4937 /** Count the number of beats that are equivalent to distance when going forward,
4938     starting at pos.
4939 */
4940 Evoral::Beats
4941 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4942 {
4943         Glib::Threads::RWLock::ReaderLock lm (lock);
4944
4945         return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4946 }
4947
4948 struct bbtcmp {
4949     bool operator() (const BBT_Time& a, const BBT_Time& b) {
4950             return a < b;
4951     }
4952 };
4953
4954 std::ostream&
4955 operator<< (std::ostream& o, const Meter& m) {
4956         return o << m.divisions_per_bar() << '/' << m.note_divisor();
4957 }
4958
4959 std::ostream&
4960 operator<< (std::ostream& o, const Tempo& t) {
4961         return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4962 }
4963
4964 std::ostream&
4965 operator<< (std::ostream& o, const MetricSection& section) {
4966
4967         o << "MetricSection @ " << section.frame() << ' ';
4968
4969         const TempoSection* ts;
4970         const MeterSection* ms;
4971
4972         if ((ts = dynamic_cast<const TempoSection*> (&section)) != 0) {
4973                 o << *((const Tempo*) ts);
4974         } else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
4975                 o << *((const Meter*) ms);
4976         }
4977
4978         return o;
4979 }