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*> (&section)) != 0) {
4964 		o << *((const Tempo*) ts);
4965 	} else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
4966 		o << *((const Meter*) ms);
4967 	}
4968 
4969 	return o;
4970 }
4971