2 * Copyright (C) 2000-2017 Paul Davis <paul@linuxaudiosystems.com>
3 * Copyright (C) 2005-2009 Taybin Rutkin <taybin@taybin.com>
4 * Copyright (C) 2008-2015 David Robillard <d@drobilla.net>
5 * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
6 * Copyright (C) 2014-2019 Robin Gareus <robin@gareus.org>
7 * Copyright (C) 2015-2016 Ben Loftis <ben@harrisonconsoles.com>
8 * Copyright (C) 2015-2017 Nick Mainsbridge <mainsbridge@gmail.com>
9 * Copyright (C) 2015 Colin Fletcher <colin.m.fletcher@googlemail.com>
10 * Copyright (C) 2016 Tim Mayberry <mojofunk@gmail.com>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License along
23 * with this program; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
33 #include <glibmm/threads.h>
35 #include "pbd/enumwriter.h"
36 #include "pbd/xml++.h"
38 #include "temporal/beats.h"
40 #include "ardour/debug.h"
41 #include "ardour/lmath.h"
42 #include "ardour/tempo.h"
43 #include "ardour/types_convert.h"
49 using namespace ARDOUR;
52 using Timecode::BBT_Time;
54 /* _default tempo is 4/4 qtr=120 */
56 Meter TempoMap::_default_meter (4.0, 4.0);
57 Tempo TempoMap::_default_tempo (120.0, 4.0, 120.0);
60 MetricSection::sample_at_minute (const double& time) const
62 return (samplepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
66 MetricSection::minute_at_sample (const samplepos_t sample) const
68 return (sample / (double) _sample_rate) / 60.0;
71 /***********************************************************************/
74 ARDOUR::bbt_time_to_string (const BBT_Time& bbt, std::string& str)
77 int retval = snprintf (buf, sizeof(buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, bbt.bars, bbt.beats,
80 if (retval <= 0 || retval >= (int)sizeof(buf)) {
89 ARDOUR::string_to_bbt_time (const std::string& str, BBT_Time& bbt)
91 if (sscanf (str.c_str (), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, &bbt.bars, &bbt.beats,
99 /***********************************************************************/
102 Meter::samples_per_grid (const Tempo& tempo, samplecnt_t sr) const
104 /* This is tempo- and meter-sensitive. The number it returns
105 is based on the interval between any two lines in the
106 grid that is constructed from tempo and meter sections.
108 The return value IS NOT interpretable in terms of "beats".
111 return (60.0 * sr) / (tempo.note_types_per_minute() * (_note_type / tempo.note_type()));
115 Meter::samples_per_bar (const Tempo& tempo, samplecnt_t sr) const
117 return samples_per_grid (tempo, sr) * _divisions_per_bar;
120 /***********************************************************************/
123 MetricSection::add_state_to_node(XMLNode& node) const
125 node.set_property ("pulse", _pulse);
126 node.set_property ("frame", sample());
127 node.set_property ("movable", !_initial);
128 node.set_property ("lock-style", _position_lock_style);
132 MetricSection::set_state (const XMLNode& node, int /*version*/)
134 node.get_property ("pulse", _pulse);
137 if (node.get_property ("frame", sample)) {
138 set_minute (minute_at_sample (sample));
142 if (!node.get_property ("movable", tmp)) {
143 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
144 throw failed_constructor();
148 if (!node.get_property ("lock-style", _position_lock_style)) {
150 _position_lock_style = MusicTime;
152 _position_lock_style = AudioTime;
158 /***********************************************************************/
160 const string TempoSection::xml_state_node_name = "Tempo";
162 TempoSection::TempoSection (const XMLNode& node, samplecnt_t sample_rate)
163 : MetricSection (0.0, 0, MusicTime, true, sample_rate)
164 , Tempo (TempoMap::default_tempo())
167 , _locked_to_meter (false)
171 std::string start_bbt;
172 _legacy_bbt.bars = 0; // legacy session check compars .bars != 0; default BBT_Time c'tor uses 1.
173 if (node.get_property ("start", start_bbt)) {
174 if (string_to_bbt_time (start_bbt, bbt)) {
175 /* legacy session - start used to be in bbt*/
178 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
182 // Don't worry about return value, exception will be thrown on error
183 MetricSection::set_state (node, Stateful::loading_state_version);
185 if (node.get_property ("beats-per-minute", _note_types_per_minute)) {
186 if (_note_types_per_minute < 0.0) {
187 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
188 throw failed_constructor();
192 if (node.get_property ("note-type", _note_type)) {
193 if (_note_type < 1.0) {
194 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
195 throw failed_constructor();
198 /* older session, make note type be quarter by default */
202 if (!node.get_property ("clamped", _clamped)) {
206 if (node.get_property ("end-beats-per-minute", _end_note_types_per_minute)) {
207 if (_end_note_types_per_minute < 0.0) {
208 info << _("TempoSection XML node has an illegal \"end-beats-per-minute\" value") << endmsg;
209 throw failed_constructor();
213 TempoSection::Type old_type;
214 if (node.get_property ("tempo-type", old_type)) {
215 /* sessions with a tempo-type node contain no end-beats-per-minute.
216 if the legacy node indicates a constant tempo, simply fill this in with the
217 start tempo. otherwise we need the next neighbour to know what it will be.
220 if (old_type == TempoSection::Constant) {
221 _end_note_types_per_minute = _note_types_per_minute;
223 _end_note_types_per_minute = -1.0;
227 if (!node.get_property ("active", _active)) {
228 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
232 if (!node.get_property ("locked-to-meter", _locked_to_meter)) {
234 set_locked_to_meter (true);
236 set_locked_to_meter (false);
240 /* 5.5 marked initial tempo as not locked to meter. this should always be true anyway */
242 set_locked_to_meter (true);
247 TempoSection::get_state() const
249 XMLNode *root = new XMLNode (xml_state_node_name);
251 MetricSection::add_state_to_node (*root);
253 root->set_property ("beats-per-minute", _note_types_per_minute);
254 root->set_property ("note-type", _note_type);
255 root->set_property ("clamped", _clamped);
256 root->set_property ("end-beats-per-minute", _end_note_types_per_minute);
257 root->set_property ("active", _active);
258 root->set_property ("locked-to-meter", _locked_to_meter);
263 /** returns the Tempo at the session-relative minute.
266 TempoSection::tempo_at_minute (const double& m) const
268 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
270 return Tempo (note_types_per_minute(), note_type());
273 return Tempo (_tempo_at_time (m - minute()), _note_type, _end_note_types_per_minute);
276 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
277 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
278 * @param p the pulse used to calculate the returned minute for constant tempi
279 * @return the minute at the supplied tempo
281 * note that the note_type is currently ignored in this function. see below.
285 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
286 * there should be no ramp between the two even if we are ramped.
287 * in other words a ramp should only place a curve on note_types_per_minute.
288 * we should be able to use Tempo note type here, but the above
289 * complicates things a bit.
292 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
294 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
296 return ((p - pulse()) / pulses_per_minute()) + minute();
299 return _time_at_tempo (ntpm) + minute();
302 /** returns the Tempo at the supplied whole-note pulse.
305 TempoSection::tempo_at_pulse (const double& p) const
307 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
310 return Tempo (note_types_per_minute(), note_type());
313 return Tempo (_tempo_at_pulse (p - pulse()), _note_type, _end_note_types_per_minute);
316 /** returns the whole-note pulse where a tempo in note types per minute occurs.
317 * constant tempi require minute m.
318 * @param ntpm the note types per minute value used to calculate the returned pulse
319 * @param m the minute used to calculate the returned pulse if the tempo is constant
320 * @return the whole-note pulse at the supplied tempo
322 * note that note_type is currently ignored in this function. see minute_at_tempo().
324 * for constant tempi, this is anaologous to pulse_at_minute().
327 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
329 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
331 return ((m - minute()) * pulses_per_minute()) + pulse();
334 return _pulse_at_tempo (ntpm) + pulse();
337 /** returns the whole-note pulse at the supplied session-relative minute.
340 TempoSection::pulse_at_minute (const double& m) const
342 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
344 return ((m - minute()) * pulses_per_minute()) + pulse();
347 return _pulse_at_time (m - minute()) + pulse();
350 /** returns the session-relative minute at the supplied whole-note pulse.
353 TempoSection::minute_at_pulse (const double& p) const
355 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
357 return ((p - pulse()) / pulses_per_minute()) + minute();
360 return _time_at_pulse (p - pulse()) + minute();
363 /** returns thw whole-note pulse at session sample position f.
364 * @param f the sample position.
365 * @return the position in whole-note pulses corresponding to f
367 * for use with musical units whose granularity is coarser than samples (e.g. ticks)
370 TempoSection::pulse_at_sample (const samplepos_t f) const
372 const bool constant = type() == Constant || _c == 0.0 || (initial() && f < sample());
374 return (minute_at_sample (f - sample()) * pulses_per_minute()) + pulse();
377 return _pulse_at_time (minute_at_sample (f - sample())) + pulse();
381 TempoSection::sample_at_pulse (const double& p) const
383 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
385 return sample_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
388 return sample_at_minute (_time_at_pulse (p - pulse()) + minute());
396 Tt----|-----------------*|
397 Ta----|--------------|* |
403 _______________|___|____
404 time a t (next tempo)
407 Duration in beats at time a is the integral of some Tempo function.
408 In our case, the Tempo function (Tempo at time t) is
411 with function constant
416 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
417 b(t) = T0(e^(ct) - 1) / c
419 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
420 t(b) = log((c.b / T0) + 1) / c
422 The time t at which Tempo T occurs is a as above:
423 t(T) = log(T / T0) / c
425 The beat at which a Tempo T occurs is:
428 The Tempo at which beat b occurs is:
431 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
432 Our problem is that we usually don't know t.
433 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
434 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
435 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
437 By substituting our expanded t as a in the c function above, our problem is reduced to:
438 c = T0 (e^(log (Ta / T0)) - 1) / b
440 Of course the word 'beat' has been left loosely defined above.
441 In music, a beat is defined by the musical pulse (which comes from the tempo)
442 and the meter in use at a particular time (how many pulse divisions there are in one bar).
443 It would be more accurate to substitute the work 'pulse' for 'beat' above.
447 We can now store c for future time calculations.
448 If the following tempo section (the one that defines c in conjunction with this one)
449 is changed or moved, c is no longer valid.
451 The public methods are session-relative.
453 Most of this stuff is taken from this paper:
456 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
459 Zurich University of Arts
460 Institute for Computer Music and Sound Technology
462 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
466 /** compute this ramp's function constant from some tempo-pulse point
467 * @param end_npm end tempo (in note types per minute)
468 * @param end_pulse duration (pulses into global start) of some other position.
469 * @return the calculated function constant
472 TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const
474 if (note_types_per_minute() == end_npm || type() == Constant) {
478 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
479 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
482 /** compute the function constant from some tempo-time point.
483 * @param end_npm tempo (note types/min.)
484 * @param end_minute distance (in minutes) from session origin
485 * @return the calculated function constant
488 TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const
490 if (note_types_per_minute() == end_npm || type() == Constant) {
494 return c_func (end_npm, end_minute - minute());
497 /* position function */
499 TempoSection::a_func (double end_npm, double c) const
501 return log (end_npm / note_types_per_minute()) / c;
504 /*function constant*/
506 TempoSection::c_func (double end_npm, double end_time) const
508 return log (end_npm / note_types_per_minute()) / end_time;
511 /* tempo in note types per minute at time in minutes */
513 TempoSection::_tempo_at_time (const double& time) const
515 return exp (_c * time) * note_types_per_minute();
518 /* time in minutes at tempo in note types per minute */
520 TempoSection::_time_at_tempo (const double& npm) const
522 return log (npm / note_types_per_minute()) / _c;
525 /* pulse at tempo in note types per minute */
527 TempoSection::_pulse_at_tempo (const double& npm) const
529 return ((npm - note_types_per_minute()) / _c) / _note_type;
532 /* tempo in note types per minute at pulse */
534 TempoSection::_tempo_at_pulse (const double& pulse) const
536 return (pulse * _note_type * _c) + note_types_per_minute();
539 /* pulse at time in minutes */
541 TempoSection::_pulse_at_time (const double& time) const
543 return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
546 /* time in minutes at pulse */
548 TempoSection::_time_at_pulse (const double& pulse) const
550 return log1p ((_c * pulse * _note_type) / note_types_per_minute()) / _c;
553 /***********************************************************************/
555 const string MeterSection::xml_state_node_name = "Meter";
557 MeterSection::MeterSection (const XMLNode& node, const samplecnt_t sample_rate)
558 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
560 pair<double, BBT_Time> start;
564 if (node.get_property ("start", bbt_str)) {
565 if (string_to_bbt_time (bbt_str, start.second)) {
566 /* legacy session - start used to be in bbt*/
567 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
570 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
574 MetricSection::set_state (node, Stateful::loading_state_version);
576 node.get_property ("beat", start.first);
578 if (node.get_property ("bbt", bbt_str)) {
579 if (!string_to_bbt_time (bbt_str, start.second)) {
580 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
581 throw failed_constructor();
584 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
589 /* beats-per-bar is old; divisions-per-bar is new */
591 if (!node.get_property ("divisions-per-bar", _divisions_per_bar)) {
592 if (!node.get_property ("beats-per-bar", _divisions_per_bar)) {
593 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
594 throw failed_constructor();
598 if (_divisions_per_bar < 0.0) {
599 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
600 throw failed_constructor();
603 if (!node.get_property ("note-type", _note_type)) {
604 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
605 throw failed_constructor();
608 if (_note_type < 0.0) {
609 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
610 throw failed_constructor();
615 MeterSection::get_state() const
617 XMLNode *root = new XMLNode (xml_state_node_name);
619 MetricSection::add_state_to_node (*root);
622 bbt_time_to_string (_bbt, bbt_str);
623 root->set_property ("bbt", bbt_str);
624 root->set_property ("beat", beat());
625 root->set_property ("note-type", _note_type);
626 root->set_property ("divisions-per-bar", _divisions_per_bar);
631 /***********************************************************************/
635 Tempo determines the rate of musical pulse determined by its components
636 note types per minute - the rate per minute of the whole note divisor _note_type
637 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
638 Meter divides the musical pulse into measures and beats according to its components
642 TempoSection - translates between time, musical pulse and tempo.
643 has a musical location in whole notes (pulses).
644 has a time location in minutes.
645 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
646 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
648 MeterSection - translates between BBT, meter-based beat and musical pulse.
649 has a musical location in whole notes (pulses)
650 has a musical location in meter-based beats
651 has a musical location in BBT time
652 has a time location expressed in minutes.
654 TempoSection and MeterSection may be locked to either audio or music (position lock style).
655 The lock style determines the location type to be kept as a reference when location is recalculated.
657 The first tempo and meter are special. they must move together, and are locked to audio.
658 Audio locked tempi which lie before the first meter are made inactive.
660 Recomputing the map is the process where the 'missing' location types are calculated.
661 We construct the tempo map by first using the locked location type of each section
662 to determine non-locked location types (pulse or minute position).
663 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
665 Having done this, we can now traverse the Metrics list by pulse or minute
666 to query its relevant meter/tempo.
668 It is important to keep the _metrics in an order that makes sense.
669 Because ramped MusicTime and AudioTime tempos can interact with each other,
670 reordering is frequent. Care must be taken to keep _metrics in a solved state.
671 Solved means ordered by sample or pulse with sample-accurate precision (see check_solved()).
675 Music and audio-locked objects may seem interchangeable on the surface, but when translating
676 between audio samples and beat, remember that a sample is only a quantised approximation
677 of the actual time (in minutes) of a beat.
678 Thus if a gui user points to the sample occupying the start of a music-locked object on 1|3|0, it does not
679 mean that this sample is the actual location in time of 1|3|0.
681 You cannot use a sample measurement to determine beat distance except under special circumstances
682 (e.g. where the user has requested that a beat lie on a SMPTE sample or if the tempo is known to be constant over the duration).
684 This means is that a user operating on a musical grid must supply the desired beat position and/or current beat quantization in order for the
685 sample space the user is operating at to be translated correctly to the object.
687 The current approach is to interpret the supplied sample using the grid division the user has currently selected.
688 If the user has no musical grid set, they are actually operating in sample space (even SMPTE samples are rounded to audio sample), so
689 the supplied audio sample is interpreted as the desired musical location (beat_at_sample()).
691 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
693 When sample_at_beat() is called, the position calculation is performed in pulses and minutes.
694 The result is rounded to audio samples.
695 When beat_at_sample() is called, the sample is converted to minutes, with no rounding performed on the result.
698 sample_at_beat (beat_at_sample (sample)) == sample
700 beat_at_sample (sample_at_beat (beat)) != beat due to the time quantization of sample_at_beat().
702 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
703 samples_between_quarter_notes () eliminats this effect when determining time duration
704 from Beats distance, or instead work in quarter-notes and/or beats and convert to samples last.
706 The above pointless example could instead do:
707 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
709 The Shaggs - Things I Wonder
710 https://www.youtube.com/watch?v=9wQK6zMJOoQ
713 struct MetricSectionSorter {
714 bool operator() (const MetricSection* a, const MetricSection* b) {
715 return a->pulse() < b->pulse();
719 struct MetricSectionFrameSorter {
720 bool operator() (const MetricSection* a, const MetricSection* b) {
721 return a->sample() < b->sample();
725 TempoMap::TempoMap (samplecnt_t fr)
728 BBT_Time start (1, 1, 0);
730 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo, AudioTime, fr);
731 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
733 t->set_initial (true);
734 t->set_locked_to_meter (true);
736 m->set_initial (true);
738 /* note: sample time is correct (zero) for both of these */
740 _metrics.push_back (t);
741 _metrics.push_back (m);
746 TempoMap::operator= (TempoMap const & other)
748 if (&other != this) {
749 Glib::Threads::RWLock::ReaderLock lr (other.lock);
750 Glib::Threads::RWLock::WriterLock lm (lock);
751 _sample_rate = other._sample_rate;
753 Metrics::const_iterator d = _metrics.begin();
754 while (d != _metrics.end()) {
760 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
761 TempoSection const * const ts = dynamic_cast<TempoSection const * const> (*m);
762 MeterSection const * const ms = dynamic_cast<MeterSection const * const> (*m);
765 TempoSection* new_section = new TempoSection (*ts);
766 _metrics.push_back (new_section);
768 MeterSection* new_section = new MeterSection (*ms);
769 _metrics.push_back (new_section);
774 PropertyChanged (PropertyChange());
779 TempoMap::~TempoMap ()
781 Metrics::const_iterator d = _metrics.begin();
782 while (d != _metrics.end()) {
790 TempoMap::sample_at_minute (const double time) const
792 return (samplepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
796 TempoMap::minute_at_sample (const samplepos_t sample) const
798 return (sample / (double) _sample_rate) / 60.0;
802 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
804 bool removed = false;
807 Glib::Threads::RWLock::WriterLock lm (lock);
808 if ((removed = remove_tempo_locked (tempo))) {
809 if (complete_operation) {
810 recompute_map (_metrics);
815 if (removed && complete_operation) {
816 PropertyChanged (PropertyChange ());
821 TempoMap::remove_tempo_locked (const TempoSection& tempo)
825 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
826 if (dynamic_cast<TempoSection*> (*i) != 0) {
827 if (tempo.sample() == (*i)->sample()) {
828 if (!(*i)->initial()) {
841 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
843 bool removed = false;
846 Glib::Threads::RWLock::WriterLock lm (lock);
847 if ((removed = remove_meter_locked (tempo))) {
848 if (complete_operation) {
849 recompute_map (_metrics);
854 if (removed && complete_operation) {
855 PropertyChanged (PropertyChange ());
860 TempoMap::remove_meter_locked (const MeterSection& meter)
863 if (meter.position_lock_style() == AudioTime) {
864 /* remove meter-locked tempo */
865 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
867 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
868 if (t->locked_to_meter() && meter.sample() == (*i)->sample()) {
877 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
878 if (dynamic_cast<MeterSection*> (*i) != 0) {
879 if (meter.sample() == (*i)->sample()) {
880 if (!(*i)->initial()) {
893 TempoMap::do_insert (MetricSection* section)
895 bool need_add = true;
896 /* we only allow new meters to be inserted on beat 1 of an existing
900 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
902 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
904 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
905 corrected.second.beats = 1;
906 corrected.second.ticks = 0;
907 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
908 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
909 m->bbt(), corrected.second) << endmsg;
910 //m->set_pulse (corrected);
914 /* Look for any existing MetricSection that is of the same type and
915 in the same bar as the new one, and remove it before adding
916 the new one. Note that this means that if we find a matching,
917 existing section, we can break out of the loop since we're
918 guaranteed that there is only one such match.
921 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
923 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
924 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
925 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
926 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
928 if (tempo && insert_tempo) {
931 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
932 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->sample() == insert_tempo->sample())) {
934 if (tempo->initial()) {
936 /* can't (re)move this section, so overwrite
937 * its data content (but not its properties as
941 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
942 (*i)->set_position_lock_style (AudioTime);
951 } else if (meter && insert_meter) {
955 bool const ipm = insert_meter->position_lock_style() == MusicTime;
957 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->sample() == insert_meter->sample())) {
959 if (meter->initial()) {
961 /* can't (re)move this section, so overwrite
962 * its data content (but not its properties as
966 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
967 (*i)->set_position_lock_style (AudioTime);
977 /* non-matching types, so we don't care */
981 /* Add the given MetricSection, if we didn't just reset an existing
986 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
987 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
991 TempoSection* prev_t = 0;
993 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
994 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
995 bool const ipm = insert_meter->position_lock_style() == MusicTime;
998 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->sample() > insert_meter->sample())) {
1002 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->sample() == insert_meter->sample())) {
1006 prev_t = dynamic_cast<TempoSection*> (*i);
1009 } else if (insert_tempo) {
1010 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1011 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1014 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1015 const bool lm = insert_tempo->locked_to_meter();
1016 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->sample() > insert_tempo->sample())
1017 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1024 _metrics.insert (i, section);
1028 /* user supplies the exact pulse if pls == MusicTime */
1030 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const samplepos_t sample, PositionLockStyle pls)
1032 if (tempo.note_types_per_minute() <= 0.0) {
1033 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1037 TempoSection* ts = 0;
1039 Glib::Threads::RWLock::WriterLock lm (lock);
1040 /* here we default to not clamped for a new tempo section. preference? */
1041 ts = add_tempo_locked (tempo, pulse, minute_at_sample (sample), pls, true, false, false);
1043 recompute_map (_metrics);
1046 PropertyChanged (PropertyChange ());
1052 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const samplepos_t sample, PositionLockStyle pls)
1054 if (tempo.note_types_per_minute() <= 0.0) {
1055 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1059 bool const locked_to_meter = ts.locked_to_meter();
1060 bool const ts_clamped = ts.clamped();
1061 TempoSection* new_ts = 0;
1064 Glib::Threads::RWLock::WriterLock lm (lock);
1065 TempoSection& first (first_tempo());
1066 if (!ts.initial()) {
1067 if (locked_to_meter) {
1069 /* cannot move a meter-locked tempo section */
1070 *static_cast<Tempo*>(&ts) = tempo;
1071 recompute_map (_metrics);
1074 remove_tempo_locked (ts);
1075 new_ts = add_tempo_locked (tempo, pulse, minute_at_sample (sample), pls, true, locked_to_meter, ts_clamped);
1076 /* enforce clampedness of next tempo section */
1077 TempoSection* next_t = next_tempo_section_locked (_metrics, new_ts);
1078 if (next_t && next_t->clamped()) {
1079 next_t->set_note_types_per_minute (new_ts->end_note_types_per_minute());
1084 first.set_pulse (0.0);
1085 first.set_minute (minute_at_sample (sample));
1086 first.set_position_lock_style (AudioTime);
1087 first.set_locked_to_meter (true);
1088 first.set_clamped (ts_clamped);
1090 /* cannot move the first tempo section */
1091 *static_cast<Tempo*>(&first) = tempo;
1094 recompute_map (_metrics);
1097 PropertyChanged (PropertyChange ());
1101 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1102 , PositionLockStyle pls, bool recompute, bool locked_to_meter, bool clamped)
1104 TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _sample_rate);
1105 t->set_locked_to_meter (locked_to_meter);
1106 t->set_clamped (clamped);
1110 TempoSection* prev_tempo = 0;
1111 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1112 TempoSection* const this_t = dynamic_cast<TempoSection*>(*i);
1115 if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1116 prev_tempo->set_end_note_types_per_minute (t->note_types_per_minute());
1120 prev_tempo = this_t;
1125 if (pls == AudioTime) {
1126 solve_map_minute (_metrics, t, t->minute());
1128 solve_map_pulse (_metrics, t, t->pulse());
1130 recompute_meters (_metrics);
1137 TempoMap::add_meter (const Meter& meter, const Timecode::BBT_Time& where, samplepos_t sample, PositionLockStyle pls)
1139 MeterSection* m = 0;
1141 Glib::Threads::RWLock::WriterLock lm (lock);
1142 m = add_meter_locked (meter, where, sample, pls, true);
1147 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1152 PropertyChanged (PropertyChange ());
1157 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, samplepos_t sample, PositionLockStyle pls)
1160 Glib::Threads::RWLock::WriterLock lm (lock);
1162 if (!ms.initial()) {
1163 remove_meter_locked (ms);
1164 add_meter_locked (meter, where, sample, pls, true);
1166 MeterSection& first (first_meter());
1167 TempoSection& first_t (first_tempo());
1168 /* cannot move the first meter section */
1169 *static_cast<Meter*>(&first) = meter;
1170 first.set_position_lock_style (AudioTime);
1171 first.set_pulse (0.0);
1172 first.set_minute (minute_at_sample (sample));
1173 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1174 first.set_beat (beat);
1175 first_t.set_minute (first.minute());
1176 first_t.set_locked_to_meter (true);
1177 first_t.set_pulse (0.0);
1178 first_t.set_position_lock_style (AudioTime);
1179 recompute_map (_metrics);
1183 PropertyChanged (PropertyChange ());
1187 TempoMap::add_meter_locked (const Meter& meter, const BBT_Time& bbt, samplepos_t sample, PositionLockStyle pls, bool recompute)
1189 double const minute_at_bbt = minute_at_bbt_locked (_metrics, bbt);
1190 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_bbt - minute_at_sample (1));
1191 double const pulse = ((bbt.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1192 /* the natural time of the BBT position */
1193 double const time_minutes = minute_at_pulse_locked (_metrics, pulse);
1195 if (pls == AudioTime) {
1196 /* add meter-locked tempo at the natural time in the current map (sample may differ). */
1197 Tempo const tempo_at_time = tempo_at_minute_locked (_metrics, time_minutes);
1198 TempoSection* mlt = add_tempo_locked (tempo_at_time, pulse, time_minutes, AudioTime, true, true, false);
1204 /* still using natural time for the position, ignoring lock style. */
1205 MeterSection* new_meter = new MeterSection (pulse, time_minutes, beat_at_bbt_locked (_metrics, bbt), bbt, meter.divisions_per_bar(), meter.note_divisor(), pls, _sample_rate);
1207 bool solved = false;
1209 do_insert (new_meter);
1213 if (pls == AudioTime) {
1214 /* now set the audio locked meter's position to sample */
1215 solved = solve_map_minute (_metrics, new_meter, minute_at_sample (sample));
1216 /* we failed, most likely due to some impossible sample requirement wrt audio-locked tempi.
1217 fudge sample so that the meter ends up at its BBT position instead.
1220 solved = solve_map_minute (_metrics, new_meter, minute_at_sample (prev_m.sample() + 1));
1223 solved = solve_map_bbt (_metrics, new_meter, bbt);
1224 /* required due to resetting the pulse of meter-locked tempi above.
1225 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1226 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1228 recompute_map (_metrics);
1232 if (!solved && recompute) {
1233 /* if this has failed to solve, there is little we can do other than to ensure that
1234 the new map is recalculated.
1236 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1237 recompute_map (_metrics);
1244 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1246 Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1249 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1250 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1255 Glib::Threads::RWLock::WriterLock lm (lock);
1256 *((Tempo*) t) = newtempo;
1257 recompute_map (_metrics);
1259 PropertyChanged (PropertyChange ());
1266 TempoMap::change_existing_tempo_at (samplepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1268 Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1271 TempoSection* first;
1272 Metrics::iterator i;
1274 /* find the TempoSection immediately preceding "where"
1277 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1279 if ((*i)->sample() > where) {
1285 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1298 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1308 Glib::Threads::RWLock::WriterLock lm (lock);
1309 /* cannot move the first tempo section */
1310 *((Tempo*)prev) = newtempo;
1311 recompute_map (_metrics);
1314 PropertyChanged (PropertyChange ());
1318 TempoMap::first_meter () const
1320 const MeterSection *m = 0;
1322 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1323 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1328 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1329 abort(); /*NOTREACHED*/
1334 TempoMap::first_meter ()
1336 MeterSection *m = 0;
1338 /* CALLER MUST HOLD LOCK */
1340 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1341 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1346 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1347 abort(); /*NOTREACHED*/
1352 TempoMap::first_tempo () const
1354 const TempoSection *t = 0;
1356 /* CALLER MUST HOLD LOCK */
1358 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1359 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1369 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1370 abort(); /*NOTREACHED*/
1375 TempoMap::first_tempo ()
1377 TempoSection *t = 0;
1379 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1380 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1390 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1391 abort(); /*NOTREACHED*/
1395 TempoMap::recompute_tempi (Metrics& metrics)
1397 TempoSection* prev_t = 0;
1399 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1402 if ((*i)->is_tempo()) {
1403 t = static_cast<TempoSection*> (*i);
1416 if (t->position_lock_style() == AudioTime) {
1417 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1418 if (!t->locked_to_meter()) {
1419 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1423 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1424 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1432 prev_t->set_c (0.0);
1435 /* tempos must be positioned correctly.
1436 * the current approach is to use a meter's bbt time as its base position unit.
1437 * an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1438 * while a music-locked meter requires recomputations of sample pulse and beat (but not bbt)
1441 TempoMap::recompute_meters (Metrics& metrics)
1443 MeterSection* meter = 0;
1444 MeterSection* prev_m = 0;
1446 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1447 if (!(*mi)->is_tempo()) {
1448 meter = static_cast<MeterSection*> (*mi);
1449 if (meter->position_lock_style() == AudioTime) {
1451 pair<double, BBT_Time> b_bbt;
1452 TempoSection* meter_locked_tempo = 0;
1453 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1455 if ((*ii)->is_tempo()) {
1456 t = static_cast<TempoSection*> (*ii);
1457 if (t->locked_to_meter() && t->sample() == meter->sample()) {
1458 meter_locked_tempo = t;
1465 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1466 if (beats + prev_m->beat() != meter->beat()) {
1467 /* reordering caused a bbt change */
1469 beats = meter->beat() - prev_m->beat();
1470 b_bbt = make_pair (beats + prev_m->beat()
1471 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1472 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1474 } else if (!meter->initial()) {
1475 b_bbt = make_pair (meter->beat(), meter->bbt());
1476 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1479 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1481 if (meter_locked_tempo) {
1482 meter_locked_tempo->set_pulse (pulse);
1484 meter->set_beat (b_bbt);
1485 meter->set_pulse (pulse);
1490 pair<double, BBT_Time> b_bbt;
1492 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1493 if (beats + prev_m->beat() != meter->beat()) {
1494 /* reordering caused a bbt change */
1495 b_bbt = make_pair (beats + prev_m->beat()
1496 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1498 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1500 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1502 /* shouldn't happen - the first is audio-locked */
1503 pulse = pulse_at_beat_locked (metrics, meter->beat());
1504 b_bbt = make_pair (meter->beat(), meter->bbt());
1507 meter->set_beat (b_bbt);
1508 meter->set_pulse (pulse);
1509 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1518 TempoMap::recompute_map (Metrics& metrics, samplepos_t end)
1520 /* CALLER MUST HOLD WRITE LOCK */
1522 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1525 /* silly call from Session::process() during startup
1530 recompute_tempi (metrics);
1531 recompute_meters (metrics);
1535 TempoMap::metric_at (samplepos_t sample, Metrics::const_iterator* last) const
1537 Glib::Threads::RWLock::ReaderLock lm (lock);
1538 TempoMetric m (first_meter(), first_tempo());
1541 *last = ++_metrics.begin();
1544 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1545 at something, because we insert the default tempo and meter during
1546 TempoMap construction.
1548 now see if we can find better candidates.
1551 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1553 if ((*i)->sample() > sample) {
1567 /* XX meters only */
1569 TempoMap::metric_at (BBT_Time bbt) const
1571 Glib::Threads::RWLock::ReaderLock lm (lock);
1572 TempoMetric m (first_meter(), first_tempo());
1574 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1575 at something, because we insert the default tempo and meter during
1576 TempoMap construction.
1578 now see if we can find better candidates.
1581 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1583 if (!(*i)->is_tempo()) {
1584 mw = static_cast<MeterSection*> (*i);
1585 BBT_Time section_start (mw->bbt());
1587 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1598 /** Returns the BBT (meter-based) beat corresponding to the supplied sample, possibly returning a negative value.
1599 * @param sample The session sample position.
1600 * @return The beat duration according to the tempo map at the supplied sample.
1602 * If the supplied sample lies before the first meter, the returned beat duration will be negative.
1603 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1605 * This function uses both tempo and meter.
1608 TempoMap::beat_at_sample (const samplecnt_t sample) const
1610 Glib::Threads::RWLock::ReaderLock lm (lock);
1612 return beat_at_minute_locked (_metrics, minute_at_sample (sample));
1615 /* This function uses both tempo and meter.*/
1617 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1619 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1620 MeterSection* prev_m = 0;
1621 MeterSection* next_m = 0;
1623 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1624 if (!(*i)->is_tempo()) {
1625 if (prev_m && (*i)->minute() > minute) {
1626 next_m = static_cast<MeterSection*> (*i);
1629 prev_m = static_cast<MeterSection*> (*i);
1635 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1637 /* audio locked meters fake their beat */
1638 if (next_m && next_m->beat() < beat) {
1639 return next_m->beat();
1645 /** Returns the sample corresponding to the supplied BBT (meter-based) beat.
1646 * @param beat The BBT (meter-based) beat.
1647 * @return The sample duration according to the tempo map at the supplied BBT (meter-based) beat.
1649 * This function uses both tempo and meter.
1652 TempoMap::sample_at_beat (const double& beat) const
1654 Glib::Threads::RWLock::ReaderLock lm (lock);
1656 return sample_at_minute (minute_at_beat_locked (_metrics, beat));
1659 /* meter & tempo section based */
1661 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1663 MeterSection* prev_m = 0;
1664 TempoSection* prev_t = 0;
1668 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1669 if (!(*i)->is_tempo()) {
1670 m = static_cast<MeterSection*> (*i);
1671 if (prev_m && m->beat() > beat) {
1681 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1682 if ((*i)->is_tempo()) {
1683 t = static_cast<TempoSection*> (*i);
1689 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1698 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1701 /** Returns a Tempo corresponding to the supplied sample position.
1702 * @param sample The audio sample.
1703 * @return a Tempo according to the tempo map at the supplied sample.
1707 TempoMap::tempo_at_sample (const samplepos_t sample) const
1709 Glib::Threads::RWLock::ReaderLock lm (lock);
1711 return tempo_at_minute_locked (_metrics, minute_at_sample (sample));
1715 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1717 TempoSection* prev_t = 0;
1721 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1722 if ((*i)->is_tempo()) {
1723 t = static_cast<TempoSection*> (*i);
1727 if ((prev_t) && t->minute() > minute) {
1728 /* t is the section past sample */
1729 return prev_t->tempo_at_minute (minute);
1736 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1739 /** returns the sample at which the supplied tempo occurs, or
1740 * the sample of the last tempo section (search exhausted)
1741 * only the position of the first occurence will be returned
1745 TempoMap::sample_at_tempo (const Tempo& tempo) const
1747 Glib::Threads::RWLock::ReaderLock lm (lock);
1749 return sample_at_minute (minute_at_tempo_locked (_metrics, tempo));
1753 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1755 TempoSection* prev_t = 0;
1756 const double tempo_bpm = tempo.note_types_per_minute();
1758 Metrics::const_iterator i;
1760 for (i = metrics.begin(); i != metrics.end(); ++i) {
1762 if ((*i)->is_tempo()) {
1763 t = static_cast<TempoSection*> (*i);
1771 if (t->note_types_per_minute() == tempo_bpm) {
1776 const double prev_t_bpm = prev_t->note_types_per_minute();
1777 const double prev_t_end_bpm = prev_t->end_note_types_per_minute();
1778 if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm)
1779 || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm)
1780 || (prev_t_end_bpm == tempo_bpm)) {
1782 return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
1790 return prev_t->minute();
1794 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1796 TempoSection* prev_t = 0;
1800 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1801 if ((*i)->is_tempo()) {
1802 t = static_cast<TempoSection*> (*i);
1806 if ((prev_t) && t->pulse() > pulse) {
1807 /* t is the section past sample */
1808 return prev_t->tempo_at_pulse (pulse);
1815 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1819 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1821 TempoSection* prev_t = 0;
1822 const double tempo_bpm = tempo.note_types_per_minute();
1824 Metrics::const_iterator i;
1826 for (i = metrics.begin(); i != metrics.end(); ++i) {
1828 if ((*i)->is_tempo()) {
1829 t = static_cast<TempoSection*> (*i);
1835 const double t_bpm = t->note_types_per_minute();
1837 if (t_bpm == tempo_bpm) {
1842 const double prev_t_bpm = prev_t->note_types_per_minute();
1844 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1845 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1853 return prev_t->pulse();
1856 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1857 * @param qn the position in quarter note beats.
1858 * @return the Tempo at the supplied quarter-note.
1861 TempoMap::tempo_at_quarter_note (const double& qn) const
1863 Glib::Threads::RWLock::ReaderLock lm (lock);
1865 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1868 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1869 * @param tempo the tempo.
1870 * @return the position in quarter-note beats where the map bpm
1871 * is equal to that of the Tempo. currently ignores note_type.
1874 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1876 Glib::Threads::RWLock::ReaderLock lm (lock);
1878 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;
1881 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1882 * @param metrics the list of metric sections used to calculate the pulse.
1883 * @param beat The BBT (meter-based) beat.
1884 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1886 * a pulse or whole note is the base musical position of a MetricSection.
1887 * it is equivalent to four quarter notes.
1891 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1893 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1895 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1898 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1899 * @param metrics the list of metric sections used to calculate the beat.
1900 * @param pulse the whole-note pulse.
1901 * @return the meter-based beat at the supplied whole-note pulse.
1903 * a pulse or whole note is the base musical position of a MetricSection.
1904 * it is equivalent to four quarter notes.
1907 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1909 MeterSection* prev_m = 0;
1911 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1913 if (!(*i)->is_tempo()) {
1914 m = static_cast<MeterSection*> (*i);
1915 if (prev_m && m->pulse() > pulse) {
1923 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1927 /* tempo section based */
1929 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1931 /* HOLD (at least) THE READER LOCK */
1932 TempoSection* prev_t = 0;
1934 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1936 if ((*i)->is_tempo()) {
1937 t = static_cast<TempoSection*> (*i);
1941 if (prev_t && t->minute() > minute) {
1942 /*the previous ts is the one containing the sample */
1943 const double ret = prev_t->pulse_at_minute (minute);
1944 /* audio locked section in new meter*/
1945 if (t->pulse() < ret) {
1956 /* treated as constant for this ts */
1957 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1959 return pulses_in_section + prev_t->pulse();
1962 /* tempo section based */
1964 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1966 /* HOLD THE READER LOCK */
1968 const TempoSection* prev_t = 0;
1970 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1973 if ((*i)->is_tempo()) {
1974 t = static_cast<TempoSection*> (*i);
1978 if (prev_t && t->pulse() > pulse) {
1979 return prev_t->minute_at_pulse (pulse);
1988 /* must be treated as constant, irrespective of _type */
1989 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1991 return dtime + prev_t->minute();
1994 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1995 * @param bbt The BBT time (meter-based).
1996 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
2000 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
2002 Glib::Threads::RWLock::ReaderLock lm (lock);
2003 return beat_at_bbt_locked (_metrics, bbt);
2008 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2010 /* CALLER HOLDS READ LOCK */
2012 MeterSection* prev_m = 0;
2014 /* because audio-locked meters have 'fake' integral beats,
2015 there is no pulse offset here.
2019 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2020 if (!(*i)->is_tempo()) {
2021 m = static_cast<MeterSection*> (*i);
2023 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2024 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2034 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2035 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2036 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2041 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2042 * @param beat The BBT (meter-based) beat.
2043 * @return The BBT time (meter-based) at the supplied meter-based beat.
2047 TempoMap::bbt_at_beat (const double& beat)
2049 Glib::Threads::RWLock::ReaderLock lm (lock);
2050 return bbt_at_beat_locked (_metrics, beat);
2054 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2056 /* CALLER HOLDS READ LOCK */
2057 MeterSection* prev_m = 0;
2058 const double beats = max (0.0, b);
2060 MeterSection* m = 0;
2062 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2063 if (!(*i)->is_tempo()) {
2064 m = static_cast<MeterSection*> (*i);
2066 if (m->beat() > beats) {
2067 /* this is the meter after the one our beat is on*/
2077 const double beats_in_ms = beats - prev_m->beat();
2078 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2079 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2080 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2081 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2085 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2086 ret.beats = (uint32_t) floor (remaining_beats);
2087 ret.bars = total_bars;
2089 /* 0 0 0 to 1 1 0 - based mapping*/
2093 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2095 ret.ticks -= BBT_Time::ticks_per_beat;
2098 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2106 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2107 * @param bbt The BBT time (meter-based).
2108 * @return the quarter note beat at the supplied BBT time
2110 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2112 * while the input uses meter, the output does not.
2115 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2117 Glib::Threads::RWLock::ReaderLock lm (lock);
2119 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2123 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2125 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2128 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2131 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2135 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2137 /* CALLER HOLDS READ LOCK */
2139 MeterSection* prev_m = 0;
2141 /* because audio-locked meters have 'fake' integral beats,
2142 there is no pulse offset here.
2146 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2147 if (!(*i)->is_tempo()) {
2148 m = static_cast<MeterSection*> (*i);
2150 if (m->bbt().bars > bbt.bars) {
2160 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2161 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2162 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2167 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2168 * @param qn the quarter-note beat.
2169 * @return The BBT time (meter-based) at the supplied meter-based beat.
2171 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2175 TempoMap::bbt_at_quarter_note (const double& qn)
2177 Glib::Threads::RWLock::ReaderLock lm (lock);
2179 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2182 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2183 * @param metrics The list of metric sections used to determine the result.
2184 * @param pulse The whole-note pulse.
2185 * @return The BBT time at the supplied whole-note pulse.
2187 * a pulse or whole note is the basic musical position of a MetricSection.
2188 * it is equivalent to four quarter notes.
2189 * while the output uses meter, the input does not.
2192 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2194 MeterSection* prev_m = 0;
2196 MeterSection* m = 0;
2198 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2200 if (!(*i)->is_tempo()) {
2201 m = static_cast<MeterSection*> (*i);
2204 double const pulses_to_m = m->pulse() - prev_m->pulse();
2205 if (prev_m->pulse() + pulses_to_m > pulse) {
2206 /* this is the meter after the one our beat is on*/
2217 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2218 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2219 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2220 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2221 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2225 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2226 ret.beats = (uint32_t) floor (remaining_beats);
2227 ret.bars = total_bars;
2229 /* 0 0 0 to 1 1 0 mapping*/
2233 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2235 ret.ticks -= BBT_Time::ticks_per_beat;
2238 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2246 /** Returns the BBT time corresponding to the supplied sample position.
2247 * @param sample the position in audio samples.
2248 * @return the BBT time at the sample position .
2252 TempoMap::bbt_at_sample (samplepos_t sample)
2260 warning << string_compose (_("tempo map was asked for BBT time at sample %1\n"), sample) << endmsg;
2265 const double minute = minute_at_sample (sample);
2267 Glib::Threads::RWLock::ReaderLock lm (lock);
2269 return bbt_at_minute_locked (_metrics, minute);
2273 TempoMap::bbt_at_sample_rt (samplepos_t sample)
2275 const double minute = minute_at_sample (sample);
2277 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2280 throw std::logic_error ("TempoMap::bbt_at_sample_rt() could not lock tempo map");
2283 return bbt_at_minute_locked (_metrics, minute);
2287 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2297 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2298 MeterSection* prev_m = 0;
2299 MeterSection* next_m = 0;
2303 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2304 if (!(*i)->is_tempo()) {
2305 m = static_cast<MeterSection*> (*i);
2306 if (prev_m && m->minute() > minute) {
2316 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2318 /* handle sample before first meter */
2319 if (minute < prev_m->minute()) {
2322 /* audio locked meters fake their beat */
2323 if (next_m && next_m->beat() < beat) {
2324 beat = next_m->beat();
2327 beat = max (0.0, beat);
2329 const double beats_in_ms = beat - prev_m->beat();
2330 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2331 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2332 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2333 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2337 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2338 ret.beats = (uint32_t) floor (remaining_beats);
2339 ret.bars = total_bars;
2341 /* 0 0 0 to 1 1 0 - based mapping*/
2345 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2347 ret.ticks -= BBT_Time::ticks_per_beat;
2350 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2358 /** Returns the sample position corresponding to the supplied BBT time.
2359 * @param bbt the position in BBT time.
2360 * @return the sample position at bbt.
2364 TempoMap::sample_at_bbt (const BBT_Time& bbt)
2368 warning << string_compose (_("tempo map asked for sample time at bar < 1 (%1)\n"), bbt) << endmsg;
2373 if (bbt.beats < 1) {
2374 throw std::logic_error ("beats are counted from one");
2379 Glib::Threads::RWLock::ReaderLock lm (lock);
2380 minute = minute_at_bbt_locked (_metrics, bbt);
2383 return sample_at_minute (minute);
2386 /* meter & tempo section based */
2388 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2390 /* HOLD THE READER LOCK */
2392 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2397 * Returns the quarter-note beat position corresponding to the supplied sample.
2399 * @param sample the position in samples.
2400 * @return The quarter-note position of the supplied sample. Ignores meter.
2404 TempoMap::quarter_note_at_sample (const samplepos_t sample) const
2406 const double minute = minute_at_sample (sample);
2408 Glib::Threads::RWLock::ReaderLock lm (lock);
2410 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2414 TempoMap::quarter_note_at_sample_rt (const samplepos_t sample) const
2416 const double minute = minute_at_sample (sample);
2418 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2421 throw std::logic_error ("TempoMap::quarter_note_at_sample_rt() could not lock tempo map");
2424 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2428 * Returns the sample position corresponding to the supplied quarter-note beat.
2430 * @param quarter_note the quarter-note position.
2431 * @return the sample position of the supplied quarter-note. Ignores meter.
2436 TempoMap::sample_at_quarter_note (const double quarter_note) const
2440 Glib::Threads::RWLock::ReaderLock lm (lock);
2442 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2445 return sample_at_minute (minute);
2448 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2449 * @param beat The BBT (meter-based) beat.
2450 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2452 * a quarter-note may be compared with and assigned to Temporal::Beats.
2456 TempoMap::quarter_note_at_beat (const double beat) const
2458 Glib::Threads::RWLock::ReaderLock lm (lock);
2460 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2463 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2464 * @param quarter_note The position in quarter-note beats.
2465 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2467 * a quarter-note is the musical unit of Temporal::Beats.
2471 TempoMap::beat_at_quarter_note (const double quarter_note) const
2473 Glib::Threads::RWLock::ReaderLock lm (lock);
2475 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2478 /** Returns the duration in samples between two supplied quarter-note beat positions.
2479 * @param start the first position in quarter-note beats.
2480 * @param end the end position in quarter-note beats.
2481 * @return the sample distance ober the quarter-note beats duration.
2483 * use this rather than e.g.
2484 * sample_at-quarter_note (end_beats) - sample_at_quarter_note (start_beats).
2485 * samples_between_quarter_notes() doesn't round to audio samples as an intermediate step,
2489 TempoMap::samples_between_quarter_notes (const double start, const double end) const
2494 Glib::Threads::RWLock::ReaderLock lm (lock);
2495 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2498 return sample_at_minute (minutes);
2502 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2505 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2509 TempoMap::quarter_notes_between_samples (const samplecnt_t start, const samplecnt_t end) const
2511 Glib::Threads::RWLock::ReaderLock lm (lock);
2513 return quarter_notes_between_samples_locked (_metrics, start, end);
2517 TempoMap::quarter_notes_between_samples_locked (const Metrics& metrics, const samplecnt_t start, const samplecnt_t end) const
2519 const TempoSection* prev_t = 0;
2521 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2524 if ((*i)->is_tempo()) {
2525 t = static_cast<TempoSection*> (*i);
2529 if (prev_t && t->sample() > start) {
2536 const double start_qn = prev_t->pulse_at_sample (start);
2538 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2541 if ((*i)->is_tempo()) {
2542 t = static_cast<TempoSection*> (*i);
2546 if (prev_t && t->sample() > end) {
2552 const double end_qn = prev_t->pulse_at_sample (end);
2554 return (end_qn - start_qn) * 4.0;
2558 TempoMap::check_solved (const Metrics& metrics) const
2560 TempoSection* prev_t = 0;
2561 MeterSection* prev_m = 0;
2563 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2566 if ((*i)->is_tempo()) {
2567 t = static_cast<TempoSection*> (*i);
2572 /* check ordering */
2573 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2577 /* precision check ensures tempo and samples align.*/
2578 if (t->sample() != sample_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2579 if (!t->locked_to_meter()) {
2584 /* gradient limit - who knows what it should be?
2585 things are also ok (if a little chaotic) without this
2587 if (fabs (prev_t->c()) > 1000.0) {
2588 //std::cout << "c : " << prev_t->c() << std::endl;
2595 if (!(*i)->is_tempo()) {
2596 m = static_cast<MeterSection*> (*i);
2597 if (prev_m && m->position_lock_style() == AudioTime) {
2598 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_sample (m->sample() - 1));
2599 const samplepos_t nascent_m_sample = sample_at_minute (t->minute_at_pulse (m->pulse()));
2600 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2602 if (t && (nascent_m_sample > m->sample() || nascent_m_sample < 0)) {
2616 TempoMap::set_active_tempi (const Metrics& metrics, const samplepos_t sample)
2618 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2620 if ((*i)->is_tempo()) {
2621 t = static_cast<TempoSection*> (*i);
2622 if (t->locked_to_meter()) {
2623 t->set_active (true);
2624 } else if (t->position_lock_style() == AudioTime) {
2625 if (t->sample() < sample) {
2626 t->set_active (false);
2627 t->set_pulse (-1.0);
2628 } else if (t->sample() > sample) {
2629 t->set_active (true);
2630 } else if (t->sample() == sample) {
2640 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2642 TempoSection* prev_t = 0;
2643 TempoSection* section_prev = 0;
2644 double first_m_minute = 0.0;
2645 const bool sml = section->locked_to_meter();
2647 /* can't move a tempo before the first meter */
2648 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2650 if (!(*i)->is_tempo()) {
2651 m = static_cast<MeterSection*> (*i);
2653 first_m_minute = m->minute();
2658 if (!section->initial() && minute <= first_m_minute) {
2662 section->set_active (true);
2663 section->set_minute (minute);
2665 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2667 if ((*i)->is_tempo()) {
2668 t = static_cast<TempoSection*> (*i);
2680 if (t->sample() == sample_at_minute (minute)) {
2684 const bool tlm = t->position_lock_style() == MusicTime;
2686 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2687 section_prev = prev_t;
2689 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2690 if (!section->locked_to_meter()) {
2691 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2696 if (t->position_lock_style() == MusicTime) {
2697 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2698 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2700 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2701 if (!t->locked_to_meter()) {
2702 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2710 MetricSectionFrameSorter fcmp;
2711 imaginary.sort (fcmp);
2713 recompute_tempi (imaginary);
2715 if (check_solved (imaginary)) {
2723 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2725 TempoSection* prev_t = 0;
2726 TempoSection* section_prev = 0;
2728 section->set_pulse (pulse);
2730 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2732 if ((*i)->is_tempo()) {
2733 t = static_cast<TempoSection*> (*i);
2744 section_prev = prev_t;
2748 if (t->position_lock_style() == MusicTime) {
2749 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2750 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2752 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2753 if (!t->locked_to_meter()) {
2754 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2763 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2764 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2767 MetricSectionSorter cmp;
2768 imaginary.sort (cmp);
2770 recompute_tempi (imaginary);
2772 * XX need a restriction here, but only for this case,
2773 * as audio locked tempos don't interact in the same way.
2775 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2777 * |50 bpm |250 bpm |60 bpm
2778 * drag 250 to the pulse after 60->
2779 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2781 if (check_solved (imaginary)) {
2789 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2791 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2792 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2793 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2797 if (section->initial()) {
2798 /* lock the first tempo to our first meter */
2799 if (!set_active_tempi (imaginary, sample_at_minute (minute))) {
2804 TempoSection* meter_locked_tempo = 0;
2806 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2808 if ((*ii)->is_tempo()) {
2809 t = static_cast<TempoSection*> (*ii);
2810 if (t->locked_to_meter() && t->sample() == section->sample()) {
2811 meter_locked_tempo = t;
2817 if (!meter_locked_tempo) {
2821 MeterSection* prev_m = 0;
2823 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2824 bool solved = false;
2826 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2828 if (!(*i)->is_tempo()) {
2829 m = static_cast<MeterSection*> (*i);
2831 if (prev_m && !section->initial()) {
2832 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2833 if (beats + prev_m->beat() < section->beat()) {
2834 /* set the section pulse according to its musical position,
2835 * as an earlier time than this has been requested.
2837 const double new_pulse = ((section->beat() - prev_m->beat())
2838 / prev_m->note_divisor()) + prev_m->pulse();
2840 tempo_copy->set_position_lock_style (MusicTime);
2841 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2842 meter_locked_tempo->set_position_lock_style (MusicTime);
2843 section->set_position_lock_style (MusicTime);
2844 section->set_pulse (new_pulse);
2845 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2846 meter_locked_tempo->set_position_lock_style (AudioTime);
2847 section->set_position_lock_style (AudioTime);
2848 section->set_minute (meter_locked_tempo->minute());
2854 Metrics::const_iterator d = future_map.begin();
2855 while (d != future_map.end()) {
2864 /* all is ok. set section's locked tempo if allowed.
2865 possibly disallow if there is an adjacent audio-locked tempo.
2866 XX this check could possibly go. its never actually happened here.
2868 MeterSection* meter_copy = const_cast<MeterSection*>
2869 (&meter_section_at_minute_locked (future_map, section->minute()));
2871 meter_copy->set_minute (minute);
2873 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2874 section->set_minute (minute);
2875 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2876 / prev_m->note_divisor()) + prev_m->pulse());
2877 solve_map_minute (imaginary, meter_locked_tempo, minute);
2882 Metrics::const_iterator d = future_map.begin();
2883 while (d != future_map.end()) {
2893 /* initial (first meter atm) */
2895 tempo_copy->set_minute (minute);
2896 tempo_copy->set_pulse (0.0);
2898 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2899 section->set_minute (minute);
2900 meter_locked_tempo->set_minute (minute);
2901 meter_locked_tempo->set_pulse (0.0);
2902 solve_map_minute (imaginary, meter_locked_tempo, minute);
2907 Metrics::const_iterator d = future_map.begin();
2908 while (d != future_map.end()) {
2917 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2918 section->set_beat (b_bbt);
2919 section->set_pulse (0.0);
2929 MetricSectionFrameSorter fcmp;
2930 imaginary.sort (fcmp);
2932 recompute_meters (imaginary);
2938 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2940 /* disallow setting section to an existing meter's bbt */
2941 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2943 if (!(*i)->is_tempo()) {
2944 m = static_cast<MeterSection*> (*i);
2945 if (m != section && m->bbt().bars == when.bars) {
2951 MeterSection* prev_m = 0;
2952 MeterSection* section_prev = 0;
2954 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2956 if (!(*i)->is_tempo()) {
2957 m = static_cast<MeterSection*> (*i);
2963 pair<double, BBT_Time> b_bbt;
2964 double new_pulse = 0.0;
2966 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2967 section_prev = prev_m;
2969 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2970 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2971 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2973 section->set_beat (b_bbt);
2974 section->set_pulse (pulse);
2975 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2979 if (m->position_lock_style() == AudioTime) {
2980 TempoSection* meter_locked_tempo = 0;
2982 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2984 if ((*ii)->is_tempo()) {
2985 t = static_cast<TempoSection*> (*ii);
2986 if (t->locked_to_meter() && t->sample() == m->sample()) {
2987 meter_locked_tempo = t;
2993 if (!meter_locked_tempo) {
2998 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3000 if (beats + prev_m->beat() != m->beat()) {
3001 /* tempo/ meter change caused a change in beat (bar). */
3003 /* the user has requested that the previous section of music overlaps this one.
3004 we have no choice but to change the bar number here, as being locked to audio means
3005 we must stay where we are on the timeline.
3007 beats = m->beat() - prev_m->beat();
3008 b_bbt = make_pair (beats + prev_m->beat()
3009 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3010 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3012 } else if (!m->initial()) {
3013 b_bbt = make_pair (m->beat(), m->bbt());
3014 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3017 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3020 meter_locked_tempo->set_pulse (new_pulse);
3021 m->set_beat (b_bbt);
3022 m->set_pulse (new_pulse);
3026 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3027 if (beats + prev_m->beat() != m->beat()) {
3028 /* tempo/ meter change caused a change in beat (bar). */
3029 b_bbt = make_pair (beats + prev_m->beat()
3030 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3032 b_bbt = make_pair (beats + prev_m->beat()
3035 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3036 m->set_beat (b_bbt);
3037 m->set_pulse (new_pulse);
3038 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3045 if (!section_prev) {
3048 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3049 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3050 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3052 section->set_beat (b_bbt);
3053 section->set_pulse (pulse);
3054 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3057 MetricSectionSorter cmp;
3058 imaginary.sort (cmp);
3060 recompute_meters (imaginary);
3065 /** places a copy of _metrics into copy and returns a pointer
3066 * to section's equivalent in copy.
3069 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section) const
3071 TempoSection* 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);
3077 ret = new TempoSection (*t);
3078 copy.push_back (ret);
3082 TempoSection* cp = new TempoSection (*t);
3083 copy.push_back (cp);
3085 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3086 MeterSection* cp = new MeterSection (*m);
3087 copy.push_back (cp);
3095 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section) const
3097 MeterSection* ret = 0;
3099 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3100 if ((*i)->is_tempo()) {
3101 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3102 TempoSection* cp = new TempoSection (*t);
3103 copy.push_back (cp);
3105 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3107 ret = new MeterSection (*m);
3108 copy.push_back (ret);
3111 MeterSection* cp = new MeterSection (*m);
3112 copy.push_back (cp);
3119 /** answers the question "is this a valid beat position for this tempo section?".
3120 * it returns true if the tempo section can be moved to the requested bbt position,
3121 * leaving the tempo map in a solved state.
3122 * @param ts the tempo section to be moved
3123 * @param bbt the requested new position for the tempo section
3124 * @return true if the tempo section can be moved to the position, otherwise false.
3127 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3130 TempoSection* tempo_copy = 0;
3133 Glib::Threads::RWLock::ReaderLock lm (lock);
3134 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3140 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3142 Metrics::const_iterator d = copy.begin();
3143 while (d != copy.end()) {
3152 * 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,
3153 * taking any possible reordering as a consequence of this into account.
3154 * @param section - the section to be altered
3155 * @param bbt - the BBT time where the altered tempo will fall
3156 * @return returns - the position in pulses and samples (as a pair) where the new tempo section will lie.
3158 pair<double, samplepos_t>
3159 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3162 pair<double, samplepos_t> ret = make_pair (0.0, 0);
3164 Glib::Threads::RWLock::ReaderLock lm (lock);
3166 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3168 const double beat = beat_at_bbt_locked (future_map, bbt);
3170 if (section->position_lock_style() == AudioTime) {
3171 tempo_copy->set_position_lock_style (MusicTime);
3174 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3175 ret.first = tempo_copy->pulse();
3176 ret.second = tempo_copy->sample();
3178 ret.first = section->pulse();
3179 ret.second = section->sample();
3182 Metrics::const_iterator d = future_map.begin();
3183 while (d != future_map.end()) {
3190 /** moves a TempoSection to a specified position.
3191 * @param ts - the section to be moved
3192 * @param sample - the new position in samples for the tempo
3193 * @param sub_num - the snap division to use if using musical time.
3195 * if sub_num is non-zero, the sample position is used to calculate an exact
3198 * -1 | snap to bars (meter-based)
3199 * 0 | no snap - use audio sample for musical position
3200 * 1 | snap to meter-based (BBT) beat
3201 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3203 * this follows the snap convention in the gui.
3204 * if sub_num is zero, the musical position will be taken from the supplied sample.
3207 TempoMap::gui_set_tempo_position (TempoSection* ts, const samplepos_t sample, const int& sub_num)
3211 if (ts->position_lock_style() == MusicTime) {
3213 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied sample. */
3214 Glib::Threads::RWLock::WriterLock lm (lock);
3215 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3217 tempo_copy->set_position_lock_style (AudioTime);
3219 if (solve_map_minute (future_map, tempo_copy, minute_at_sample (sample))) {
3220 const double beat = exact_beat_at_sample_locked (future_map, sample, sub_num);
3221 const double pulse = pulse_at_beat_locked (future_map, beat);
3223 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3224 solve_map_pulse (_metrics, ts, pulse);
3225 recompute_meters (_metrics);
3233 Glib::Threads::RWLock::WriterLock lm (lock);
3234 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3238 /* We're moving the object that defines the grid while snapping to it...
3239 * Placing the ts at the beat corresponding to the requested sample may shift the
3240 * grid in such a way that the mouse is left hovering over a completerly different division,
3241 * causing jittering when the mouse next moves (esp. large tempo deltas).
3242 * We fudge around this by doing this in the musical domain and then swapping back for the recompute.
3244 const double qn = exact_qn_at_sample_locked (_metrics, sample, sub_num);
3245 tempo_copy->set_position_lock_style (MusicTime);
3246 if (solve_map_pulse (future_map, tempo_copy, qn / 4.0)) {
3247 ts->set_position_lock_style (MusicTime);
3248 solve_map_pulse (_metrics, ts, qn / 4.0);
3249 ts->set_position_lock_style (AudioTime);
3250 recompute_meters (_metrics);
3253 if (solve_map_minute (future_map, tempo_copy, minute_at_sample (sample))) {
3254 solve_map_minute (_metrics, ts, minute_at_sample (sample));
3255 recompute_meters (_metrics);
3261 Metrics::const_iterator d = future_map.begin();
3262 while (d != future_map.end()) {
3267 MetricPositionChanged (PropertyChange ()); // Emit Signal
3270 /** moves a MeterSection to a specified position.
3271 * @param ms - the section to be moved
3272 * @param sample - the new position in samples for the meter
3274 * as a meter cannot snap to anything but bars,
3275 * the supplied sample is rounded to the nearest bar, possibly
3276 * leaving the meter position unchanged.
3279 TempoMap::gui_set_meter_position (MeterSection* ms, const samplepos_t sample)
3283 if (ms->position_lock_style() == AudioTime) {
3286 Glib::Threads::RWLock::WriterLock lm (lock);
3287 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3289 if (solve_map_minute (future_map, copy, minute_at_sample (sample))) {
3290 solve_map_minute (_metrics, ms, minute_at_sample (sample));
3291 recompute_tempi (_metrics);
3296 Glib::Threads::RWLock::WriterLock lm (lock);
3297 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3299 const double beat = beat_at_minute_locked (_metrics, minute_at_sample (sample));
3300 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3302 if (solve_map_bbt (future_map, copy, bbt)) {
3303 solve_map_bbt (_metrics, ms, bbt);
3304 recompute_tempi (_metrics);
3309 Metrics::const_iterator d = future_map.begin();
3310 while (d != future_map.end()) {
3315 MetricPositionChanged (PropertyChange ()); // Emit Signal
3319 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3322 bool can_solve = false;
3324 Glib::Threads::RWLock::WriterLock lm (lock);
3325 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3327 if (tempo_copy->type() == TempoSection::Constant) {
3328 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3329 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3331 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3332 tempo_copy->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3335 if (ts->clamped()) {
3336 TempoSection* prev = 0;
3337 if ((prev = previous_tempo_section_locked (future_map, tempo_copy)) != 0) {
3338 prev->set_end_note_types_per_minute (tempo_copy->note_types_per_minute());
3342 recompute_tempi (future_map);
3344 if (check_solved (future_map)) {
3345 if (ts->type() == TempoSection::Constant) {
3346 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3347 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3349 ts->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3350 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3353 if (ts->clamped()) {
3354 TempoSection* prev = 0;
3355 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3356 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3360 recompute_map (_metrics);
3365 Metrics::const_iterator d = future_map.begin();
3366 while (d != future_map.end()) {
3371 MetricPositionChanged (PropertyChange ()); // Emit Signal
3378 TempoMap::gui_stretch_tempo (TempoSection* ts, const samplepos_t sample, const samplepos_t end_sample, const double start_qnote, const double end_qnote)
3381 Ts (future prev_t) Tnext
3384 |----------|----------
3385 e_f qn_beats(sample)
3391 Glib::Threads::RWLock::WriterLock lm (lock);
3397 TempoSection* ts_copy = copy_metrics_and_point (_metrics, future_map, ts);
3403 /* minimum allowed measurement distance in samples */
3404 samplepos_t const min_dframe = 2;
3407 if (ts_copy->clamped()) {
3408 TempoSection* next_t = next_tempo_section_locked (future_map, ts_copy);
3409 TempoSection* prev_to_ts_copy = previous_tempo_section_locked (future_map, ts_copy);
3410 /* the change in samples is the result of changing the slope of at most 2 previous tempo sections.
3411 * constant to constant is straightforward, as the tempo prev to ts_copy has constant slope.
3413 double contribution = 0.0;
3414 if (next_t && prev_to_ts_copy && prev_to_ts_copy->type() == TempoSection::Ramp) {
3415 contribution = (ts_copy->pulse() - prev_to_ts_copy->pulse()) / (double) (next_t->pulse() - prev_to_ts_copy->pulse());
3417 samplepos_t const fr_off = end_sample - sample;
3418 sampleoffset_t const ts_copy_sample_contribution = fr_off - (contribution * (double) fr_off);
3420 if (sample > prev_to_ts_copy->sample() + min_dframe && (sample + ts_copy_sample_contribution) > prev_to_ts_copy->sample() + min_dframe) {
3421 new_bpm = ts_copy->note_types_per_minute() * ((start_qnote - (prev_to_ts_copy->pulse() * 4.0))
3422 / (end_qnote - (prev_to_ts_copy->pulse() * 4.0)));
3424 new_bpm = ts_copy->note_types_per_minute();
3427 if (sample > ts_copy->sample() + min_dframe && end_sample > ts_copy->sample() + min_dframe) {
3429 new_bpm = ts_copy->note_types_per_minute() * ((sample - ts_copy->sample())
3430 / (double) (end_sample - ts_copy->sample()));
3432 new_bpm = ts_copy->note_types_per_minute();
3435 new_bpm = min (new_bpm, (double) 1000.0);
3437 /* don't clamp and proceed here.
3438 testing has revealed that this can go negative,
3439 which is an entirely different thing to just being too low.
3442 if (new_bpm < 0.5) {
3446 ts_copy->set_note_types_per_minute (new_bpm);
3448 if (ts_copy->clamped()) {
3449 TempoSection* prev = 0;
3450 if ((prev = previous_tempo_section_locked (future_map, ts_copy)) != 0) {
3451 prev->set_end_note_types_per_minute (ts_copy->note_types_per_minute());
3455 recompute_tempi (future_map);
3456 recompute_meters (future_map);
3458 if (check_solved (future_map)) {
3459 ts->set_note_types_per_minute (new_bpm);
3461 if (ts->clamped()) {
3462 TempoSection* prev = 0;
3463 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3464 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3468 recompute_tempi (_metrics);
3469 recompute_meters (_metrics);
3475 Metrics::const_iterator d = future_map.begin();
3476 while (d != future_map.end()) {
3480 MetricPositionChanged (PropertyChange ()); // Emit Signal
3485 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const samplepos_t sample, const samplepos_t end_sample)
3488 Ts (future prev_t) Tnext
3491 |----------|----------
3492 e_f qn_beats(sample)
3498 Glib::Threads::RWLock::WriterLock lm (lock);
3504 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3510 /* minimum allowed measurement distance in samples */
3511 samplepos_t const min_dframe = 2;
3514 if (sample > prev_t->sample() + min_dframe && end_sample > prev_t->sample() + min_dframe) {
3515 new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->sample() - sample)
3516 / (double) (prev_t->sample() - end_sample));
3518 new_bpm = prev_t->end_note_types_per_minute();
3521 new_bpm = min (new_bpm, (double) 1000.0);
3523 if (new_bpm < 0.5) {
3527 prev_t->set_end_note_types_per_minute (new_bpm);
3529 TempoSection* next = 0;
3530 if ((next = next_tempo_section_locked (future_map, prev_t)) != 0) {
3531 if (next->clamped()) {
3532 next->set_note_types_per_minute (prev_t->end_note_types_per_minute());
3536 recompute_tempi (future_map);
3537 recompute_meters (future_map);
3539 if (check_solved (future_map)) {
3540 ts->set_end_note_types_per_minute (new_bpm);
3542 TempoSection* true_next = 0;
3543 if ((true_next = next_tempo_section_locked (_metrics, ts)) != 0) {
3544 if (true_next->clamped()) {
3545 true_next->set_note_types_per_minute (ts->end_note_types_per_minute());
3549 recompute_tempi (_metrics);
3550 recompute_meters (_metrics);
3556 Metrics::const_iterator d = future_map.begin();
3557 while (d != future_map.end()) {
3562 MetricPositionChanged (PropertyChange ()); // Emit Signal
3566 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const samplepos_t sample, const samplepos_t end_sample)
3568 TempoSection* next_t = 0;
3569 TempoSection* next_to_next_t = 0;
3571 bool can_solve = false;
3573 /* minimum allowed measurement distance in samples */
3574 samplepos_t const min_dframe = 2;
3577 Glib::Threads::RWLock::WriterLock lm (lock);
3582 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3583 TempoSection* prev_to_prev_t = 0;
3584 const sampleoffset_t fr_off = end_sample - sample;
3590 if (tempo_copy->pulse() > 0.0) {
3591 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_sample (tempo_copy->sample() - 1)));
3594 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3595 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3596 next_t = static_cast<TempoSection*> (*i);
3605 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3606 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3607 next_to_next_t = static_cast<TempoSection*> (*i);
3612 if (!next_to_next_t) {
3616 double prev_contribution = 0.0;
3618 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3619 prev_contribution = (tempo_copy->sample() - prev_to_prev_t->sample()) / (double) (next_t->sample() - prev_to_prev_t->sample());
3622 const sampleoffset_t tempo_copy_sample_contribution = fr_off - (prev_contribution * (double) fr_off);
3625 samplepos_t old_tc_minute = tempo_copy->minute();
3626 double old_next_minute = next_t->minute();
3627 double old_next_to_next_minute = next_to_next_t->minute();
3630 double new_next_bpm;
3631 double new_copy_end_bpm;
3633 if (sample > tempo_copy->sample() + min_dframe && (sample + tempo_copy_sample_contribution) > tempo_copy->sample() + min_dframe) {
3634 new_bpm = tempo_copy->note_types_per_minute() * ((sample - tempo_copy->sample())
3635 / (double) (end_sample - tempo_copy->sample()));
3637 new_bpm = tempo_copy->note_types_per_minute();
3640 /* don't clamp and proceed here.
3641 testing has revealed that this can go negative,
3642 which is an entirely different thing to just being too low.
3644 if (new_bpm < 0.5) {
3648 new_bpm = min (new_bpm, (double) 1000.0);
3650 tempo_copy->set_note_types_per_minute (new_bpm);
3651 if (tempo_copy->type() == TempoSection::Constant) {
3652 tempo_copy->set_end_note_types_per_minute (new_bpm);
3655 recompute_tempi (future_map);
3657 if (check_solved (future_map)) {
3663 ts->set_note_types_per_minute (new_bpm);
3664 if (ts->type() == TempoSection::Constant) {
3665 ts->set_end_note_types_per_minute (new_bpm);
3668 recompute_map (_metrics);
3673 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3674 if (sample > tempo_copy->sample() + min_dframe && end_sample > tempo_copy->sample() + min_dframe) {
3676 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3677 / (double) ((old_next_to_next_minute) - old_next_minute));
3680 new_next_bpm = next_t->note_types_per_minute();
3683 next_t->set_note_types_per_minute (new_next_bpm);
3684 recompute_tempi (future_map);
3686 if (check_solved (future_map)) {
3687 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3688 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3689 next_t = static_cast<TempoSection*> (*i);
3697 next_t->set_note_types_per_minute (new_next_bpm);
3698 recompute_map (_metrics);
3702 double next_sample_ratio = 1.0;
3703 double copy_sample_ratio = 1.0;
3705 if (next_to_next_t) {
3706 next_sample_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute - old_next_minute);
3708 copy_sample_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute));
3711 new_next_bpm = next_t->note_types_per_minute() * next_sample_ratio;
3712 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_sample_ratio;
3714 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3716 if (next_t->clamped()) {
3717 next_t->set_note_types_per_minute (new_copy_end_bpm);
3719 next_t->set_note_types_per_minute (new_next_bpm);
3722 recompute_tempi (future_map);
3724 if (check_solved (future_map)) {
3725 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3726 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3727 next_t = static_cast<TempoSection*> (*i);
3736 if (next_t->clamped()) {
3737 next_t->set_note_types_per_minute (new_copy_end_bpm);
3739 next_t->set_note_types_per_minute (new_next_bpm);
3742 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3743 recompute_map (_metrics);
3749 Metrics::const_iterator d = future_map.begin();
3750 while (d != future_map.end()) {
3755 MetricPositionChanged (PropertyChange ()); // Emit Signal
3760 /** Returns the sample position of the musical position zero */
3762 TempoMap::music_origin ()
3764 Glib::Threads::RWLock::ReaderLock lm (lock);
3766 return first_tempo().sample();
3769 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3770 * the supplied sample, possibly returning a negative value.
3772 * @param sample The session sample position.
3773 * @param sub_num The subdivision to use when rounding the beat.
3774 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3775 * Positive integers indicate quarter note (non BBT) divisions.
3776 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_sample()).
3777 * @return The beat position of the supplied sample.
3779 * when working to a musical grid, the use of sub_nom indicates that
3780 * the position should be interpreted musically.
3782 * it effectively snaps to meter bars, meter beats or quarter note divisions
3783 * (as per current gui convention) and returns a musical position independent of frame rate.
3785 * If the supplied sample lies before the first meter, the return will be negative,
3786 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3787 * the continuation of the tempo curve (backwards).
3789 * This function is sensitive to tempo and meter.
3792 TempoMap::exact_beat_at_sample (const samplepos_t sample, const int32_t sub_num) const
3794 Glib::Threads::RWLock::ReaderLock lm (lock);
3796 return exact_beat_at_sample_locked (_metrics, sample, sub_num);
3800 TempoMap::exact_beat_at_sample_locked (const Metrics& metrics, const samplepos_t sample, const int32_t divisions) const
3802 return beat_at_pulse_locked (_metrics, exact_qn_at_sample_locked (metrics, sample, divisions) / 4.0);
3805 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3806 * the supplied sample, possibly returning a negative value.
3808 * @param sample The session sample position.
3809 * @param sub_num The subdivision to use when rounding the quarter note.
3810 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3811 * Positive integers indicate quarter note (non BBT) divisions.
3812 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_sample()).
3813 * @return The quarter note position of the supplied sample.
3815 * When working to a musical grid, the use of sub_nom indicates that
3816 * the sample position should be interpreted musically.
3818 * it effectively snaps to meter bars, meter beats or quarter note divisions
3819 * (as per current gui convention) and returns a musical position independent of frame rate.
3821 * If the supplied sample lies before the first meter, the return will be negative,
3822 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3823 * the continuation of the tempo curve (backwards).
3825 * This function is tempo-sensitive.
3828 TempoMap::exact_qn_at_sample (const samplepos_t sample, const int32_t sub_num) const
3830 Glib::Threads::RWLock::ReaderLock lm (lock);
3832 return exact_qn_at_sample_locked (_metrics, sample, sub_num);
3836 TempoMap::exact_qn_at_sample_locked (const Metrics& metrics, const samplepos_t sample, const int32_t sub_num) const
3838 double qn = pulse_at_minute_locked (metrics, minute_at_sample (sample)) * 4.0;
3841 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3842 } else if (sub_num == 1) {
3843 /* the gui requested exact musical (BBT) beat */
3844 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_sample (sample)) + 0.5))) * 4.0;
3845 } else if (sub_num == -1) {
3847 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3851 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3853 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3855 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3865 /** returns the sample duration of the supplied BBT time at a specified sample position in the tempo map.
3866 * @param pos the sample position in the tempo map.
3867 * @param bbt the distance in BBT time from pos to calculate.
3868 * @param dir the rounding direction..
3869 * @return the duration in samples between pos and bbt
3872 TempoMap::bbt_duration_at (samplepos_t pos, const BBT_Time& bbt, int dir)
3874 Glib::Threads::RWLock::ReaderLock lm (lock);
3876 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_sample (pos));
3878 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_sample (pos)).divisions_per_bar();
3881 pos_bbt.bars += bbt.bars;
3883 pos_bbt.ticks += bbt.ticks;
3884 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3886 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3889 pos_bbt.beats += bbt.beats;
3890 if ((double) pos_bbt.beats > divisions) {
3892 pos_bbt.beats -= divisions;
3894 const samplecnt_t pos_bbt_sample = sample_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3896 return pos_bbt_sample - pos;
3900 if (pos_bbt.bars <= bbt.bars) {
3903 pos_bbt.bars -= bbt.bars;
3906 if (pos_bbt.ticks < bbt.ticks) {
3907 if (pos_bbt.bars > 1) {
3908 if (pos_bbt.beats == 1) {
3910 pos_bbt.beats = divisions;
3914 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3920 pos_bbt.ticks -= bbt.ticks;
3923 if (pos_bbt.beats <= bbt.beats) {
3924 if (pos_bbt.bars > 1) {
3926 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3931 pos_bbt.beats -= bbt.beats;
3934 return pos - sample_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3941 TempoMap::round_to_bar (samplepos_t fr, RoundMode dir)
3943 return round_to_type (fr, dir, Bar);
3947 TempoMap::round_to_beat (samplepos_t fr, RoundMode dir)
3949 return round_to_type (fr, dir, Beat);
3953 TempoMap::round_to_quarter_note_subdivision (samplepos_t fr, int sub_num, RoundMode dir)
3955 Glib::Threads::RWLock::ReaderLock lm (lock);
3956 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);
3957 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3958 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3960 ticks -= beats * BBT_Time::ticks_per_beat;
3963 /* round to next (or same iff dir == RoundUpMaybe) */
3965 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3967 if (mod == 0 && dir == RoundUpMaybe) {
3968 /* right on the subdivision, which is fine, so do nothing */
3970 } else if (mod == 0) {
3971 /* right on the subdivision, so the difference is just the subdivision ticks */
3972 ticks += ticks_one_subdivisions_worth;
3975 /* not on subdivision, compute distance to next subdivision */
3977 ticks += ticks_one_subdivisions_worth - mod;
3980 /* NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3981 * For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3982 * And since the "prev" direction DOES move beats, I assume this code is unintended.
3983 * But I'm keeping it around, commened out, until we determine there are no terrible consequences.
3986 if (ticks >= BBT_Time::ticks_per_beat) {
3987 ticks -= BBT_Time::ticks_per_beat;
3991 } else if (dir < 0) {
3993 /* round to previous (or same iff dir == RoundDownMaybe) */
3995 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3997 if (difference == 0 && dir == RoundDownAlways) {
3998 /* right on the subdivision, but force-rounding down,
3999 so the difference is just the subdivision ticks */
4000 difference = ticks_one_subdivisions_worth;
4003 if (ticks < difference) {
4004 ticks = BBT_Time::ticks_per_beat - ticks;
4006 ticks -= difference;
4010 /* round to nearest */
4013 /* compute the distance to the previous and next subdivision */
4015 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
4017 /* closer to the next subdivision, so shift forward */
4019 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
4021 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
4023 if (ticks > BBT_Time::ticks_per_beat) {
4025 ticks -= BBT_Time::ticks_per_beat;
4026 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
4029 } else if (rem > 0) {
4031 /* closer to previous subdivision, so shift backward */
4035 /* can't go backwards past zero, so ... */
4036 return MusicSample (0, 0);
4038 /* step back to previous beat */
4040 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4041 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4043 ticks = lrint (ticks - rem);
4044 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4047 /* on the subdivision, do nothing */
4051 MusicSample ret (0, 0);
4052 ret.sample = sample_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4053 ret.division = sub_num;
4059 TempoMap::round_to_type (samplepos_t sample, RoundMode dir, BBTPointType type)
4061 Glib::Threads::RWLock::ReaderLock lm (lock);
4062 const double minute = minute_at_sample (sample);
4063 const double beat_at_samplepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4064 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_samplepos));
4065 MusicSample ret (0, 0);
4072 /* find bar previous to 'sample' */
4078 ret.sample = sample_at_minute (minute_at_bbt_locked (_metrics, bbt));
4082 } else if (dir > 0) {
4083 /* find bar following 'sample' */
4088 ret.sample = sample_at_minute (minute_at_bbt_locked (_metrics, bbt));
4092 /* true rounding: find nearest bar */
4093 samplepos_t raw_ft = sample_at_minute (minute_at_bbt_locked (_metrics, bbt));
4096 samplepos_t prev_ft = sample_at_minute (minute_at_bbt_locked (_metrics, bbt));
4098 samplepos_t next_ft = sample_at_minute (minute_at_bbt_locked (_metrics, bbt));
4100 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4101 ret.sample = next_ft;
4106 ret.sample = prev_ft;
4118 ret.sample = sample_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_samplepos)));
4121 } else if (dir > 0) {
4122 ret.sample = sample_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_samplepos)));
4126 ret.sample = sample_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_samplepos + 0.5)));
4133 return MusicSample (0, 0);
4137 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4138 samplepos_t lower, samplepos_t upper, uint32_t bar_mod)
4140 Glib::Threads::RWLock::ReaderLock lm (lock);
4141 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_sample (lower)));
4142 samplecnt_t pos = 0;
4143 /* although the map handles negative beats, bbt doesn't. */
4148 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_sample (upper)) {
4152 while (pos >= 0 && pos < upper) {
4153 pos = sample_at_minute (minute_at_beat_locked (_metrics, cnt));
4154 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_sample (pos));
4155 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4156 const double qn = pulse_at_beat_locked (_metrics, cnt) * 4.0;
4158 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_sample (pos)), pos, bbt.bars, bbt.beats, qn));
4162 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_sample (lower));
4167 bbt.bars -= bbt.bars % bar_mod;
4171 while (pos >= 0 && pos < upper) {
4172 pos = sample_at_minute (minute_at_bbt_locked (_metrics, bbt));
4173 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_sample (pos));
4174 const double qn = pulse_at_bbt_locked (_metrics, bbt) * 4.0;
4176 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_sample (pos)), pos, bbt.bars, bbt.beats, qn));
4177 bbt.bars += bar_mod;
4183 TempoMap::tempo_section_at_sample (samplepos_t sample) const
4185 Glib::Threads::RWLock::ReaderLock lm (lock);
4187 return tempo_section_at_minute_locked (_metrics, minute_at_sample (sample));
4191 TempoMap::tempo_section_at_sample (samplepos_t sample)
4193 Glib::Threads::RWLock::ReaderLock lm (lock);
4195 return tempo_section_at_minute_locked (_metrics, minute_at_sample (sample));
4199 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4201 TempoSection* prev = 0;
4205 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4207 if ((*i)->is_tempo()) {
4208 t = static_cast<TempoSection*> (*i);
4212 if (prev && t->minute() > minute) {
4222 abort(); /*NOTREACHED*/
4228 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4230 TempoSection* prev = 0;
4234 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4236 if ((*i)->is_tempo()) {
4237 t = static_cast<TempoSection*> (*i);
4241 if (prev && t->minute() > minute) {
4251 abort(); /*NOTREACHED*/
4257 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4259 TempoSection* prev_t = 0;
4260 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4264 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4265 if ((*i)->is_tempo()) {
4266 t = static_cast<TempoSection*> (*i);
4272 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4282 abort(); /*NOTREACHED*/
4289 TempoMap::previous_tempo_section (TempoSection* ts) const
4291 Glib::Threads::RWLock::ReaderLock lm (lock);
4293 return previous_tempo_section_locked (_metrics, ts);
4298 TempoMap::previous_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4304 TempoSection* prev = 0;
4306 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4308 if ((*i)->is_tempo()) {
4309 TempoSection* t = static_cast<TempoSection*> (*i);
4315 if (prev && t == ts) {
4326 abort(); /*NOTREACHED*/
4333 TempoMap::next_tempo_section (TempoSection* ts) const
4335 Glib::Threads::RWLock::ReaderLock lm (lock);
4337 return next_tempo_section_locked (_metrics, ts);
4341 TempoMap::next_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4347 TempoSection* prev = 0;
4349 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4351 if ((*i)->is_tempo()) {
4352 TempoSection* t = static_cast<TempoSection*> (*i);
4358 if (prev && prev == ts) {
4369 abort(); /*NOTREACHED*/
4374 /* don't use this to calculate length (the tempo is only correct for this sample).
4375 * do that stuff based on the beat_at_sample and sample_at_beat api
4378 TempoMap::samples_per_quarter_note_at (const samplepos_t sample, const samplecnt_t sr) const
4380 Glib::Threads::RWLock::ReaderLock lm (lock);
4382 const TempoSection* ts_at = 0;
4383 const TempoSection* ts_after = 0;
4384 Metrics::const_iterator i;
4387 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4389 if ((*i)->is_tempo()) {
4390 t = static_cast<TempoSection*> (*i);
4394 if (ts_at && (*i)->sample() > sample) {
4404 return (60.0 * _sample_rate) / ts_at->tempo_at_minute (minute_at_sample (sample)).quarter_notes_per_minute();
4406 /* must be treated as constant tempo */
4407 return ts_at->samples_per_quarter_note (_sample_rate);
4411 TempoMap::meter_section_at_sample (samplepos_t sample) const
4413 Glib::Threads::RWLock::ReaderLock lm (lock);
4414 return meter_section_at_minute_locked (_metrics, minute_at_sample (sample));
4418 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4420 Metrics::const_iterator i;
4421 MeterSection* prev = 0;
4425 for (i = metrics.begin(); i != metrics.end(); ++i) {
4427 if (!(*i)->is_tempo()) {
4428 m = static_cast<MeterSection*> (*i);
4430 if (prev && (*i)->minute() > minute) {
4440 abort(); /*NOTREACHED*/
4447 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4449 MeterSection* prev_m = 0;
4451 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4453 if (!(*i)->is_tempo()) {
4454 m = static_cast<MeterSection*> (*i);
4455 if (prev_m && m->beat() > beat) {
4465 abort(); /*NOTREACHED*/
4472 TempoMap::meter_section_at_beat (double beat) const
4474 Glib::Threads::RWLock::ReaderLock lm (lock);
4475 return meter_section_at_beat_locked (_metrics, beat);
4479 TempoMap::meter_at_sample (samplepos_t sample) const
4481 TempoMetric m (metric_at (sample));
4486 TempoMap::fix_legacy_session ()
4488 MeterSection* prev_m = 0;
4489 TempoSection* prev_t = 0;
4490 bool have_initial_t = false;
4492 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4496 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4498 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4501 m->set_minute (0.0);
4502 m->set_position_lock_style (AudioTime);
4507 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4508 + (m->bbt().beats - 1)
4509 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4511 m->set_beat (start);
4512 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4513 + (m->bbt().beats - 1)
4514 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4515 m->set_pulse (start_beat / prev_m->note_divisor());
4518 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4523 /* Ramp type never existed in the era of this tempo section */
4524 t->set_end_note_types_per_minute (t->note_types_per_minute());
4528 t->set_minute (0.0);
4529 t->set_position_lock_style (AudioTime);
4531 have_initial_t = true;
4536 /* some 4.x sessions have no initial (non-movable) tempo. */
4537 if (!have_initial_t) {
4538 prev_t->set_pulse (0.0);
4539 prev_t->set_minute (0.0);
4540 prev_t->set_position_lock_style (AudioTime);
4541 prev_t->set_initial (true);
4542 prev_t->set_locked_to_meter (true);
4543 have_initial_t = true;
4546 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4547 + (t->legacy_bbt().beats - 1)
4548 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4550 t->set_pulse (beat / prev_m->note_divisor());
4552 /* really shouldn't happen but.. */
4553 t->set_pulse (beat / 4.0);
4561 TempoMap::fix_legacy_end_session ()
4563 TempoSection* prev_t = 0;
4565 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4568 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4575 if (prev_t->end_note_types_per_minute() < 0.0) {
4576 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4585 prev_t->set_end_note_types_per_minute (prev_t->note_types_per_minute());
4590 TempoMap::get_state ()
4592 Metrics::const_iterator i;
4593 XMLNode *root = new XMLNode ("TempoMap");
4596 Glib::Threads::RWLock::ReaderLock lm (lock);
4597 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4598 root->add_child_nocopy ((*i)->get_state());
4606 TempoMap::set_state (const XMLNode& node, int /*version*/)
4609 Glib::Threads::RWLock::WriterLock lm (lock);
4612 XMLNodeConstIterator niter;
4613 Metrics old_metrics (_metrics);
4616 nlist = node.children();
4618 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4619 XMLNode* child = *niter;
4621 if (child->name() == TempoSection::xml_state_node_name) {
4624 TempoSection* ts = new TempoSection (*child, _sample_rate);
4625 _metrics.push_back (ts);
4628 catch (failed_constructor& err){
4629 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4630 _metrics = old_metrics;
4631 old_metrics.clear();
4635 } else if (child->name() == MeterSection::xml_state_node_name) {
4638 MeterSection* ms = new MeterSection (*child, _sample_rate);
4639 _metrics.push_back (ms);
4642 catch (failed_constructor& err) {
4643 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4644 _metrics = old_metrics;
4645 old_metrics.clear();
4651 /* check for legacy sessions where bbt was the base musical unit for tempo */
4652 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4654 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4655 if (t->legacy_bbt().bars != 0) {
4656 fix_legacy_session();
4660 if (t->end_note_types_per_minute() < 0.0) {
4661 fix_legacy_end_session();
4667 if (niter == nlist.end()) {
4668 MetricSectionSorter cmp;
4669 _metrics.sort (cmp);
4672 /* check for multiple tempo/meters at the same location, which
4673 ardour2 somehow allowed.
4676 Metrics::iterator prev = _metrics.end();
4677 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4678 if (prev != _metrics.end()) {
4680 MeterSection* prev_m;
4682 TempoSection* prev_t;
4683 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4684 if (prev_m->beat() == ms->beat()) {
4685 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
4686 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
4689 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4690 if (prev_t->pulse() == ts->pulse()) {
4691 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4692 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4700 recompute_map (_metrics);
4702 Metrics::const_iterator d = old_metrics.begin();
4703 while (d != old_metrics.end()) {
4707 old_metrics.clear ();
4710 PropertyChanged (PropertyChange ());
4716 TempoMap::dump (std::ostream& o) const
4718 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4719 const MeterSection* m;
4720 const TempoSection* t;
4721 const TempoSection* prev_t = 0;
4723 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4725 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4726 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4727 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4728 << " minute= " << t->minute() << " sample= " << t->sample() << " (initial? " << t->initial() << ')'
4729 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4731 o << " current start : " << t->note_types_per_minute()
4732 << " current end : " << t->end_note_types_per_minute()
4733 << " | " << t->pulse() << " | " << t->sample() << " | " << t->minute() << std::endl;
4734 o << " previous : " << prev_t->note_types_per_minute()
4735 << " | " << prev_t->pulse() << " | " << prev_t->sample() << " | " << prev_t->minute() << std::endl;
4736 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4737 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4738 << " | " << sample_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4739 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4742 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4743 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4744 << " sample= " << m->sample() << " pulse: " << m->pulse() << " beat : " << m->beat()
4745 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4748 o << "------" << std::endl;
4752 TempoMap::n_tempos() const
4754 Glib::Threads::RWLock::ReaderLock lm (lock);
4757 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4758 if ((*i)->is_tempo()) {
4767 TempoMap::n_meters() const
4769 Glib::Threads::RWLock::ReaderLock lm (lock);
4772 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4773 if (!(*i)->is_tempo()) {
4782 TempoMap::insert_time (samplepos_t where, samplecnt_t amount)
4784 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4785 if ((*i)->sample() >= where && !(*i)->initial ()) {
4789 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4790 gui_set_meter_position (ms, (*i)->sample() + amount);
4793 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4794 gui_set_tempo_position (ts, (*i)->sample() + amount, 0);
4799 PropertyChanged (PropertyChange ());
4803 TempoMap::remove_time (samplepos_t where, samplecnt_t amount)
4807 std::list<MetricSection*> metric_kill_list;
4809 TempoSection* last_tempo = NULL;
4810 MeterSection* last_meter = NULL;
4811 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4812 bool meter_after = false; // is there a meter marker likewise?
4814 Glib::Threads::RWLock::WriterLock lm (lock);
4815 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4816 if ((*i)->sample() >= where && (*i)->sample() < where+amount) {
4817 metric_kill_list.push_back(*i);
4818 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4821 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4825 else if ((*i)->sample() >= where) {
4826 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4827 (*i)->set_minute ((*i)->minute() - minute_at_sample (amount));
4828 if ((*i)->sample() == where) {
4829 // marker was immediately after end of range
4830 tempo_after = dynamic_cast<TempoSection*> (*i);
4831 meter_after = dynamic_cast<MeterSection*> (*i);
4837 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4838 if (last_tempo && !tempo_after) {
4839 metric_kill_list.remove(last_tempo);
4840 last_tempo->set_minute (minute_at_sample (where));
4843 if (last_meter && !meter_after) {
4844 metric_kill_list.remove(last_meter);
4845 last_meter->set_minute (minute_at_sample (where));
4849 //remove all the remaining metrics
4850 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4851 _metrics.remove(*i);
4856 recompute_map (_metrics);
4859 PropertyChanged (PropertyChange ());
4863 /** Add some (fractional) Beats to a session sample position, and return the result in samples.
4864 * pos can be -ve, if required.
4867 TempoMap::samplepos_plus_qn (samplepos_t sample, Temporal::Beats beats) const
4869 Glib::Threads::RWLock::ReaderLock lm (lock);
4870 const double sample_qn = pulse_at_minute_locked (_metrics, minute_at_sample (sample)) * 4.0;
4872 return sample_at_minute (minute_at_pulse_locked (_metrics, (sample_qn + beats.to_double()) / 4.0));
4876 TempoMap::samplepos_plus_bbt (samplepos_t pos, BBT_Time op) const
4878 Glib::Threads::RWLock::ReaderLock lm (lock);
4880 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_sample (pos)));
4881 pos_bbt.ticks += op.ticks;
4882 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4884 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4886 pos_bbt.beats += op.beats;
4887 /* the meter in effect will start on the bar */
4888 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();
4889 while (pos_bbt.beats >= divisions_per_bar + 1) {
4891 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4892 pos_bbt.beats -= divisions_per_bar;
4894 pos_bbt.bars += op.bars;
4896 return sample_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4899 /** Count the number of beats that are equivalent to distance when going forward,
4903 TempoMap::framewalk_to_qn (samplepos_t pos, samplecnt_t distance) const
4905 Glib::Threads::RWLock::ReaderLock lm (lock);
4907 return Temporal::Beats (quarter_notes_between_samples_locked (_metrics, pos, pos + distance));
4911 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4917 operator<< (std::ostream& o, const Meter& m) {
4918 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4922 operator<< (std::ostream& o, const Tempo& t) {
4923 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4927 operator<< (std::ostream& o, const MetricSection& section) {
4929 o << "MetricSection @ " << section.sample() << ' ';
4931 const TempoSection* ts;
4932 const MeterSection* ms;
4934 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4935 o << *((const Tempo*) ts);
4936 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4937 o << *((const Meter*) ms);