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