2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
31 #include "temporal/beats.h"
33 #include "ardour/debug.h"
34 #include "ardour/lmath.h"
35 #include "ardour/tempo.h"
36 #include "ardour/types_convert.h"
42 using namespace ARDOUR;
45 using Timecode::BBT_Time;
47 /* _default tempo is 4/4 qtr=120 */
49 Meter TempoMap::_default_meter (4.0, 4.0);
50 Tempo TempoMap::_default_tempo (120.0, 4.0, 120.0);
53 MetricSection::sample_at_minute (const double& time) const
55 return (samplepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
59 MetricSection::minute_at_sample (const samplepos_t sample) const
61 return (sample / (double) _sample_rate) / 60.0;
64 /***********************************************************************/
67 ARDOUR::bbt_time_to_string (const BBT_Time& bbt, std::string& str)
70 int retval = snprintf (buf, sizeof(buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, bbt.bars, bbt.beats,
73 if (retval <= 0 || retval >= (int)sizeof(buf)) {
82 ARDOUR::string_to_bbt_time (const std::string& str, BBT_Time& bbt)
84 if (sscanf (str.c_str (), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, &bbt.bars, &bbt.beats,
92 /***********************************************************************/
95 Meter::samples_per_grid (const Tempo& tempo, samplecnt_t sr) const
97 /* This is tempo- and meter-sensitive. The number it returns
98 is based on the interval between any two lines in the
99 grid that is constructed from tempo and meter sections.
101 The return value IS NOT interpretable in terms of "beats".
104 return (60.0 * sr) / (tempo.note_types_per_minute() * (_note_type / tempo.note_type()));
108 Meter::samples_per_bar (const Tempo& tempo, samplecnt_t sr) const
110 return samples_per_grid (tempo, sr) * _divisions_per_bar;
113 /***********************************************************************/
116 MetricSection::add_state_to_node(XMLNode& node) const
118 node.set_property ("pulse", _pulse);
119 node.set_property ("frame", sample());
120 node.set_property ("movable", !_initial);
121 node.set_property ("lock-style", _position_lock_style);
125 MetricSection::set_state (const XMLNode& node, int /*version*/)
127 node.get_property ("pulse", _pulse);
130 if (node.get_property ("frame", sample)) {
131 set_minute (minute_at_sample (sample));
135 if (!node.get_property ("movable", tmp)) {
136 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
137 throw failed_constructor();
141 if (!node.get_property ("lock-style", _position_lock_style)) {
143 _position_lock_style = MusicTime;
145 _position_lock_style = AudioTime;
151 /***********************************************************************/
153 const string TempoSection::xml_state_node_name = "Tempo";
155 TempoSection::TempoSection (const XMLNode& node, samplecnt_t sample_rate)
156 : MetricSection (0.0, 0, MusicTime, true, sample_rate)
157 , Tempo (TempoMap::default_tempo())
160 , _locked_to_meter (false)
164 std::string start_bbt;
165 if (node.get_property ("start", start_bbt)) {
166 if (string_to_bbt_time (start_bbt, bbt)) {
167 /* legacy session - start used to be in bbt*/
170 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
174 // Don't worry about return value, exception will be thrown on error
175 MetricSection::set_state (node, Stateful::loading_state_version);
177 if (node.get_property ("beats-per-minute", _note_types_per_minute)) {
178 if (_note_types_per_minute < 0.0) {
179 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
180 throw failed_constructor();
184 if (node.get_property ("note-type", _note_type)) {
185 if (_note_type < 1.0) {
186 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
187 throw failed_constructor();
190 /* older session, make note type be quarter by default */
194 if (!node.get_property ("clamped", _clamped)) {
198 if (node.get_property ("end-beats-per-minute", _end_note_types_per_minute)) {
199 if (_end_note_types_per_minute < 0.0) {
200 info << _("TempoSection XML node has an illegal \"end-beats-per-minute\" value") << endmsg;
201 throw failed_constructor();
205 TempoSection::Type old_type;
206 if (node.get_property ("tempo-type", old_type)) {
207 /* sessions with a tempo-type node contain no end-beats-per-minute.
208 if the legacy node indicates a constant tempo, simply fill this in with the
209 start tempo. otherwise we need the next neighbour to know what it will be.
212 if (old_type == TempoSection::Constant) {
213 _end_note_types_per_minute = _note_types_per_minute;
215 _end_note_types_per_minute = -1.0;
219 if (!node.get_property ("active", _active)) {
220 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
224 if (!node.get_property ("locked-to-meter", _locked_to_meter)) {
226 set_locked_to_meter (true);
228 set_locked_to_meter (false);
232 /* 5.5 marked initial tempo as not locked to meter. this should always be true anyway */
234 set_locked_to_meter (true);
239 TempoSection::get_state() const
241 XMLNode *root = new XMLNode (xml_state_node_name);
243 MetricSection::add_state_to_node (*root);
245 root->set_property ("beats-per-minute", _note_types_per_minute);
246 root->set_property ("note-type", _note_type);
247 root->set_property ("clamped", _clamped);
248 root->set_property ("end-beats-per-minute", _end_note_types_per_minute);
249 root->set_property ("active", _active);
250 root->set_property ("locked-to-meter", _locked_to_meter);
255 /** returns the Tempo at the session-relative minute.
258 TempoSection::tempo_at_minute (const double& m) const
260 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
262 return Tempo (note_types_per_minute(), note_type());
265 return Tempo (_tempo_at_time (m - minute()), _note_type, _end_note_types_per_minute);
268 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
269 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
270 * @param p the pulse used to calculate the returned minute for constant tempi
271 * @return the minute at the supplied tempo
273 * note that the note_type is currently ignored in this function. see below.
277 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
278 * there should be no ramp between the two even if we are ramped.
279 * in other words a ramp should only place a curve on note_types_per_minute.
280 * we should be able to use Tempo note type here, but the above
281 * complicates things a bit.
284 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
286 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
288 return ((p - pulse()) / pulses_per_minute()) + minute();
291 return _time_at_tempo (ntpm) + minute();
294 /** returns the Tempo at the supplied whole-note pulse.
297 TempoSection::tempo_at_pulse (const double& p) const
299 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
302 return Tempo (note_types_per_minute(), note_type());
305 return Tempo (_tempo_at_pulse (p - pulse()), _note_type, _end_note_types_per_minute);
308 /** returns the whole-note pulse where a tempo in note types per minute occurs.
309 * constant tempi require minute m.
310 * @param ntpm the note types per minute value used to calculate the returned pulse
311 * @param m the minute used to calculate the returned pulse if the tempo is constant
312 * @return the whole-note pulse at the supplied tempo
314 * note that note_type is currently ignored in this function. see minute_at_tempo().
316 * for constant tempi, this is anaologous to pulse_at_minute().
319 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
321 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
323 return ((m - minute()) * pulses_per_minute()) + pulse();
326 return _pulse_at_tempo (ntpm) + pulse();
329 /** returns the whole-note pulse at the supplied session-relative minute.
332 TempoSection::pulse_at_minute (const double& m) const
334 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
336 return ((m - minute()) * pulses_per_minute()) + pulse();
339 return _pulse_at_time (m - minute()) + pulse();
342 /** returns the session-relative minute at the supplied whole-note pulse.
345 TempoSection::minute_at_pulse (const double& p) const
347 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
349 return ((p - pulse()) / pulses_per_minute()) + minute();
352 return _time_at_pulse (p - pulse()) + minute();
355 /** returns thw whole-note pulse at session sample position f.
356 * @param f the sample position.
357 * @return the position in whole-note pulses corresponding to f
359 * for use with musical units whose granularity is coarser than samples (e.g. ticks)
362 TempoSection::pulse_at_sample (const samplepos_t f) const
364 const bool constant = type() == Constant || _c == 0.0 || (initial() && f < sample());
366 return (minute_at_sample (f - sample()) * pulses_per_minute()) + pulse();
369 return _pulse_at_time (minute_at_sample (f - sample())) + pulse();
373 TempoSection::sample_at_pulse (const double& p) const
375 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
377 return sample_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
380 return sample_at_minute (_time_at_pulse (p - pulse()) + minute());
388 Tt----|-----------------*|
389 Ta----|--------------|* |
395 _______________|___|____
396 time a t (next tempo)
399 Duration in beats at time a is the integral of some Tempo function.
400 In our case, the Tempo function (Tempo at time t) is
403 with function constant
408 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
409 b(t) = T0(e^(ct) - 1) / c
411 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:
412 t(b) = log((c.b / T0) + 1) / c
414 The time t at which Tempo T occurs is a as above:
415 t(T) = log(T / T0) / c
417 The beat at which a Tempo T occurs is:
420 The Tempo at which beat b occurs is:
423 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
424 Our problem is that we usually don't know t.
425 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.
426 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
427 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
429 By substituting our expanded t as a in the c function above, our problem is reduced to:
430 c = T0 (e^(log (Ta / T0)) - 1) / b
432 Of course the word 'beat' has been left loosely defined above.
433 In music, a beat is defined by the musical pulse (which comes from the tempo)
434 and the meter in use at a particular time (how many pulse divisions there are in one bar).
435 It would be more accurate to substitute the work 'pulse' for 'beat' above.
439 We can now store c for future time calculations.
440 If the following tempo section (the one that defines c in conjunction with this one)
441 is changed or moved, c is no longer valid.
443 The public methods are session-relative.
445 Most of this stuff is taken from this paper:
448 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
451 Zurich University of Arts
452 Institute for Computer Music and Sound Technology
454 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
458 /** compute this ramp's function constant from some tempo-pulse point
459 * @param end_npm end tempo (in note types per minute)
460 * @param end_pulse duration (pulses into global start) of some other position.
461 * @return the calculated function constant
464 TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const
466 if (note_types_per_minute() == end_npm || type() == Constant) {
470 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
471 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
474 /** compute the function constant from some tempo-time point.
475 * @param end_npm tempo (note types/min.)
476 * @param end_minute distance (in minutes) from session origin
477 * @return the calculated function constant
480 TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const
482 if (note_types_per_minute() == end_npm || type() == Constant) {
486 return c_func (end_npm, end_minute - minute());
489 /* position function */
491 TempoSection::a_func (double end_npm, double c) const
493 return log (end_npm / note_types_per_minute()) / c;
496 /*function constant*/
498 TempoSection::c_func (double end_npm, double end_time) const
500 return log (end_npm / note_types_per_minute()) / end_time;
503 /* tempo in note types per minute at time in minutes */
505 TempoSection::_tempo_at_time (const double& time) const
507 return exp (_c * time) * note_types_per_minute();
510 /* time in minutes at tempo in note types per minute */
512 TempoSection::_time_at_tempo (const double& npm) const
514 return log (npm / note_types_per_minute()) / _c;
517 /* pulse at tempo in note types per minute */
519 TempoSection::_pulse_at_tempo (const double& npm) const
521 return ((npm - note_types_per_minute()) / _c) / _note_type;
524 /* tempo in note types per minute at pulse */
526 TempoSection::_tempo_at_pulse (const double& pulse) const
528 return (pulse * _note_type * _c) + note_types_per_minute();
531 /* pulse at time in minutes */
533 TempoSection::_pulse_at_time (const double& time) const
535 return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
538 /* time in minutes at pulse */
540 TempoSection::_time_at_pulse (const double& pulse) const
542 return log1p ((_c * pulse * _note_type) / note_types_per_minute()) / _c;
545 /***********************************************************************/
547 const string MeterSection::xml_state_node_name = "Meter";
549 MeterSection::MeterSection (const XMLNode& node, const samplecnt_t sample_rate)
550 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
552 pair<double, BBT_Time> start;
556 if (node.get_property ("start", bbt_str)) {
557 if (string_to_bbt_time (bbt_str, start.second)) {
558 /* legacy session - start used to be in bbt*/
559 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
562 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
566 MetricSection::set_state (node, Stateful::loading_state_version);
568 node.get_property ("beat", start.first);
570 if (node.get_property ("bbt", bbt_str)) {
571 if (!string_to_bbt_time (bbt_str, start.second)) {
572 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
573 throw failed_constructor();
576 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
581 /* beats-per-bar is old; divisions-per-bar is new */
583 if (!node.get_property ("divisions-per-bar", _divisions_per_bar)) {
584 if (!node.get_property ("beats-per-bar", _divisions_per_bar)) {
585 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
586 throw failed_constructor();
590 if (_divisions_per_bar < 0.0) {
591 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
592 throw failed_constructor();
595 if (!node.get_property ("note-type", _note_type)) {
596 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
597 throw failed_constructor();
600 if (_note_type < 0.0) {
601 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
602 throw failed_constructor();
607 MeterSection::get_state() const
609 XMLNode *root = new XMLNode (xml_state_node_name);
611 MetricSection::add_state_to_node (*root);
614 bbt_time_to_string (_bbt, bbt_str);
615 root->set_property ("bbt", bbt_str);
616 root->set_property ("beat", beat());
617 root->set_property ("note-type", _note_type);
618 root->set_property ("divisions-per-bar", _divisions_per_bar);
623 /***********************************************************************/
627 Tempo determines the rate of musical pulse determined by its components
628 note types per minute - the rate per minute of the whole note divisor _note_type
629 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
630 Meter divides the musical pulse into measures and beats according to its components
634 TempoSection - translates between time, musical pulse and tempo.
635 has a musical location in whole notes (pulses).
636 has a time location in minutes.
637 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
638 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
640 MeterSection - translates between BBT, meter-based beat and musical pulse.
641 has a musical location in whole notes (pulses)
642 has a musical location in meter-based beats
643 has a musical location in BBT time
644 has a time location expressed in minutes.
646 TempoSection and MeterSection may be locked to either audio or music (position lock style).
647 The lock style determines the location type to be kept as a reference when location is recalculated.
649 The first tempo and meter are special. they must move together, and are locked to audio.
650 Audio locked tempi which lie before the first meter are made inactive.
652 Recomputing the map is the process where the 'missing' location types are calculated.
653 We construct the tempo map by first using the locked location type of each section
654 to determine non-locked location types (pulse or minute position).
655 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
657 Having done this, we can now traverse the Metrics list by pulse or minute
658 to query its relevant meter/tempo.
660 It is important to keep the _metrics in an order that makes sense.
661 Because ramped MusicTime and AudioTime tempos can interact with each other,
662 reordering is frequent. Care must be taken to keep _metrics in a solved state.
663 Solved means ordered by sample or pulse with sample-accurate precision (see check_solved()).
667 Music and audio-locked objects may seem interchangeable on the surface, but when translating
668 between audio samples and beat, remember that a sample is only a quantised approximation
669 of the actual time (in minutes) of a beat.
670 Thus if a gui user points to the sample occupying the start of a music-locked object on 1|3|0, it does not
671 mean that this sample is the actual location in time of 1|3|0.
673 You cannot use a sample measurement to determine beat distance except under special circumstances
674 (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).
676 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
677 sample space the user is operating at to be translated correctly to the object.
679 The current approach is to interpret the supplied sample using the grid division the user has currently selected.
680 If the user has no musical grid set, they are actually operating in sample space (even SMPTE samples are rounded to audio sample), so
681 the supplied audio sample is interpreted as the desired musical location (beat_at_sample()).
683 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
685 When sample_at_beat() is called, the position calculation is performed in pulses and minutes.
686 The result is rounded to audio samples.
687 When beat_at_sample() is called, the sample is converted to minutes, with no rounding performed on the result.
690 sample_at_beat (beat_at_sample (sample)) == sample
692 beat_at_sample (sample_at_beat (beat)) != beat due to the time quantization of sample_at_beat().
694 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
695 samples_between_quarter_notes () eliminats this effect when determining time duration
696 from Beats distance, or instead work in quarter-notes and/or beats and convert to samples last.
698 The above pointless example could instead do:
699 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
701 The Shaggs - Things I Wonder
702 https://www.youtube.com/watch?v=9wQK6zMJOoQ
705 struct MetricSectionSorter {
706 bool operator() (const MetricSection* a, const MetricSection* b) {
707 return a->pulse() < b->pulse();
711 struct MetricSectionFrameSorter {
712 bool operator() (const MetricSection* a, const MetricSection* b) {
713 return a->sample() < b->sample();
717 TempoMap::TempoMap (samplecnt_t fr)
720 BBT_Time start (1, 1, 0);
722 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo, AudioTime, fr);
723 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
725 t->set_initial (true);
726 t->set_locked_to_meter (true);
728 m->set_initial (true);
730 /* note: sample time is correct (zero) for both of these */
732 _metrics.push_back (t);
733 _metrics.push_back (m);
738 TempoMap::operator= (TempoMap const & other)
740 if (&other != this) {
741 Glib::Threads::RWLock::ReaderLock lr (other.lock);
742 Glib::Threads::RWLock::WriterLock lm (lock);
743 _sample_rate = other._sample_rate;
745 Metrics::const_iterator d = _metrics.begin();
746 while (d != _metrics.end()) {
752 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
753 TempoSection const * const ts = dynamic_cast<TempoSection const * const> (*m);
754 MeterSection const * const ms = dynamic_cast<MeterSection const * const> (*m);
757 TempoSection* new_section = new TempoSection (*ts);
758 _metrics.push_back (new_section);
760 MeterSection* new_section = new MeterSection (*ms);
761 _metrics.push_back (new_section);
766 PropertyChanged (PropertyChange());
771 TempoMap::~TempoMap ()
773 Metrics::const_iterator d = _metrics.begin();
774 while (d != _metrics.end()) {
782 TempoMap::sample_at_minute (const double time) const
784 return (samplepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
788 TempoMap::minute_at_sample (const samplepos_t sample) const
790 return (sample / (double) _sample_rate) / 60.0;
794 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
796 bool removed = false;
799 Glib::Threads::RWLock::WriterLock lm (lock);
800 if ((removed = remove_tempo_locked (tempo))) {
801 if (complete_operation) {
802 recompute_map (_metrics);
807 if (removed && complete_operation) {
808 PropertyChanged (PropertyChange ());
813 TempoMap::remove_tempo_locked (const TempoSection& tempo)
817 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
818 if (dynamic_cast<TempoSection*> (*i) != 0) {
819 if (tempo.sample() == (*i)->sample()) {
820 if (!(*i)->initial()) {
833 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
835 bool removed = false;
838 Glib::Threads::RWLock::WriterLock lm (lock);
839 if ((removed = remove_meter_locked (tempo))) {
840 if (complete_operation) {
841 recompute_map (_metrics);
846 if (removed && complete_operation) {
847 PropertyChanged (PropertyChange ());
852 TempoMap::remove_meter_locked (const MeterSection& meter)
855 if (meter.position_lock_style() == AudioTime) {
856 /* remove meter-locked tempo */
857 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
859 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
860 if (t->locked_to_meter() && meter.sample() == (*i)->sample()) {
869 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
870 if (dynamic_cast<MeterSection*> (*i) != 0) {
871 if (meter.sample() == (*i)->sample()) {
872 if (!(*i)->initial()) {
885 TempoMap::do_insert (MetricSection* section)
887 bool need_add = true;
888 /* we only allow new meters to be inserted on beat 1 of an existing
892 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
894 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
896 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
897 corrected.second.beats = 1;
898 corrected.second.ticks = 0;
899 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
900 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
901 m->bbt(), corrected.second) << endmsg;
902 //m->set_pulse (corrected);
906 /* Look for any existing MetricSection that is of the same type and
907 in the same bar as the new one, and remove it before adding
908 the new one. Note that this means that if we find a matching,
909 existing section, we can break out of the loop since we're
910 guaranteed that there is only one such match.
913 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
915 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
916 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
917 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
918 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
920 if (tempo && insert_tempo) {
923 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
924 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->sample() == insert_tempo->sample())) {
926 if (tempo->initial()) {
928 /* can't (re)move this section, so overwrite
929 * its data content (but not its properties as
933 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
934 (*i)->set_position_lock_style (AudioTime);
943 } else if (meter && insert_meter) {
947 bool const ipm = insert_meter->position_lock_style() == MusicTime;
949 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->sample() == insert_meter->sample())) {
951 if (meter->initial()) {
953 /* can't (re)move this section, so overwrite
954 * its data content (but not its properties as
958 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
959 (*i)->set_position_lock_style (AudioTime);
969 /* non-matching types, so we don't care */
973 /* Add the given MetricSection, if we didn't just reset an existing
978 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
979 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
983 TempoSection* prev_t = 0;
985 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
986 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
987 bool const ipm = insert_meter->position_lock_style() == MusicTime;
990 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->sample() > insert_meter->sample())) {
994 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->sample() == insert_meter->sample())) {
998 prev_t = dynamic_cast<TempoSection*> (*i);
1001 } else if (insert_tempo) {
1002 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1003 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1006 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1007 const bool lm = insert_tempo->locked_to_meter();
1008 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->sample() > insert_tempo->sample())
1009 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1016 _metrics.insert (i, section);
1020 /* user supplies the exact pulse if pls == MusicTime */
1022 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const samplepos_t sample, PositionLockStyle pls)
1024 if (tempo.note_types_per_minute() <= 0.0) {
1025 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1029 TempoSection* ts = 0;
1031 Glib::Threads::RWLock::WriterLock lm (lock);
1032 /* here we default to not clamped for a new tempo section. preference? */
1033 ts = add_tempo_locked (tempo, pulse, minute_at_sample (sample), pls, true, false, false);
1035 recompute_map (_metrics);
1038 PropertyChanged (PropertyChange ());
1044 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const samplepos_t sample, PositionLockStyle pls)
1046 if (tempo.note_types_per_minute() <= 0.0) {
1047 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1051 bool const locked_to_meter = ts.locked_to_meter();
1052 bool const ts_clamped = ts.clamped();
1053 TempoSection* new_ts = 0;
1056 Glib::Threads::RWLock::WriterLock lm (lock);
1057 TempoSection& first (first_tempo());
1058 if (!ts.initial()) {
1059 if (locked_to_meter) {
1061 /* cannot move a meter-locked tempo section */
1062 *static_cast<Tempo*>(&ts) = tempo;
1063 recompute_map (_metrics);
1066 remove_tempo_locked (ts);
1067 new_ts = add_tempo_locked (tempo, pulse, minute_at_sample (sample), pls, true, locked_to_meter, ts_clamped);
1068 /* enforce clampedness of next tempo section */
1069 TempoSection* next_t = next_tempo_section_locked (_metrics, new_ts);
1070 if (next_t && next_t->clamped()) {
1071 next_t->set_note_types_per_minute (new_ts->end_note_types_per_minute());
1076 first.set_pulse (0.0);
1077 first.set_minute (minute_at_sample (sample));
1078 first.set_position_lock_style (AudioTime);
1079 first.set_locked_to_meter (true);
1080 first.set_clamped (ts_clamped);
1082 /* cannot move the first tempo section */
1083 *static_cast<Tempo*>(&first) = tempo;
1086 recompute_map (_metrics);
1089 PropertyChanged (PropertyChange ());
1093 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1094 , PositionLockStyle pls, bool recompute, bool locked_to_meter, bool clamped)
1096 TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _sample_rate);
1097 t->set_locked_to_meter (locked_to_meter);
1098 t->set_clamped (clamped);
1102 TempoSection* prev_tempo = 0;
1103 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1104 TempoSection* const this_t = dynamic_cast<TempoSection*>(*i);
1107 if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1108 prev_tempo->set_end_note_types_per_minute (t->note_types_per_minute());
1112 prev_tempo = this_t;
1117 if (pls == AudioTime) {
1118 solve_map_minute (_metrics, t, t->minute());
1120 solve_map_pulse (_metrics, t, t->pulse());
1122 recompute_meters (_metrics);
1129 TempoMap::add_meter (const Meter& meter, const Timecode::BBT_Time& where, samplepos_t sample, PositionLockStyle pls)
1131 MeterSection* m = 0;
1133 Glib::Threads::RWLock::WriterLock lm (lock);
1134 m = add_meter_locked (meter, where, sample, pls, true);
1139 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1144 PropertyChanged (PropertyChange ());
1149 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, samplepos_t sample, PositionLockStyle pls)
1152 Glib::Threads::RWLock::WriterLock lm (lock);
1154 if (!ms.initial()) {
1155 remove_meter_locked (ms);
1156 add_meter_locked (meter, where, sample, pls, true);
1158 MeterSection& first (first_meter());
1159 TempoSection& first_t (first_tempo());
1160 /* cannot move the first meter section */
1161 *static_cast<Meter*>(&first) = meter;
1162 first.set_position_lock_style (AudioTime);
1163 first.set_pulse (0.0);
1164 first.set_minute (minute_at_sample (sample));
1165 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1166 first.set_beat (beat);
1167 first_t.set_minute (first.minute());
1168 first_t.set_locked_to_meter (true);
1169 first_t.set_pulse (0.0);
1170 first_t.set_position_lock_style (AudioTime);
1171 recompute_map (_metrics);
1175 PropertyChanged (PropertyChange ());
1179 TempoMap::add_meter_locked (const Meter& meter, const BBT_Time& bbt, samplepos_t sample, PositionLockStyle pls, bool recompute)
1181 double const minute_at_bbt = minute_at_bbt_locked (_metrics, bbt);
1182 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_bbt - minute_at_sample (1));
1183 double const pulse = ((bbt.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1184 /* the natural time of the BBT position */
1185 double const time_minutes = minute_at_pulse_locked (_metrics, pulse);
1187 if (pls == AudioTime) {
1188 /* add meter-locked tempo at the natural time in the current map (sample may differ). */
1189 Tempo const tempo_at_time = tempo_at_minute_locked (_metrics, time_minutes);
1190 TempoSection* mlt = add_tempo_locked (tempo_at_time, pulse, time_minutes, AudioTime, true, true, false);
1196 /* still using natural time for the position, ignoring lock style. */
1197 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);
1199 bool solved = false;
1201 do_insert (new_meter);
1205 if (pls == AudioTime) {
1206 /* now set the audio locked meter's position to sample */
1207 solved = solve_map_minute (_metrics, new_meter, minute_at_sample (sample));
1208 /* we failed, most likely due to some impossible sample requirement wrt audio-locked tempi.
1209 fudge sample so that the meter ends up at its BBT position instead.
1212 solved = solve_map_minute (_metrics, new_meter, minute_at_sample (prev_m.sample() + 1));
1215 solved = solve_map_bbt (_metrics, new_meter, bbt);
1216 /* required due to resetting the pulse of meter-locked tempi above.
1217 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1218 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1220 recompute_map (_metrics);
1224 if (!solved && recompute) {
1225 /* if this has failed to solve, there is little we can do other than to ensure that
1226 the new map is recalculated.
1228 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1229 recompute_map (_metrics);
1236 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1238 Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1241 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1242 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1247 Glib::Threads::RWLock::WriterLock lm (lock);
1248 *((Tempo*) t) = newtempo;
1249 recompute_map (_metrics);
1251 PropertyChanged (PropertyChange ());
1258 TempoMap::change_existing_tempo_at (samplepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1260 Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1263 TempoSection* first;
1264 Metrics::iterator i;
1266 /* find the TempoSection immediately preceding "where"
1269 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1271 if ((*i)->sample() > where) {
1277 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1290 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1300 Glib::Threads::RWLock::WriterLock lm (lock);
1301 /* cannot move the first tempo section */
1302 *((Tempo*)prev) = newtempo;
1303 recompute_map (_metrics);
1306 PropertyChanged (PropertyChange ());
1310 TempoMap::first_meter () const
1312 const MeterSection *m = 0;
1314 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1315 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1320 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1321 abort(); /*NOTREACHED*/
1326 TempoMap::first_meter ()
1328 MeterSection *m = 0;
1330 /* CALLER MUST HOLD LOCK */
1332 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1333 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1338 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1339 abort(); /*NOTREACHED*/
1344 TempoMap::first_tempo () const
1346 const TempoSection *t = 0;
1348 /* CALLER MUST HOLD LOCK */
1350 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1351 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1361 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1362 abort(); /*NOTREACHED*/
1367 TempoMap::first_tempo ()
1369 TempoSection *t = 0;
1371 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1372 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1382 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1383 abort(); /*NOTREACHED*/
1387 TempoMap::recompute_tempi (Metrics& metrics)
1389 TempoSection* prev_t = 0;
1391 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1394 if ((*i)->is_tempo()) {
1395 t = static_cast<TempoSection*> (*i);
1408 if (t->position_lock_style() == AudioTime) {
1409 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1410 if (!t->locked_to_meter()) {
1411 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1415 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1416 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1424 prev_t->set_c (0.0);
1427 /* tempos must be positioned correctly.
1428 the current approach is to use a meter's bbt time as its base position unit.
1429 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1430 while a music-locked meter requires recomputations of sample pulse and beat (but not bbt)
1433 TempoMap::recompute_meters (Metrics& metrics)
1435 MeterSection* meter = 0;
1436 MeterSection* prev_m = 0;
1438 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1439 if (!(*mi)->is_tempo()) {
1440 meter = static_cast<MeterSection*> (*mi);
1441 if (meter->position_lock_style() == AudioTime) {
1443 pair<double, BBT_Time> b_bbt;
1444 TempoSection* meter_locked_tempo = 0;
1445 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1447 if ((*ii)->is_tempo()) {
1448 t = static_cast<TempoSection*> (*ii);
1449 if (t->locked_to_meter() && t->sample() == meter->sample()) {
1450 meter_locked_tempo = t;
1457 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1458 if (beats + prev_m->beat() != meter->beat()) {
1459 /* reordering caused a bbt change */
1461 beats = meter->beat() - prev_m->beat();
1462 b_bbt = make_pair (beats + prev_m->beat()
1463 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1464 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1466 } else if (!meter->initial()) {
1467 b_bbt = make_pair (meter->beat(), meter->bbt());
1468 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1471 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1473 if (meter_locked_tempo) {
1474 meter_locked_tempo->set_pulse (pulse);
1476 meter->set_beat (b_bbt);
1477 meter->set_pulse (pulse);
1482 pair<double, BBT_Time> b_bbt;
1484 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1485 if (beats + prev_m->beat() != meter->beat()) {
1486 /* reordering caused a bbt change */
1487 b_bbt = make_pair (beats + prev_m->beat()
1488 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1490 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1492 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1494 /* shouldn't happen - the first is audio-locked */
1495 pulse = pulse_at_beat_locked (metrics, meter->beat());
1496 b_bbt = make_pair (meter->beat(), meter->bbt());
1499 meter->set_beat (b_bbt);
1500 meter->set_pulse (pulse);
1501 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1510 TempoMap::recompute_map (Metrics& metrics, samplepos_t end)
1512 /* CALLER MUST HOLD WRITE LOCK */
1514 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1517 /* silly call from Session::process() during startup
1522 recompute_tempi (metrics);
1523 recompute_meters (metrics);
1527 TempoMap::metric_at (samplepos_t sample, Metrics::const_iterator* last) const
1529 Glib::Threads::RWLock::ReaderLock lm (lock);
1530 TempoMetric m (first_meter(), first_tempo());
1533 *last = ++_metrics.begin();
1536 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1537 at something, because we insert the default tempo and meter during
1538 TempoMap construction.
1540 now see if we can find better candidates.
1543 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1545 if ((*i)->sample() > sample) {
1559 /* XX meters only */
1561 TempoMap::metric_at (BBT_Time bbt) const
1563 Glib::Threads::RWLock::ReaderLock lm (lock);
1564 TempoMetric m (first_meter(), first_tempo());
1566 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1567 at something, because we insert the default tempo and meter during
1568 TempoMap construction.
1570 now see if we can find better candidates.
1573 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1575 if (!(*i)->is_tempo()) {
1576 mw = static_cast<MeterSection*> (*i);
1577 BBT_Time section_start (mw->bbt());
1579 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1590 /** Returns the BBT (meter-based) beat corresponding to the supplied sample, possibly returning a negative value.
1591 * @param sample The session sample position.
1592 * @return The beat duration according to the tempo map at the supplied sample.
1594 * If the supplied sample lies before the first meter, the returned beat duration will be negative.
1595 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1597 * This function uses both tempo and meter.
1600 TempoMap::beat_at_sample (const samplecnt_t sample) const
1602 Glib::Threads::RWLock::ReaderLock lm (lock);
1604 return beat_at_minute_locked (_metrics, minute_at_sample (sample));
1607 /* This function uses both tempo and meter.*/
1609 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1611 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1612 MeterSection* prev_m = 0;
1613 MeterSection* next_m = 0;
1615 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1616 if (!(*i)->is_tempo()) {
1617 if (prev_m && (*i)->minute() > minute) {
1618 next_m = static_cast<MeterSection*> (*i);
1621 prev_m = static_cast<MeterSection*> (*i);
1625 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1627 /* audio locked meters fake their beat */
1628 if (next_m && next_m->beat() < beat) {
1629 return next_m->beat();
1635 /** Returns the sample corresponding to the supplied BBT (meter-based) beat.
1636 * @param beat The BBT (meter-based) beat.
1637 * @return The sample duration according to the tempo map at the supplied BBT (meter-based) beat.
1639 * This function uses both tempo and meter.
1642 TempoMap::sample_at_beat (const double& beat) const
1644 Glib::Threads::RWLock::ReaderLock lm (lock);
1646 return sample_at_minute (minute_at_beat_locked (_metrics, beat));
1649 /* meter & tempo section based */
1651 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1653 MeterSection* prev_m = 0;
1654 TempoSection* prev_t = 0;
1658 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1659 if (!(*i)->is_tempo()) {
1660 m = static_cast<MeterSection*> (*i);
1661 if (prev_m && m->beat() > beat) {
1671 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1672 if ((*i)->is_tempo()) {
1673 t = static_cast<TempoSection*> (*i);
1679 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1688 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1691 /** Returns a Tempo corresponding to the supplied sample position.
1692 * @param sample The audio sample.
1693 * @return a Tempo according to the tempo map at the supplied sample.
1697 TempoMap::tempo_at_sample (const samplepos_t sample) const
1699 Glib::Threads::RWLock::ReaderLock lm (lock);
1701 return tempo_at_minute_locked (_metrics, minute_at_sample (sample));
1705 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1707 TempoSection* prev_t = 0;
1711 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1712 if ((*i)->is_tempo()) {
1713 t = static_cast<TempoSection*> (*i);
1717 if ((prev_t) && t->minute() > minute) {
1718 /* t is the section past sample */
1719 return prev_t->tempo_at_minute (minute);
1725 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1728 /** returns the sample at which the supplied tempo occurs, or
1729 * the sample of the last tempo section (search exhausted)
1730 * only the position of the first occurence will be returned
1734 TempoMap::sample_at_tempo (const Tempo& tempo) const
1736 Glib::Threads::RWLock::ReaderLock lm (lock);
1738 return sample_at_minute (minute_at_tempo_locked (_metrics, tempo));
1742 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1744 TempoSection* prev_t = 0;
1745 const double tempo_bpm = tempo.note_types_per_minute();
1747 Metrics::const_iterator i;
1749 for (i = metrics.begin(); i != metrics.end(); ++i) {
1751 if ((*i)->is_tempo()) {
1752 t = static_cast<TempoSection*> (*i);
1760 if (t->note_types_per_minute() == tempo_bpm) {
1765 const double prev_t_bpm = prev_t->note_types_per_minute();
1766 const double prev_t_end_bpm = prev_t->end_note_types_per_minute();
1767 if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm)
1768 || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm)
1769 || (prev_t_end_bpm == tempo_bpm)) {
1771 return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
1778 return prev_t->minute();
1782 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1784 TempoSection* prev_t = 0;
1788 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1789 if ((*i)->is_tempo()) {
1790 t = static_cast<TempoSection*> (*i);
1794 if ((prev_t) && t->pulse() > pulse) {
1795 /* t is the section past sample */
1796 return prev_t->tempo_at_pulse (pulse);
1802 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1806 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1808 TempoSection* prev_t = 0;
1809 const double tempo_bpm = tempo.note_types_per_minute();
1811 Metrics::const_iterator i;
1813 for (i = metrics.begin(); i != metrics.end(); ++i) {
1815 if ((*i)->is_tempo()) {
1816 t = static_cast<TempoSection*> (*i);
1822 const double t_bpm = t->note_types_per_minute();
1824 if (t_bpm == tempo_bpm) {
1829 const double prev_t_bpm = prev_t->note_types_per_minute();
1831 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1832 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1839 return prev_t->pulse();
1842 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1843 * @param qn the position in quarter note beats.
1844 * @return the Tempo at the supplied quarter-note.
1847 TempoMap::tempo_at_quarter_note (const double& qn) const
1849 Glib::Threads::RWLock::ReaderLock lm (lock);
1851 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1854 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1855 * @param tempo the tempo.
1856 * @return the position in quarter-note beats where the map bpm
1857 * is equal to that of the Tempo. currently ignores note_type.
1860 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1862 Glib::Threads::RWLock::ReaderLock lm (lock);
1864 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;
1867 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1868 * @param metrics the list of metric sections used to calculate the pulse.
1869 * @param beat The BBT (meter-based) beat.
1870 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1872 * a pulse or whole note is the base musical position of a MetricSection.
1873 * it is equivalent to four quarter notes.
1877 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1879 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1881 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1884 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1885 * @param metrics the list of metric sections used to calculate the beat.
1886 * @param pulse the whole-note pulse.
1887 * @return the meter-based beat at the supplied whole-note pulse.
1889 * a pulse or whole note is the base musical position of a MetricSection.
1890 * it is equivalent to four quarter notes.
1893 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1895 MeterSection* prev_m = 0;
1897 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1899 if (!(*i)->is_tempo()) {
1900 m = static_cast<MeterSection*> (*i);
1901 if (prev_m && m->pulse() > pulse) {
1909 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1913 /* tempo section based */
1915 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1917 /* HOLD (at least) THE READER LOCK */
1918 TempoSection* prev_t = 0;
1920 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1922 if ((*i)->is_tempo()) {
1923 t = static_cast<TempoSection*> (*i);
1927 if (prev_t && t->minute() > minute) {
1928 /*the previous ts is the one containing the sample */
1929 const double ret = prev_t->pulse_at_minute (minute);
1930 /* audio locked section in new meter*/
1931 if (t->pulse() < ret) {
1940 /* treated as constant for this ts */
1941 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1943 return pulses_in_section + prev_t->pulse();
1946 /* tempo section based */
1948 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1950 /* HOLD THE READER LOCK */
1952 const TempoSection* prev_t = 0;
1954 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1957 if ((*i)->is_tempo()) {
1958 t = static_cast<TempoSection*> (*i);
1962 if (prev_t && t->pulse() > pulse) {
1963 return prev_t->minute_at_pulse (pulse);
1969 /* must be treated as constant, irrespective of _type */
1970 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1972 return dtime + prev_t->minute();
1975 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1976 * @param bbt The BBT time (meter-based).
1977 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1981 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1983 Glib::Threads::RWLock::ReaderLock lm (lock);
1984 return beat_at_bbt_locked (_metrics, bbt);
1989 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1991 /* CALLER HOLDS READ LOCK */
1993 MeterSection* prev_m = 0;
1995 /* because audio-locked meters have 'fake' integral beats,
1996 there is no pulse offset here.
2000 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2001 if (!(*i)->is_tempo()) {
2002 m = static_cast<MeterSection*> (*i);
2004 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2005 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2013 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2014 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2015 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2020 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2021 * @param beat The BBT (meter-based) beat.
2022 * @return The BBT time (meter-based) at the supplied meter-based beat.
2026 TempoMap::bbt_at_beat (const double& beat)
2028 Glib::Threads::RWLock::ReaderLock lm (lock);
2029 return bbt_at_beat_locked (_metrics, beat);
2033 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2035 /* CALLER HOLDS READ LOCK */
2036 MeterSection* prev_m = 0;
2037 const double beats = max (0.0, b);
2039 MeterSection* m = 0;
2041 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2042 if (!(*i)->is_tempo()) {
2043 m = static_cast<MeterSection*> (*i);
2045 if (m->beat() > beats) {
2046 /* this is the meter after the one our beat is on*/
2056 const double beats_in_ms = beats - prev_m->beat();
2057 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2058 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2059 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2060 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2064 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2065 ret.beats = (uint32_t) floor (remaining_beats);
2066 ret.bars = total_bars;
2068 /* 0 0 0 to 1 1 0 - based mapping*/
2072 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2074 ret.ticks -= BBT_Time::ticks_per_beat;
2077 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2085 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2086 * @param bbt The BBT time (meter-based).
2087 * @return the quarter note beat at the supplied BBT time
2089 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2091 * while the input uses meter, the output does not.
2094 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2096 Glib::Threads::RWLock::ReaderLock lm (lock);
2098 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2102 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2104 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2107 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2110 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2114 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2116 /* CALLER HOLDS READ LOCK */
2118 MeterSection* prev_m = 0;
2120 /* because audio-locked meters have 'fake' integral beats,
2121 there is no pulse offset here.
2125 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2126 if (!(*i)->is_tempo()) {
2127 m = static_cast<MeterSection*> (*i);
2129 if (m->bbt().bars > bbt.bars) {
2137 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2138 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2139 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2144 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2145 * @param qn the quarter-note beat.
2146 * @return The BBT time (meter-based) at the supplied meter-based beat.
2148 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2152 TempoMap::bbt_at_quarter_note (const double& qn)
2154 Glib::Threads::RWLock::ReaderLock lm (lock);
2156 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2159 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2160 * @param metrics The list of metric sections used to determine the result.
2161 * @param pulse The whole-note pulse.
2162 * @return The BBT time at the supplied whole-note pulse.
2164 * a pulse or whole note is the basic musical position of a MetricSection.
2165 * it is equivalent to four quarter notes.
2166 * while the output uses meter, the input does not.
2169 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2171 MeterSection* prev_m = 0;
2173 MeterSection* m = 0;
2175 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2177 if (!(*i)->is_tempo()) {
2178 m = static_cast<MeterSection*> (*i);
2181 double const pulses_to_m = m->pulse() - prev_m->pulse();
2182 if (prev_m->pulse() + pulses_to_m > pulse) {
2183 /* this is the meter after the one our beat is on*/
2194 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2195 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2196 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2197 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2198 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2202 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2203 ret.beats = (uint32_t) floor (remaining_beats);
2204 ret.bars = total_bars;
2206 /* 0 0 0 to 1 1 0 mapping*/
2210 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2212 ret.ticks -= BBT_Time::ticks_per_beat;
2215 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2223 /** Returns the BBT time corresponding to the supplied sample position.
2224 * @param sample the position in audio samples.
2225 * @return the BBT time at the sample position .
2229 TempoMap::bbt_at_sample (samplepos_t sample)
2237 warning << string_compose (_("tempo map was asked for BBT time at sample %1\n"), sample) << endmsg;
2242 const double minute = minute_at_sample (sample);
2244 Glib::Threads::RWLock::ReaderLock lm (lock);
2246 return bbt_at_minute_locked (_metrics, minute);
2250 TempoMap::bbt_at_sample_rt (samplepos_t sample)
2252 const double minute = minute_at_sample (sample);
2254 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2257 throw std::logic_error ("TempoMap::bbt_at_sample_rt() could not lock tempo map");
2260 return bbt_at_minute_locked (_metrics, minute);
2264 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2274 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2275 MeterSection* prev_m = 0;
2276 MeterSection* next_m = 0;
2280 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2281 if (!(*i)->is_tempo()) {
2282 m = static_cast<MeterSection*> (*i);
2283 if (prev_m && m->minute() > minute) {
2291 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2293 /* handle sample before first meter */
2294 if (minute < prev_m->minute()) {
2297 /* audio locked meters fake their beat */
2298 if (next_m && next_m->beat() < beat) {
2299 beat = next_m->beat();
2302 beat = max (0.0, beat);
2304 const double beats_in_ms = beat - prev_m->beat();
2305 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2306 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2307 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2308 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2312 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2313 ret.beats = (uint32_t) floor (remaining_beats);
2314 ret.bars = total_bars;
2316 /* 0 0 0 to 1 1 0 - based mapping*/
2320 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2322 ret.ticks -= BBT_Time::ticks_per_beat;
2325 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2333 /** Returns the sample position corresponding to the supplied BBT time.
2334 * @param bbt the position in BBT time.
2335 * @return the sample position at bbt.
2339 TempoMap::sample_at_bbt (const BBT_Time& bbt)
2343 warning << string_compose (_("tempo map asked for sample time at bar < 1 (%1)\n"), bbt) << endmsg;
2348 if (bbt.beats < 1) {
2349 throw std::logic_error ("beats are counted from one");
2354 Glib::Threads::RWLock::ReaderLock lm (lock);
2355 minute = minute_at_bbt_locked (_metrics, bbt);
2358 return sample_at_minute (minute);
2361 /* meter & tempo section based */
2363 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2365 /* HOLD THE READER LOCK */
2367 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2372 * Returns the quarter-note beat position corresponding to the supplied sample.
2374 * @param sample the position in samples.
2375 * @return The quarter-note position of the supplied sample. Ignores meter.
2379 TempoMap::quarter_note_at_sample (const samplepos_t sample) const
2381 const double minute = minute_at_sample (sample);
2383 Glib::Threads::RWLock::ReaderLock lm (lock);
2385 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2389 TempoMap::quarter_note_at_sample_rt (const samplepos_t sample) const
2391 const double minute = minute_at_sample (sample);
2393 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2396 throw std::logic_error ("TempoMap::quarter_note_at_sample_rt() could not lock tempo map");
2399 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2403 * Returns the sample position corresponding to the supplied quarter-note beat.
2405 * @param quarter_note the quarter-note position.
2406 * @return the sample position of the supplied quarter-note. Ignores meter.
2411 TempoMap::sample_at_quarter_note (const double quarter_note) const
2415 Glib::Threads::RWLock::ReaderLock lm (lock);
2417 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2420 return sample_at_minute (minute);
2423 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2424 * @param beat The BBT (meter-based) beat.
2425 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2427 * a quarter-note may be compared with and assigned to Temporal::Beats.
2431 TempoMap::quarter_note_at_beat (const double beat) const
2433 Glib::Threads::RWLock::ReaderLock lm (lock);
2435 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2438 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2439 * @param quarter_note The position in quarter-note beats.
2440 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2442 * a quarter-note is the musical unit of Temporal::Beats.
2446 TempoMap::beat_at_quarter_note (const double quarter_note) const
2448 Glib::Threads::RWLock::ReaderLock lm (lock);
2450 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2453 /** Returns the duration in samples between two supplied quarter-note beat positions.
2454 * @param start the first position in quarter-note beats.
2455 * @param end the end position in quarter-note beats.
2456 * @return the sample distance ober the quarter-note beats duration.
2458 * use this rather than e.g.
2459 * sample_at-quarter_note (end_beats) - sample_at_quarter_note (start_beats).
2460 * samples_between_quarter_notes() doesn't round to audio samples as an intermediate step,
2464 TempoMap::samples_between_quarter_notes (const double start, const double end) const
2469 Glib::Threads::RWLock::ReaderLock lm (lock);
2470 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2473 return sample_at_minute (minutes);
2477 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2480 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2484 TempoMap::quarter_notes_between_samples (const samplecnt_t start, const samplecnt_t end) const
2486 Glib::Threads::RWLock::ReaderLock lm (lock);
2488 return quarter_notes_between_samples_locked (_metrics, start, end);
2492 TempoMap::quarter_notes_between_samples_locked (const Metrics& metrics, const samplecnt_t start, const samplecnt_t end) const
2494 const TempoSection* prev_t = 0;
2496 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2499 if ((*i)->is_tempo()) {
2500 t = static_cast<TempoSection*> (*i);
2504 if (prev_t && t->sample() > start) {
2511 const double start_qn = prev_t->pulse_at_sample (start);
2513 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2516 if ((*i)->is_tempo()) {
2517 t = static_cast<TempoSection*> (*i);
2521 if (prev_t && t->sample() > end) {
2527 const double end_qn = prev_t->pulse_at_sample (end);
2529 return (end_qn - start_qn) * 4.0;
2533 TempoMap::check_solved (const Metrics& metrics) const
2535 TempoSection* prev_t = 0;
2536 MeterSection* prev_m = 0;
2538 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2541 if ((*i)->is_tempo()) {
2542 t = static_cast<TempoSection*> (*i);
2547 /* check ordering */
2548 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2552 /* precision check ensures tempo and samples align.*/
2553 if (t->sample() != sample_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2554 if (!t->locked_to_meter()) {
2559 /* gradient limit - who knows what it should be?
2560 things are also ok (if a little chaotic) without this
2562 if (fabs (prev_t->c()) > 1000.0) {
2563 //std::cout << "c : " << prev_t->c() << std::endl;
2570 if (!(*i)->is_tempo()) {
2571 m = static_cast<MeterSection*> (*i);
2572 if (prev_m && m->position_lock_style() == AudioTime) {
2573 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_sample (m->sample() - 1));
2574 const samplepos_t nascent_m_sample = sample_at_minute (t->minute_at_pulse (m->pulse()));
2575 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2577 if (t && (nascent_m_sample > m->sample() || nascent_m_sample < 0)) {
2591 TempoMap::set_active_tempi (const Metrics& metrics, const samplepos_t sample)
2593 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2595 if ((*i)->is_tempo()) {
2596 t = static_cast<TempoSection*> (*i);
2597 if (t->locked_to_meter()) {
2598 t->set_active (true);
2599 } else if (t->position_lock_style() == AudioTime) {
2600 if (t->sample() < sample) {
2601 t->set_active (false);
2602 t->set_pulse (-1.0);
2603 } else if (t->sample() > sample) {
2604 t->set_active (true);
2605 } else if (t->sample() == sample) {
2615 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2617 TempoSection* prev_t = 0;
2618 TempoSection* section_prev = 0;
2619 double first_m_minute = 0.0;
2620 const bool sml = section->locked_to_meter();
2622 /* can't move a tempo before the first meter */
2623 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2625 if (!(*i)->is_tempo()) {
2626 m = static_cast<MeterSection*> (*i);
2628 first_m_minute = m->minute();
2633 if (!section->initial() && minute <= first_m_minute) {
2637 section->set_active (true);
2638 section->set_minute (minute);
2640 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2642 if ((*i)->is_tempo()) {
2643 t = static_cast<TempoSection*> (*i);
2655 if (t->sample() == sample_at_minute (minute)) {
2659 const bool tlm = t->position_lock_style() == MusicTime;
2661 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2662 section_prev = prev_t;
2664 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2665 if (!section->locked_to_meter()) {
2666 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2671 if (t->position_lock_style() == MusicTime) {
2672 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2673 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2675 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2676 if (!t->locked_to_meter()) {
2677 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2685 MetricSectionFrameSorter fcmp;
2686 imaginary.sort (fcmp);
2688 recompute_tempi (imaginary);
2690 if (check_solved (imaginary)) {
2698 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2700 TempoSection* prev_t = 0;
2701 TempoSection* section_prev = 0;
2703 section->set_pulse (pulse);
2705 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2707 if ((*i)->is_tempo()) {
2708 t = static_cast<TempoSection*> (*i);
2719 section_prev = prev_t;
2723 if (t->position_lock_style() == MusicTime) {
2724 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2725 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2727 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2728 if (!t->locked_to_meter()) {
2729 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2738 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2739 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2742 MetricSectionSorter cmp;
2743 imaginary.sort (cmp);
2745 recompute_tempi (imaginary);
2747 * XX need a restriction here, but only for this case,
2748 * as audio locked tempos don't interact in the same way.
2750 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2752 * |50 bpm |250 bpm |60 bpm
2753 * drag 250 to the pulse after 60->
2754 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2756 if (check_solved (imaginary)) {
2764 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2766 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2767 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2768 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2772 if (section->initial()) {
2773 /* lock the first tempo to our first meter */
2774 if (!set_active_tempi (imaginary, sample_at_minute (minute))) {
2779 TempoSection* meter_locked_tempo = 0;
2781 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2783 if ((*ii)->is_tempo()) {
2784 t = static_cast<TempoSection*> (*ii);
2785 if (t->locked_to_meter() && t->sample() == section->sample()) {
2786 meter_locked_tempo = t;
2792 if (!meter_locked_tempo) {
2796 MeterSection* prev_m = 0;
2798 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2799 bool solved = false;
2801 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2803 if (!(*i)->is_tempo()) {
2804 m = static_cast<MeterSection*> (*i);
2806 if (prev_m && !section->initial()) {
2807 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2808 if (beats + prev_m->beat() < section->beat()) {
2809 /* set the section pulse according to its musical position,
2810 * as an earlier time than this has been requested.
2812 const double new_pulse = ((section->beat() - prev_m->beat())
2813 / prev_m->note_divisor()) + prev_m->pulse();
2815 tempo_copy->set_position_lock_style (MusicTime);
2816 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2817 meter_locked_tempo->set_position_lock_style (MusicTime);
2818 section->set_position_lock_style (MusicTime);
2819 section->set_pulse (new_pulse);
2820 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2821 meter_locked_tempo->set_position_lock_style (AudioTime);
2822 section->set_position_lock_style (AudioTime);
2823 section->set_minute (meter_locked_tempo->minute());
2829 Metrics::const_iterator d = future_map.begin();
2830 while (d != future_map.end()) {
2839 /* all is ok. set section's locked tempo if allowed.
2840 possibly disallow if there is an adjacent audio-locked tempo.
2841 XX this check could possibly go. its never actually happened here.
2843 MeterSection* meter_copy = const_cast<MeterSection*>
2844 (&meter_section_at_minute_locked (future_map, section->minute()));
2846 meter_copy->set_minute (minute);
2848 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2849 section->set_minute (minute);
2850 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2851 / prev_m->note_divisor()) + prev_m->pulse());
2852 solve_map_minute (imaginary, meter_locked_tempo, minute);
2857 Metrics::const_iterator d = future_map.begin();
2858 while (d != future_map.end()) {
2868 /* initial (first meter atm) */
2870 tempo_copy->set_minute (minute);
2871 tempo_copy->set_pulse (0.0);
2873 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2874 section->set_minute (minute);
2875 meter_locked_tempo->set_minute (minute);
2876 meter_locked_tempo->set_pulse (0.0);
2877 solve_map_minute (imaginary, meter_locked_tempo, minute);
2882 Metrics::const_iterator d = future_map.begin();
2883 while (d != future_map.end()) {
2892 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2893 section->set_beat (b_bbt);
2894 section->set_pulse (0.0);
2904 MetricSectionFrameSorter fcmp;
2905 imaginary.sort (fcmp);
2907 recompute_meters (imaginary);
2913 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2915 /* disallow setting section to an existing meter's bbt */
2916 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2918 if (!(*i)->is_tempo()) {
2919 m = static_cast<MeterSection*> (*i);
2920 if (m != section && m->bbt().bars == when.bars) {
2926 MeterSection* prev_m = 0;
2927 MeterSection* section_prev = 0;
2929 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2931 if (!(*i)->is_tempo()) {
2932 m = static_cast<MeterSection*> (*i);
2938 pair<double, BBT_Time> b_bbt;
2939 double new_pulse = 0.0;
2941 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2942 section_prev = prev_m;
2944 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2945 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2946 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2948 section->set_beat (b_bbt);
2949 section->set_pulse (pulse);
2950 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2954 if (m->position_lock_style() == AudioTime) {
2955 TempoSection* meter_locked_tempo = 0;
2957 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2959 if ((*ii)->is_tempo()) {
2960 t = static_cast<TempoSection*> (*ii);
2961 if (t->locked_to_meter() && t->sample() == m->sample()) {
2962 meter_locked_tempo = t;
2968 if (!meter_locked_tempo) {
2973 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2975 if (beats + prev_m->beat() != m->beat()) {
2976 /* tempo/ meter change caused a change in beat (bar). */
2978 /* the user has requested that the previous section of music overlaps this one.
2979 we have no choice but to change the bar number here, as being locked to audio means
2980 we must stay where we are on the timeline.
2982 beats = m->beat() - prev_m->beat();
2983 b_bbt = make_pair (beats + prev_m->beat()
2984 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2985 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2987 } else if (!m->initial()) {
2988 b_bbt = make_pair (m->beat(), m->bbt());
2989 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2992 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2995 meter_locked_tempo->set_pulse (new_pulse);
2996 m->set_beat (b_bbt);
2997 m->set_pulse (new_pulse);
3001 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3002 if (beats + prev_m->beat() != m->beat()) {
3003 /* tempo/ meter change caused a change in beat (bar). */
3004 b_bbt = make_pair (beats + prev_m->beat()
3005 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3007 b_bbt = make_pair (beats + prev_m->beat()
3010 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3011 m->set_beat (b_bbt);
3012 m->set_pulse (new_pulse);
3013 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3020 if (!section_prev) {
3022 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3023 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3024 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3026 section->set_beat (b_bbt);
3027 section->set_pulse (pulse);
3028 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3031 MetricSectionSorter cmp;
3032 imaginary.sort (cmp);
3034 recompute_meters (imaginary);
3039 /** places a copy of _metrics into copy and returns a pointer
3040 * to section's equivalent in copy.
3043 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section) const
3045 TempoSection* ret = 0;
3047 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3048 if ((*i)->is_tempo()) {
3049 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3051 ret = new TempoSection (*t);
3052 copy.push_back (ret);
3056 TempoSection* cp = new TempoSection (*t);
3057 copy.push_back (cp);
3059 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3060 MeterSection* cp = new MeterSection (*m);
3061 copy.push_back (cp);
3069 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section) const
3071 MeterSection* ret = 0;
3073 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3074 if ((*i)->is_tempo()) {
3075 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3076 TempoSection* cp = new TempoSection (*t);
3077 copy.push_back (cp);
3079 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3081 ret = new MeterSection (*m);
3082 copy.push_back (ret);
3085 MeterSection* cp = new MeterSection (*m);
3086 copy.push_back (cp);
3093 /** answers the question "is this a valid beat position for this tempo section?".
3094 * it returns true if the tempo section can be moved to the requested bbt position,
3095 * leaving the tempo map in a solved state.
3096 * @param ts the tempo section to be moved
3097 * @param bbt the requested new position for the tempo section
3098 * @return true if the tempo section can be moved to the position, otherwise false.
3101 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3104 TempoSection* tempo_copy = 0;
3107 Glib::Threads::RWLock::ReaderLock lm (lock);
3108 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3114 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3116 Metrics::const_iterator d = copy.begin();
3117 while (d != copy.end()) {
3126 * 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,
3127 * taking any possible reordering as a consequence of this into account.
3128 * @param section - the section to be altered
3129 * @param bbt - the BBT time where the altered tempo will fall
3130 * @return returns - the position in pulses and samples (as a pair) where the new tempo section will lie.
3132 pair<double, samplepos_t>
3133 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3136 pair<double, samplepos_t> ret = make_pair (0.0, 0);
3138 Glib::Threads::RWLock::ReaderLock lm (lock);
3140 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3142 const double beat = beat_at_bbt_locked (future_map, bbt);
3144 if (section->position_lock_style() == AudioTime) {
3145 tempo_copy->set_position_lock_style (MusicTime);
3148 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3149 ret.first = tempo_copy->pulse();
3150 ret.second = tempo_copy->sample();
3152 ret.first = section->pulse();
3153 ret.second = section->sample();
3156 Metrics::const_iterator d = future_map.begin();
3157 while (d != future_map.end()) {
3164 /** moves a TempoSection to a specified position.
3165 * @param ts - the section to be moved
3166 * @param sample - the new position in samples for the tempo
3167 * @param sub_num - the snap division to use if using musical time.
3169 * if sub_num is non-zero, the sample position is used to calculate an exact
3172 * -1 | snap to bars (meter-based)
3173 * 0 | no snap - use audio sample for musical position
3174 * 1 | snap to meter-based (BBT) beat
3175 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3177 * this follows the snap convention in the gui.
3178 * if sub_num is zero, the musical position will be taken from the supplied sample.
3181 TempoMap::gui_set_tempo_position (TempoSection* ts, const samplepos_t sample, const int& sub_num)
3185 if (ts->position_lock_style() == MusicTime) {
3187 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied sample. */
3188 Glib::Threads::RWLock::WriterLock lm (lock);
3189 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3191 tempo_copy->set_position_lock_style (AudioTime);
3193 if (solve_map_minute (future_map, tempo_copy, minute_at_sample (sample))) {
3194 const double beat = exact_beat_at_sample_locked (future_map, sample, sub_num);
3195 const double pulse = pulse_at_beat_locked (future_map, beat);
3197 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3198 solve_map_pulse (_metrics, ts, pulse);
3199 recompute_meters (_metrics);
3207 Glib::Threads::RWLock::WriterLock lm (lock);
3208 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3212 /* We're moving the object that defines the grid while snapping to it...
3213 * Placing the ts at the beat corresponding to the requested sample may shift the
3214 * grid in such a way that the mouse is left hovering over a completerly different division,
3215 * causing jittering when the mouse next moves (esp. large tempo deltas).
3216 * We fudge around this by doing this in the musical domain and then swapping back for the recompute.
3218 const double qn = exact_qn_at_sample_locked (_metrics, sample, sub_num);
3219 tempo_copy->set_position_lock_style (MusicTime);
3220 if (solve_map_pulse (future_map, tempo_copy, qn / 4.0)) {
3221 ts->set_position_lock_style (MusicTime);
3222 solve_map_pulse (_metrics, ts, qn / 4.0);
3223 ts->set_position_lock_style (AudioTime);
3224 recompute_meters (_metrics);
3227 if (solve_map_minute (future_map, tempo_copy, minute_at_sample (sample))) {
3228 solve_map_minute (_metrics, ts, minute_at_sample (sample));
3229 recompute_meters (_metrics);
3235 Metrics::const_iterator d = future_map.begin();
3236 while (d != future_map.end()) {
3241 MetricPositionChanged (PropertyChange ()); // Emit Signal
3244 /** moves a MeterSection to a specified position.
3245 * @param ms - the section to be moved
3246 * @param sample - the new position in samples for the meter
3248 * as a meter cannot snap to anything but bars,
3249 * the supplied sample is rounded to the nearest bar, possibly
3250 * leaving the meter position unchanged.
3253 TempoMap::gui_set_meter_position (MeterSection* ms, const samplepos_t sample)
3257 if (ms->position_lock_style() == AudioTime) {
3260 Glib::Threads::RWLock::WriterLock lm (lock);
3261 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3263 if (solve_map_minute (future_map, copy, minute_at_sample (sample))) {
3264 solve_map_minute (_metrics, ms, minute_at_sample (sample));
3265 recompute_tempi (_metrics);
3270 Glib::Threads::RWLock::WriterLock lm (lock);
3271 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3273 const double beat = beat_at_minute_locked (_metrics, minute_at_sample (sample));
3274 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3276 if (solve_map_bbt (future_map, copy, bbt)) {
3277 solve_map_bbt (_metrics, ms, bbt);
3278 recompute_tempi (_metrics);
3283 Metrics::const_iterator d = future_map.begin();
3284 while (d != future_map.end()) {
3289 MetricPositionChanged (PropertyChange ()); // Emit Signal
3293 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3296 bool can_solve = false;
3298 Glib::Threads::RWLock::WriterLock lm (lock);
3299 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3301 if (tempo_copy->type() == TempoSection::Constant) {
3302 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3303 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3305 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3306 tempo_copy->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3309 if (ts->clamped()) {
3310 TempoSection* prev = 0;
3311 if ((prev = previous_tempo_section_locked (future_map, tempo_copy)) != 0) {
3312 prev->set_end_note_types_per_minute (tempo_copy->note_types_per_minute());
3316 recompute_tempi (future_map);
3318 if (check_solved (future_map)) {
3319 if (ts->type() == TempoSection::Constant) {
3320 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3321 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3323 ts->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3324 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3327 if (ts->clamped()) {
3328 TempoSection* prev = 0;
3329 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3330 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3334 recompute_map (_metrics);
3339 Metrics::const_iterator d = future_map.begin();
3340 while (d != future_map.end()) {
3345 MetricPositionChanged (PropertyChange ()); // Emit Signal
3352 TempoMap::gui_stretch_tempo (TempoSection* ts, const samplepos_t sample, const samplepos_t end_sample, const double start_qnote, const double end_qnote)
3355 Ts (future prev_t) Tnext
3358 |----------|----------
3359 e_f qn_beats(sample)
3365 Glib::Threads::RWLock::WriterLock lm (lock);
3371 TempoSection* ts_copy = copy_metrics_and_point (_metrics, future_map, ts);
3377 /* minimum allowed measurement distance in samples */
3378 samplepos_t const min_dframe = 2;
3381 if (ts_copy->clamped()) {
3382 TempoSection* next_t = next_tempo_section_locked (future_map, ts_copy);
3383 TempoSection* prev_to_ts_copy = previous_tempo_section_locked (future_map, ts_copy);
3384 /* the change in samples is the result of changing the slope of at most 2 previous tempo sections.
3385 constant to constant is straightforward, as the tempo prev to ts_copy has constant slope.
3386 */ double contribution = 0.0;
3387 if (next_t && prev_to_ts_copy && prev_to_ts_copy->type() == TempoSection::Ramp) {
3388 contribution = (ts_copy->pulse() - prev_to_ts_copy->pulse()) / (double) (next_t->pulse() - prev_to_ts_copy->pulse());
3390 samplepos_t const fr_off = end_sample - sample;
3391 sampleoffset_t const ts_copy_sample_contribution = fr_off - (contribution * (double) fr_off);
3393 if (sample > prev_to_ts_copy->sample() + min_dframe && (sample + ts_copy_sample_contribution) > prev_to_ts_copy->sample() + min_dframe) {
3394 new_bpm = ts_copy->note_types_per_minute() * ((start_qnote - (prev_to_ts_copy->pulse() * 4.0))
3395 / (end_qnote - (prev_to_ts_copy->pulse() * 4.0)));
3397 new_bpm = ts_copy->note_types_per_minute();
3400 if (sample > ts_copy->sample() + min_dframe && end_sample > ts_copy->sample() + min_dframe) {
3402 new_bpm = ts_copy->note_types_per_minute() * ((sample - ts_copy->sample())
3403 / (double) (end_sample - ts_copy->sample()));
3405 new_bpm = ts_copy->note_types_per_minute();
3408 new_bpm = min (new_bpm, (double) 1000.0);
3410 /* don't clamp and proceed here.
3411 testing has revealed that this can go negative,
3412 which is an entirely different thing to just being too low.
3415 if (new_bpm < 0.5) {
3419 ts_copy->set_note_types_per_minute (new_bpm);
3421 if (ts_copy->clamped()) {
3422 TempoSection* prev = 0;
3423 if ((prev = previous_tempo_section_locked (future_map, ts_copy)) != 0) {
3424 prev->set_end_note_types_per_minute (ts_copy->note_types_per_minute());
3428 recompute_tempi (future_map);
3429 recompute_meters (future_map);
3431 if (check_solved (future_map)) {
3432 ts->set_note_types_per_minute (new_bpm);
3434 if (ts->clamped()) {
3435 TempoSection* prev = 0;
3436 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3437 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3441 recompute_tempi (_metrics);
3442 recompute_meters (_metrics);
3448 Metrics::const_iterator d = future_map.begin();
3449 while (d != future_map.end()) {
3453 MetricPositionChanged (PropertyChange ()); // Emit Signal
3458 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const samplepos_t sample, const samplepos_t end_sample)
3461 Ts (future prev_t) Tnext
3464 |----------|----------
3465 e_f qn_beats(sample)
3471 Glib::Threads::RWLock::WriterLock lm (lock);
3477 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3483 /* minimum allowed measurement distance in samples */
3484 samplepos_t const min_dframe = 2;
3487 if (sample > prev_t->sample() + min_dframe && end_sample > prev_t->sample() + min_dframe) {
3488 new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->sample() - sample)
3489 / (double) (prev_t->sample() - end_sample));
3491 new_bpm = prev_t->end_note_types_per_minute();
3494 new_bpm = min (new_bpm, (double) 1000.0);
3496 if (new_bpm < 0.5) {
3500 prev_t->set_end_note_types_per_minute (new_bpm);
3502 TempoSection* next = 0;
3503 if ((next = next_tempo_section_locked (future_map, prev_t)) != 0) {
3504 if (next->clamped()) {
3505 next->set_note_types_per_minute (prev_t->end_note_types_per_minute());
3509 recompute_tempi (future_map);
3510 recompute_meters (future_map);
3512 if (check_solved (future_map)) {
3513 ts->set_end_note_types_per_minute (new_bpm);
3515 TempoSection* true_next = 0;
3516 if ((true_next = next_tempo_section_locked (_metrics, ts)) != 0) {
3517 if (true_next->clamped()) {
3518 true_next->set_note_types_per_minute (ts->end_note_types_per_minute());
3522 recompute_tempi (_metrics);
3523 recompute_meters (_metrics);
3529 Metrics::const_iterator d = future_map.begin();
3530 while (d != future_map.end()) {
3535 MetricPositionChanged (PropertyChange ()); // Emit Signal
3539 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const samplepos_t sample, const samplepos_t end_sample)
3541 TempoSection* next_t = 0;
3542 TempoSection* next_to_next_t = 0;
3544 bool can_solve = false;
3546 /* minimum allowed measurement distance in samples */
3547 samplepos_t const min_dframe = 2;
3550 Glib::Threads::RWLock::WriterLock lm (lock);
3555 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3556 TempoSection* prev_to_prev_t = 0;
3557 const sampleoffset_t fr_off = end_sample - sample;
3563 if (tempo_copy->pulse() > 0.0) {
3564 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_sample (tempo_copy->sample() - 1)));
3567 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3568 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3569 next_t = static_cast<TempoSection*> (*i);
3578 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3579 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3580 next_to_next_t = static_cast<TempoSection*> (*i);
3585 if (!next_to_next_t) {
3589 double prev_contribution = 0.0;
3591 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3592 prev_contribution = (tempo_copy->sample() - prev_to_prev_t->sample()) / (double) (next_t->sample() - prev_to_prev_t->sample());
3595 const sampleoffset_t tempo_copy_sample_contribution = fr_off - (prev_contribution * (double) fr_off);
3598 samplepos_t old_tc_minute = tempo_copy->minute();
3599 double old_next_minute = next_t->minute();
3600 double old_next_to_next_minute = next_to_next_t->minute();
3603 double new_next_bpm;
3604 double new_copy_end_bpm;
3606 if (sample > tempo_copy->sample() + min_dframe && (sample + tempo_copy_sample_contribution) > tempo_copy->sample() + min_dframe) {
3607 new_bpm = tempo_copy->note_types_per_minute() * ((sample - tempo_copy->sample())
3608 / (double) (end_sample - tempo_copy->sample()));
3610 new_bpm = tempo_copy->note_types_per_minute();
3613 /* don't clamp and proceed here.
3614 testing has revealed that this can go negative,
3615 which is an entirely different thing to just being too low.
3617 if (new_bpm < 0.5) {
3621 new_bpm = min (new_bpm, (double) 1000.0);
3623 tempo_copy->set_note_types_per_minute (new_bpm);
3624 if (tempo_copy->type() == TempoSection::Constant) {
3625 tempo_copy->set_end_note_types_per_minute (new_bpm);
3628 recompute_tempi (future_map);
3630 if (check_solved (future_map)) {
3636 ts->set_note_types_per_minute (new_bpm);
3637 if (ts->type() == TempoSection::Constant) {
3638 ts->set_end_note_types_per_minute (new_bpm);
3641 recompute_map (_metrics);
3646 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3647 if (sample > tempo_copy->sample() + min_dframe && end_sample > tempo_copy->sample() + min_dframe) {
3649 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3650 / (double) ((old_next_to_next_minute) - old_next_minute));
3653 new_next_bpm = next_t->note_types_per_minute();
3656 next_t->set_note_types_per_minute (new_next_bpm);
3657 recompute_tempi (future_map);
3659 if (check_solved (future_map)) {
3660 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3661 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3662 next_t = static_cast<TempoSection*> (*i);
3670 next_t->set_note_types_per_minute (new_next_bpm);
3671 recompute_map (_metrics);
3675 double next_sample_ratio = 1.0;
3676 double copy_sample_ratio = 1.0;
3678 if (next_to_next_t) {
3679 next_sample_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute - old_next_minute);
3681 copy_sample_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute));
3684 new_next_bpm = next_t->note_types_per_minute() * next_sample_ratio;
3685 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_sample_ratio;
3687 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3689 if (next_t->clamped()) {
3690 next_t->set_note_types_per_minute (new_copy_end_bpm);
3692 next_t->set_note_types_per_minute (new_next_bpm);
3695 recompute_tempi (future_map);
3697 if (check_solved (future_map)) {
3698 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3699 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3700 next_t = static_cast<TempoSection*> (*i);
3709 if (next_t->clamped()) {
3710 next_t->set_note_types_per_minute (new_copy_end_bpm);
3712 next_t->set_note_types_per_minute (new_next_bpm);
3715 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3716 recompute_map (_metrics);
3722 Metrics::const_iterator d = future_map.begin();
3723 while (d != future_map.end()) {
3728 MetricPositionChanged (PropertyChange ()); // Emit Signal
3733 /** Returns the sample position of the musical position zero */
3735 TempoMap::music_origin ()
3737 Glib::Threads::RWLock::ReaderLock lm (lock);
3739 return first_tempo().sample();
3742 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3743 * the supplied sample, possibly returning a negative value.
3745 * @param sample The session sample position.
3746 * @param sub_num The subdivision to use when rounding the beat.
3747 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3748 * Positive integers indicate quarter note (non BBT) divisions.
3749 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_sample()).
3750 * @return The beat position of the supplied sample.
3752 * when working to a musical grid, the use of sub_nom indicates that
3753 * the position should be interpreted musically.
3755 * it effectively snaps to meter bars, meter beats or quarter note divisions
3756 * (as per current gui convention) and returns a musical position independent of frame rate.
3758 * If the supplied sample lies before the first meter, the return will be negative,
3759 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3760 * the continuation of the tempo curve (backwards).
3762 * This function is sensitive to tempo and meter.
3765 TempoMap::exact_beat_at_sample (const samplepos_t sample, const int32_t sub_num) const
3767 Glib::Threads::RWLock::ReaderLock lm (lock);
3769 return exact_beat_at_sample_locked (_metrics, sample, sub_num);
3773 TempoMap::exact_beat_at_sample_locked (const Metrics& metrics, const samplepos_t sample, const int32_t divisions) const
3775 return beat_at_pulse_locked (_metrics, exact_qn_at_sample_locked (metrics, sample, divisions) / 4.0);
3778 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3779 * the supplied sample, possibly returning a negative value.
3781 * @param sample The session sample position.
3782 * @param sub_num The subdivision to use when rounding the quarter note.
3783 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3784 * Positive integers indicate quarter note (non BBT) divisions.
3785 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_sample()).
3786 * @return The quarter note position of the supplied sample.
3788 * When working to a musical grid, the use of sub_nom indicates that
3789 * the sample position should be interpreted musically.
3791 * it effectively snaps to meter bars, meter beats or quarter note divisions
3792 * (as per current gui convention) and returns a musical position independent of frame rate.
3794 * If the supplied sample lies before the first meter, the return will be negative,
3795 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3796 * the continuation of the tempo curve (backwards).
3798 * This function is tempo-sensitive.
3801 TempoMap::exact_qn_at_sample (const samplepos_t sample, const int32_t sub_num) const
3803 Glib::Threads::RWLock::ReaderLock lm (lock);
3805 return exact_qn_at_sample_locked (_metrics, sample, sub_num);
3809 TempoMap::exact_qn_at_sample_locked (const Metrics& metrics, const samplepos_t sample, const int32_t sub_num) const
3811 double qn = pulse_at_minute_locked (metrics, minute_at_sample (sample)) * 4.0;
3814 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3815 } else if (sub_num == 1) {
3816 /* the gui requested exact musical (BBT) beat */
3817 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_sample (sample)) + 0.5))) * 4.0;
3818 } else if (sub_num == -1) {
3820 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3824 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3826 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3828 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3838 /** returns the sample duration of the supplied BBT time at a specified sample position in the tempo map.
3839 * @param pos the sample position in the tempo map.
3840 * @param bbt the distance in BBT time from pos to calculate.
3841 * @param dir the rounding direction..
3842 * @return the duration in samples between pos and bbt
3845 TempoMap::bbt_duration_at (samplepos_t pos, const BBT_Time& bbt, int dir)
3847 Glib::Threads::RWLock::ReaderLock lm (lock);
3849 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_sample (pos));
3851 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_sample (pos)).divisions_per_bar();
3854 pos_bbt.bars += bbt.bars;
3856 pos_bbt.ticks += bbt.ticks;
3857 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3859 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3862 pos_bbt.beats += bbt.beats;
3863 if ((double) pos_bbt.beats > divisions) {
3865 pos_bbt.beats -= divisions;
3867 const samplecnt_t pos_bbt_sample = sample_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3869 return pos_bbt_sample - pos;
3873 if (pos_bbt.bars <= bbt.bars) {
3876 pos_bbt.bars -= bbt.bars;
3879 if (pos_bbt.ticks < bbt.ticks) {
3880 if (pos_bbt.bars > 1) {
3881 if (pos_bbt.beats == 1) {
3883 pos_bbt.beats = divisions;
3887 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3893 pos_bbt.ticks -= bbt.ticks;
3896 if (pos_bbt.beats <= bbt.beats) {
3897 if (pos_bbt.bars > 1) {
3899 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3904 pos_bbt.beats -= bbt.beats;
3907 return pos - sample_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3914 TempoMap::round_to_bar (samplepos_t fr, RoundMode dir)
3916 return round_to_type (fr, dir, Bar);
3920 TempoMap::round_to_beat (samplepos_t fr, RoundMode dir)
3922 return round_to_type (fr, dir, Beat);
3926 TempoMap::round_to_quarter_note_subdivision (samplepos_t fr, int sub_num, RoundMode dir)
3928 Glib::Threads::RWLock::ReaderLock lm (lock);
3929 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);
3930 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3931 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3933 ticks -= beats * BBT_Time::ticks_per_beat;
3936 /* round to next (or same iff dir == RoundUpMaybe) */
3938 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3940 if (mod == 0 && dir == RoundUpMaybe) {
3941 /* right on the subdivision, which is fine, so do nothing */
3943 } else if (mod == 0) {
3944 /* right on the subdivision, so the difference is just the subdivision ticks */
3945 ticks += ticks_one_subdivisions_worth;
3948 /* not on subdivision, compute distance to next subdivision */
3950 ticks += ticks_one_subdivisions_worth - mod;
3953 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3954 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3955 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3956 // But I'm keeping it around, until we determine there are no terrible consequences.
3957 // if (ticks >= BBT_Time::ticks_per_beat) {
3958 // ticks -= BBT_Time::ticks_per_beat;
3961 } else if (dir < 0) {
3963 /* round to previous (or same iff dir == RoundDownMaybe) */
3965 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3967 if (difference == 0 && dir == RoundDownAlways) {
3968 /* right on the subdivision, but force-rounding down,
3969 so the difference is just the subdivision ticks */
3970 difference = ticks_one_subdivisions_worth;
3973 if (ticks < difference) {
3974 ticks = BBT_Time::ticks_per_beat - ticks;
3976 ticks -= difference;
3980 /* round to nearest */
3983 /* compute the distance to the previous and next subdivision */
3985 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3987 /* closer to the next subdivision, so shift forward */
3989 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3991 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3993 if (ticks > BBT_Time::ticks_per_beat) {
3995 ticks -= BBT_Time::ticks_per_beat;
3996 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3999 } else if (rem > 0) {
4001 /* closer to previous subdivision, so shift backward */
4005 /* can't go backwards past zero, so ... */
4006 return MusicSample (0, 0);
4008 /* step back to previous beat */
4010 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4011 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4013 ticks = lrint (ticks - rem);
4014 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4017 /* on the subdivision, do nothing */
4021 MusicSample ret (0, 0);
4022 ret.sample = sample_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4023 ret.division = sub_num;
4029 TempoMap::round_to_type (samplepos_t sample, RoundMode dir, BBTPointType type)
4031 Glib::Threads::RWLock::ReaderLock lm (lock);
4032 const double minute = minute_at_sample (sample);
4033 const double beat_at_samplepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4034 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_samplepos));
4035 MusicSample ret (0, 0);
4042 /* find bar previous to 'sample' */
4048 ret.sample = sample_at_minute (minute_at_bbt_locked (_metrics, bbt));
4052 } else if (dir > 0) {
4053 /* find bar following 'sample' */
4058 ret.sample = sample_at_minute (minute_at_bbt_locked (_metrics, bbt));
4062 /* true rounding: find nearest bar */
4063 samplepos_t raw_ft = sample_at_minute (minute_at_bbt_locked (_metrics, bbt));
4066 samplepos_t prev_ft = sample_at_minute (minute_at_bbt_locked (_metrics, bbt));
4068 samplepos_t next_ft = sample_at_minute (minute_at_bbt_locked (_metrics, bbt));
4070 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4071 ret.sample = next_ft;
4076 ret.sample = prev_ft;
4088 ret.sample = sample_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_samplepos)));
4091 } else if (dir > 0) {
4092 ret.sample = sample_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_samplepos)));
4096 ret.sample = sample_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_samplepos + 0.5)));
4103 return MusicSample (0, 0);
4107 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4108 samplepos_t lower, samplepos_t upper, uint32_t bar_mod)
4110 Glib::Threads::RWLock::ReaderLock lm (lock);
4111 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_sample (lower)));
4112 samplecnt_t pos = 0;
4113 /* although the map handles negative beats, bbt doesn't. */
4118 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_sample (upper)) {
4122 while (pos >= 0 && pos < upper) {
4123 pos = sample_at_minute (minute_at_beat_locked (_metrics, cnt));
4124 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_sample (pos));
4125 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4126 const double qn = pulse_at_beat_locked (_metrics, cnt) * 4.0;
4128 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_sample (pos)), pos, bbt.bars, bbt.beats, qn));
4132 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_sample (lower));
4137 bbt.bars -= bbt.bars % bar_mod;
4141 while (pos >= 0 && pos < upper) {
4142 pos = sample_at_minute (minute_at_bbt_locked (_metrics, bbt));
4143 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_sample (pos));
4144 const double qn = pulse_at_bbt_locked (_metrics, bbt) * 4.0;
4146 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_sample (pos)), pos, bbt.bars, bbt.beats, qn));
4147 bbt.bars += bar_mod;
4153 TempoMap::tempo_section_at_sample (samplepos_t sample) const
4155 Glib::Threads::RWLock::ReaderLock lm (lock);
4157 return tempo_section_at_minute_locked (_metrics, minute_at_sample (sample));
4161 TempoMap::tempo_section_at_sample (samplepos_t sample)
4163 Glib::Threads::RWLock::ReaderLock lm (lock);
4165 return tempo_section_at_minute_locked (_metrics, minute_at_sample (sample));
4169 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4171 TempoSection* prev = 0;
4175 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4177 if ((*i)->is_tempo()) {
4178 t = static_cast<TempoSection*> (*i);
4182 if (prev && t->minute() > minute) {
4192 abort(); /*NOTREACHED*/
4198 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4200 TempoSection* prev = 0;
4204 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4206 if ((*i)->is_tempo()) {
4207 t = static_cast<TempoSection*> (*i);
4211 if (prev && t->minute() > minute) {
4221 abort(); /*NOTREACHED*/
4227 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4229 TempoSection* prev_t = 0;
4230 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4234 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4235 if ((*i)->is_tempo()) {
4236 t = static_cast<TempoSection*> (*i);
4242 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4253 TempoMap::previous_tempo_section (TempoSection* ts) const
4255 Glib::Threads::RWLock::ReaderLock lm (lock);
4257 return previous_tempo_section_locked (_metrics, ts);
4262 TempoMap::previous_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4268 TempoSection* prev = 0;
4270 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4272 if ((*i)->is_tempo()) {
4273 TempoSection* t = static_cast<TempoSection*> (*i);
4279 if (prev && t == ts) {
4290 abort(); /*NOTREACHED*/
4297 TempoMap::next_tempo_section (TempoSection* ts) const
4299 Glib::Threads::RWLock::ReaderLock lm (lock);
4301 return next_tempo_section_locked (_metrics, ts);
4305 TempoMap::next_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4311 TempoSection* prev = 0;
4313 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4315 if ((*i)->is_tempo()) {
4316 TempoSection* t = static_cast<TempoSection*> (*i);
4322 if (prev && prev == ts) {
4333 abort(); /*NOTREACHED*/
4338 /* don't use this to calculate length (the tempo is only correct for this sample).
4339 do that stuff based on the beat_at_sample and sample_at_beat api
4342 TempoMap::samples_per_quarter_note_at (const samplepos_t sample, const samplecnt_t sr) const
4344 Glib::Threads::RWLock::ReaderLock lm (lock);
4346 const TempoSection* ts_at = 0;
4347 const TempoSection* ts_after = 0;
4348 Metrics::const_iterator i;
4351 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4353 if ((*i)->is_tempo()) {
4354 t = static_cast<TempoSection*> (*i);
4358 if (ts_at && (*i)->sample() > sample) {
4368 return (60.0 * _sample_rate) / ts_at->tempo_at_minute (minute_at_sample (sample)).quarter_notes_per_minute();
4370 /* must be treated as constant tempo */
4371 return ts_at->samples_per_quarter_note (_sample_rate);
4375 TempoMap::meter_section_at_sample (samplepos_t sample) const
4377 Glib::Threads::RWLock::ReaderLock lm (lock);
4378 return meter_section_at_minute_locked (_metrics, minute_at_sample (sample));
4382 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4384 Metrics::const_iterator i;
4385 MeterSection* prev = 0;
4389 for (i = metrics.begin(); i != metrics.end(); ++i) {
4391 if (!(*i)->is_tempo()) {
4392 m = static_cast<MeterSection*> (*i);
4394 if (prev && (*i)->minute() > minute) {
4404 abort(); /*NOTREACHED*/
4411 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4413 MeterSection* prev_m = 0;
4415 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4417 if (!(*i)->is_tempo()) {
4418 m = static_cast<MeterSection*> (*i);
4419 if (prev_m && m->beat() > beat) {
4430 TempoMap::meter_section_at_beat (double beat) const
4432 Glib::Threads::RWLock::ReaderLock lm (lock);
4433 return meter_section_at_beat_locked (_metrics, beat);
4437 TempoMap::meter_at_sample (samplepos_t sample) const
4439 TempoMetric m (metric_at (sample));
4444 TempoMap::fix_legacy_session ()
4446 MeterSection* prev_m = 0;
4447 TempoSection* prev_t = 0;
4448 bool have_initial_t = false;
4450 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4454 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4456 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4459 m->set_minute (0.0);
4460 m->set_position_lock_style (AudioTime);
4465 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4466 + (m->bbt().beats - 1)
4467 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4469 m->set_beat (start);
4470 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4471 + (m->bbt().beats - 1)
4472 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4473 m->set_pulse (start_beat / prev_m->note_divisor());
4476 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4481 /* Ramp type never existed in the era of this tempo section */
4482 t->set_end_note_types_per_minute (t->note_types_per_minute());
4486 t->set_minute (0.0);
4487 t->set_position_lock_style (AudioTime);
4489 have_initial_t = true;
4494 /* some 4.x sessions have no initial (non-movable) tempo. */
4495 if (!have_initial_t) {
4496 prev_t->set_pulse (0.0);
4497 prev_t->set_minute (0.0);
4498 prev_t->set_position_lock_style (AudioTime);
4499 prev_t->set_initial (true);
4500 prev_t->set_locked_to_meter (true);
4501 have_initial_t = true;
4504 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4505 + (t->legacy_bbt().beats - 1)
4506 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4508 t->set_pulse (beat / prev_m->note_divisor());
4510 /* really shouldn't happen but.. */
4511 t->set_pulse (beat / 4.0);
4519 TempoMap::fix_legacy_end_session ()
4521 TempoSection* prev_t = 0;
4523 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4526 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4533 if (prev_t->end_note_types_per_minute() < 0.0) {
4534 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4543 prev_t->set_end_note_types_per_minute (prev_t->note_types_per_minute());
4548 TempoMap::get_state ()
4550 Metrics::const_iterator i;
4551 XMLNode *root = new XMLNode ("TempoMap");
4554 Glib::Threads::RWLock::ReaderLock lm (lock);
4555 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4556 root->add_child_nocopy ((*i)->get_state());
4564 TempoMap::set_state (const XMLNode& node, int /*version*/)
4567 Glib::Threads::RWLock::WriterLock lm (lock);
4570 XMLNodeConstIterator niter;
4571 Metrics old_metrics (_metrics);
4574 nlist = node.children();
4576 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4577 XMLNode* child = *niter;
4579 if (child->name() == TempoSection::xml_state_node_name) {
4582 TempoSection* ts = new TempoSection (*child, _sample_rate);
4583 _metrics.push_back (ts);
4586 catch (failed_constructor& err){
4587 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4588 _metrics = old_metrics;
4589 old_metrics.clear();
4593 } else if (child->name() == MeterSection::xml_state_node_name) {
4596 MeterSection* ms = new MeterSection (*child, _sample_rate);
4597 _metrics.push_back (ms);
4600 catch (failed_constructor& err) {
4601 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4602 _metrics = old_metrics;
4603 old_metrics.clear();
4609 /* check for legacy sessions where bbt was the base musical unit for tempo */
4610 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4612 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4613 if (t->legacy_bbt().bars != 0) {
4614 fix_legacy_session();
4618 if (t->end_note_types_per_minute() < 0.0) {
4619 fix_legacy_end_session();
4625 if (niter == nlist.end()) {
4626 MetricSectionSorter cmp;
4627 _metrics.sort (cmp);
4630 /* check for multiple tempo/meters at the same location, which
4631 ardour2 somehow allowed.
4634 Metrics::iterator prev = _metrics.end();
4635 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4636 if (prev != _metrics.end()) {
4638 MeterSection* prev_m;
4640 TempoSection* prev_t;
4641 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4642 if (prev_m->beat() == ms->beat()) {
4643 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
4644 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
4647 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4648 if (prev_t->pulse() == ts->pulse()) {
4649 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4650 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4658 recompute_map (_metrics);
4660 Metrics::const_iterator d = old_metrics.begin();
4661 while (d != old_metrics.end()) {
4665 old_metrics.clear ();
4668 PropertyChanged (PropertyChange ());
4674 TempoMap::dump (std::ostream& o) const
4676 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4677 const MeterSection* m;
4678 const TempoSection* t;
4679 const TempoSection* prev_t = 0;
4681 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4683 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4684 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4685 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4686 << " minute= " << t->minute() << " sample= " << t->sample() << " (initial? " << t->initial() << ')'
4687 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4689 o << " current start : " << t->note_types_per_minute()
4690 << " current end : " << t->end_note_types_per_minute()
4691 << " | " << t->pulse() << " | " << t->sample() << " | " << t->minute() << std::endl;
4692 o << " previous : " << prev_t->note_types_per_minute()
4693 << " | " << prev_t->pulse() << " | " << prev_t->sample() << " | " << prev_t->minute() << std::endl;
4694 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4695 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4696 << " | " << sample_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4697 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4700 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4701 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4702 << " sample= " << m->sample() << " pulse: " << m->pulse() << " beat : " << m->beat()
4703 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4706 o << "------" << std::endl;
4710 TempoMap::n_tempos() const
4712 Glib::Threads::RWLock::ReaderLock lm (lock);
4715 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4716 if ((*i)->is_tempo()) {
4725 TempoMap::n_meters() const
4727 Glib::Threads::RWLock::ReaderLock lm (lock);
4730 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4731 if (!(*i)->is_tempo()) {
4740 TempoMap::insert_time (samplepos_t where, samplecnt_t amount)
4742 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4743 if ((*i)->sample() >= where && !(*i)->initial ()) {
4747 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4748 gui_set_meter_position (ms, (*i)->sample() + amount);
4751 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4752 gui_set_tempo_position (ts, (*i)->sample() + amount, 0);
4757 PropertyChanged (PropertyChange ());
4761 TempoMap::remove_time (samplepos_t where, samplecnt_t amount)
4765 std::list<MetricSection*> metric_kill_list;
4767 TempoSection* last_tempo = NULL;
4768 MeterSection* last_meter = NULL;
4769 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4770 bool meter_after = false; // is there a meter marker likewise?
4772 Glib::Threads::RWLock::WriterLock lm (lock);
4773 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4774 if ((*i)->sample() >= where && (*i)->sample() < where+amount) {
4775 metric_kill_list.push_back(*i);
4776 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4779 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4783 else if ((*i)->sample() >= where) {
4784 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4785 (*i)->set_minute ((*i)->minute() - minute_at_sample (amount));
4786 if ((*i)->sample() == where) {
4787 // marker was immediately after end of range
4788 tempo_after = dynamic_cast<TempoSection*> (*i);
4789 meter_after = dynamic_cast<MeterSection*> (*i);
4795 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4796 if (last_tempo && !tempo_after) {
4797 metric_kill_list.remove(last_tempo);
4798 last_tempo->set_minute (minute_at_sample (where));
4801 if (last_meter && !meter_after) {
4802 metric_kill_list.remove(last_meter);
4803 last_meter->set_minute (minute_at_sample (where));
4807 //remove all the remaining metrics
4808 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4809 _metrics.remove(*i);
4814 recompute_map (_metrics);
4817 PropertyChanged (PropertyChange ());
4821 /** Add some (fractional) Beats to a session sample position, and return the result in samples.
4822 * pos can be -ve, if required.
4825 TempoMap::samplepos_plus_qn (samplepos_t sample, Temporal::Beats beats) const
4827 Glib::Threads::RWLock::ReaderLock lm (lock);
4828 const double sample_qn = pulse_at_minute_locked (_metrics, minute_at_sample (sample)) * 4.0;
4830 return sample_at_minute (minute_at_pulse_locked (_metrics, (sample_qn + beats.to_double()) / 4.0));
4834 TempoMap::samplepos_plus_bbt (samplepos_t pos, BBT_Time op) const
4836 Glib::Threads::RWLock::ReaderLock lm (lock);
4838 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_sample (pos)));
4839 pos_bbt.ticks += op.ticks;
4840 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4842 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4844 pos_bbt.beats += op.beats;
4845 /* the meter in effect will start on the bar */
4846 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();
4847 while (pos_bbt.beats >= divisions_per_bar + 1) {
4849 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4850 pos_bbt.beats -= divisions_per_bar;
4852 pos_bbt.bars += op.bars;
4854 return sample_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4857 /** Count the number of beats that are equivalent to distance when going forward,
4861 TempoMap::framewalk_to_qn (samplepos_t pos, samplecnt_t distance) const
4863 Glib::Threads::RWLock::ReaderLock lm (lock);
4865 return Temporal::Beats (quarter_notes_between_samples_locked (_metrics, pos, pos + distance));
4869 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4875 operator<< (std::ostream& o, const Meter& m) {
4876 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4880 operator<< (std::ostream& o, const Tempo& t) {
4881 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4885 operator<< (std::ostream& o, const MetricSection& section) {
4887 o << "MetricSection @ " << section.sample() << ' ';
4889 const TempoSection* ts;
4890 const MeterSection* ms;
4892 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4893 o << *((const Tempo*) ts);
4894 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4895 o << *((const Meter*) ms);