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