2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
35 #include "ardour/types_convert.h"
41 using namespace ARDOUR;
44 using Timecode::BBT_Time;
46 /* _default tempo is 4/4 qtr=120 */
48 Meter TempoMap::_default_meter (4.0, 4.0);
49 Tempo TempoMap::_default_tempo (120.0, 4.0, 120.0);
52 MetricSection::frame_at_minute (const double& time) const
54 return (framepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
58 MetricSection::minute_at_frame (const framepos_t frame) const
60 return (frame / (double) _sample_rate) / 60.0;
63 /***********************************************************************/
66 ARDOUR::bbt_time_to_string (const BBT_Time& bbt, std::string& str)
69 int retval = snprintf (buf, sizeof(buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, bbt.bars, bbt.beats,
72 if (retval <= 0 || retval >= (int)sizeof(buf)) {
81 ARDOUR::string_to_bbt_time (const std::string& str, BBT_Time& bbt)
83 if (sscanf (str.c_str (), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, &bbt.bars, &bbt.beats,
91 /***********************************************************************/
94 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
96 /* This is tempo- and meter-sensitive. The number it returns
97 is based on the interval between any two lines in the
98 grid that is constructed from tempo and meter sections.
100 The return value IS NOT interpretable in terms of "beats".
103 return (60.0 * sr) / (tempo.note_types_per_minute() * (_note_type / tempo.note_type()));
107 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
109 return frames_per_grid (tempo, sr) * _divisions_per_bar;
112 /***********************************************************************/
115 MetricSection::add_state_to_node(XMLNode& node) const
117 node.set_property ("pulse", _pulse);
118 node.set_property ("frame", frame());
119 node.set_property ("movable", !_initial);
120 node.set_property ("lock-style", _position_lock_style);
124 MetricSection::set_state (const XMLNode& node, int /*version*/)
126 node.get_property ("pulse", _pulse);
129 if (node.get_property ("frame", frame)) {
130 set_minute (minute_at_frame (frame));
134 if (!node.get_property ("movable", tmp)) {
135 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
136 throw failed_constructor();
140 if (!node.get_property ("lock-style", _position_lock_style)) {
142 _position_lock_style = MusicTime;
144 _position_lock_style = AudioTime;
150 /***********************************************************************/
152 const string TempoSection::xml_state_node_name = "Tempo";
154 TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate)
155 : MetricSection (0.0, 0, MusicTime, true, sample_rate)
156 , Tempo (TempoMap::default_tempo())
159 , _locked_to_meter (false)
163 std::string start_bbt;
164 if (node.get_property ("start", start_bbt)) {
165 if (string_to_bbt_time (start_bbt, bbt)) {
166 /* legacy session - start used to be in bbt*/
169 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
173 // Don't worry about return value, exception will be thrown on error
174 MetricSection::set_state (node, Stateful::loading_state_version);
176 if (node.get_property ("beats-per-minute", _note_types_per_minute)) {
177 if (_note_types_per_minute < 0.0) {
178 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
179 throw failed_constructor();
183 if (node.get_property ("note-type", _note_type)) {
184 if (_note_type < 1.0) {
185 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
186 throw failed_constructor();
189 /* older session, make note type be quarter by default */
193 if (!node.get_property ("clamped", _clamped)) {
197 if (node.get_property ("end-beats-per-minute", _end_note_types_per_minute)) {
198 if (_end_note_types_per_minute < 0.0) {
199 info << _("TempoSection XML node has an illegal \"end-beats-per-minute\" value") << endmsg;
200 throw failed_constructor();
204 TempoSection::Type old_type;
205 if (node.get_property ("tempo-type", old_type)) {
206 /* sessions with a tempo-type node contain no end-beats-per-minute.
207 if the legacy node indicates a constant tempo, simply fill this in with the
208 start tempo. otherwise we need the next neighbour to know what it will be.
211 if (old_type == TempoSection::Constant) {
212 _end_note_types_per_minute = _note_types_per_minute;
214 _end_note_types_per_minute = -1.0;
218 if (!node.get_property ("active", _active)) {
219 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
223 if (!node.get_property ("locked-to-meter", _locked_to_meter)) {
225 set_locked_to_meter (true);
227 set_locked_to_meter (false);
231 /* 5.5 marked initial tempo as not locked to meter. this should always be true anyway */
233 set_locked_to_meter (true);
238 TempoSection::get_state() const
240 XMLNode *root = new XMLNode (xml_state_node_name);
242 MetricSection::add_state_to_node (*root);
244 root->set_property ("beats-per-minute", _note_types_per_minute);
245 root->set_property ("note-type", _note_type);
246 root->set_property ("clamped", _clamped);
247 root->set_property ("end-beats-per-minute", _end_note_types_per_minute);
248 root->set_property ("active", _active);
249 root->set_property ("locked-to-meter", _locked_to_meter);
254 /** returns the Tempo at the session-relative minute.
257 TempoSection::tempo_at_minute (const double& m) const
259 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
261 return Tempo (note_types_per_minute(), note_type());
264 return Tempo (_tempo_at_time (m - minute()), _note_type, _end_note_types_per_minute);
267 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
268 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
269 * @param p the pulse used to calculate the returned minute for constant tempi
270 * @return the minute at the supplied tempo
272 * note that the note_type is currently ignored in this function. see below.
276 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
277 * there should be no ramp between the two even if we are ramped.
278 * in other words a ramp should only place a curve on note_types_per_minute.
279 * we should be able to use Tempo note type here, but the above
280 * complicates things a bit.
283 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
285 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
287 return ((p - pulse()) / pulses_per_minute()) + minute();
290 return _time_at_tempo (ntpm) + minute();
293 /** returns the Tempo at the supplied whole-note pulse.
296 TempoSection::tempo_at_pulse (const double& p) const
298 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
301 return Tempo (note_types_per_minute(), note_type());
304 return Tempo (_tempo_at_pulse (p - pulse()), _note_type, _end_note_types_per_minute);
307 /** returns the whole-note pulse where a tempo in note types per minute occurs.
308 * constant tempi require minute m.
309 * @param ntpm the note types per minute value used to calculate the returned pulse
310 * @param m the minute used to calculate the returned pulse if the tempo is constant
311 * @return the whole-note pulse at the supplied tempo
313 * note that note_type is currently ignored in this function. see minute_at_tempo().
315 * for constant tempi, this is anaologous to pulse_at_minute().
318 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
320 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
322 return ((m - minute()) * pulses_per_minute()) + pulse();
325 return _pulse_at_tempo (ntpm) + pulse();
328 /** returns the whole-note pulse at the supplied session-relative minute.
331 TempoSection::pulse_at_minute (const double& m) const
333 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
335 return ((m - minute()) * pulses_per_minute()) + pulse();
338 return _pulse_at_time (m - minute()) + pulse();
341 /** returns the session-relative minute at the supplied whole-note pulse.
344 TempoSection::minute_at_pulse (const double& p) const
346 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
348 return ((p - pulse()) / pulses_per_minute()) + minute();
351 return _time_at_pulse (p - pulse()) + minute();
354 /** returns thw whole-note pulse at session frame position f.
355 * @param f the frame position.
356 * @return the position in whole-note pulses corresponding to f
358 * for use with musical units whose granularity is coarser than frames (e.g. ticks)
361 TempoSection::pulse_at_frame (const framepos_t f) const
363 const bool constant = type() == Constant || _c == 0.0 || (initial() && f < frame());
365 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
368 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
372 TempoSection::frame_at_pulse (const double& p) const
374 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
376 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
379 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
387 Tt----|-----------------*|
388 Ta----|--------------|* |
394 _______________|___|____
395 time a t (next tempo)
398 Duration in beats at time a is the integral of some Tempo function.
399 In our case, the Tempo function (Tempo at time t) is
402 with function constant
407 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
408 b(t) = T0(e^(ct) - 1) / c
410 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:
411 t(b) = log((c.b / T0) + 1) / c
413 The time t at which Tempo T occurs is a as above:
414 t(T) = log(T / T0) / c
416 The beat at which a Tempo T occurs is:
419 The Tempo at which beat b occurs is:
422 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
423 Our problem is that we usually don't know t.
424 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.
425 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
426 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
428 By substituting our expanded t as a in the c function above, our problem is reduced to:
429 c = T0 (e^(log (Ta / T0)) - 1) / b
431 Of course the word 'beat' has been left loosely defined above.
432 In music, a beat is defined by the musical pulse (which comes from the tempo)
433 and the meter in use at a particular time (how many pulse divisions there are in one bar).
434 It would be more accurate to substitute the work 'pulse' for 'beat' above.
438 We can now store c for future time calculations.
439 If the following tempo section (the one that defines c in conjunction with this one)
440 is changed or moved, c is no longer valid.
442 The public methods are session-relative.
444 Most of this stuff is taken from this paper:
447 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
450 Zurich University of Arts
451 Institute for Computer Music and Sound Technology
453 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
457 /** compute this ramp's function constant from some tempo-pulse point
458 * @param end_npm end tempo (in note types per minute)
459 * @param end_pulse duration (pulses into global start) of some other position.
460 * @return the calculated function constant
463 TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const
465 if (note_types_per_minute() == end_npm || type() == Constant) {
469 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
470 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
473 /** compute the function constant from some tempo-time point.
474 * @param end_npm tempo (note types/min.)
475 * @param end_minute distance (in minutes) from session origin
476 * @return the calculated function constant
479 TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const
481 if (note_types_per_minute() == end_npm || type() == Constant) {
485 return c_func (end_npm, end_minute - minute());
488 /* position function */
490 TempoSection::a_func (double end_npm, double c) const
492 return log (end_npm / note_types_per_minute()) / c;
495 /*function constant*/
497 TempoSection::c_func (double end_npm, double end_time) const
499 return log (end_npm / note_types_per_minute()) / end_time;
502 /* tempo in note types per minute at time in minutes */
504 TempoSection::_tempo_at_time (const double& time) const
506 return exp (_c * time) * note_types_per_minute();
509 /* time in minutes at tempo in note types per minute */
511 TempoSection::_time_at_tempo (const double& npm) const
513 return log (npm / note_types_per_minute()) / _c;
516 /* pulse at tempo in note types per minute */
518 TempoSection::_pulse_at_tempo (const double& npm) const
520 return ((npm - note_types_per_minute()) / _c) / _note_type;
523 /* tempo in note types per minute at pulse */
525 TempoSection::_tempo_at_pulse (const double& pulse) const
527 return (pulse * _note_type * _c) + note_types_per_minute();
530 /* pulse at time in minutes */
532 TempoSection::_pulse_at_time (const double& time) const
534 return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
537 /* time in minutes at pulse */
539 TempoSection::_time_at_pulse (const double& pulse) const
541 return log1p ((_c * pulse * _note_type) / note_types_per_minute()) / _c;
544 /***********************************************************************/
546 const string MeterSection::xml_state_node_name = "Meter";
548 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
549 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
551 pair<double, BBT_Time> start;
555 if (node.get_property ("start", bbt_str)) {
556 if (string_to_bbt_time (bbt_str, start.second)) {
557 /* legacy session - start used to be in bbt*/
558 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
561 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
565 MetricSection::set_state (node, Stateful::loading_state_version);
567 node.get_property ("beat", start.first);
569 if (node.get_property ("bbt", bbt_str)) {
570 if (!string_to_bbt_time (bbt_str, start.second)) {
571 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
572 throw failed_constructor();
575 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
580 /* beats-per-bar is old; divisions-per-bar is new */
582 if (!node.get_property ("divisions-per-bar", _divisions_per_bar)) {
583 if (!node.get_property ("beats-per-bar", _divisions_per_bar)) {
584 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
585 throw failed_constructor();
589 if (_divisions_per_bar < 0.0) {
590 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
591 throw failed_constructor();
594 if (!node.get_property ("note-type", _note_type)) {
595 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
596 throw failed_constructor();
599 if (_note_type < 0.0) {
600 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
601 throw failed_constructor();
606 MeterSection::get_state() const
608 XMLNode *root = new XMLNode (xml_state_node_name);
610 MetricSection::add_state_to_node (*root);
613 bbt_time_to_string (_bbt, bbt_str);
614 root->set_property ("bbt", bbt_str);
615 root->set_property ("beat", beat());
616 root->set_property ("note-type", _note_type);
617 root->set_property ("divisions-per-bar", _divisions_per_bar);
622 /***********************************************************************/
626 Tempo determines the rate of musical pulse determined by its components
627 note types per minute - the rate per minute of the whole note divisor _note_type
628 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
629 Meter divides the musical pulse into measures and beats according to its components
633 TempoSection - translates between time, musical pulse and tempo.
634 has a musical location in whole notes (pulses).
635 has a time location in minutes.
636 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
637 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
639 MeterSection - translates between BBT, meter-based beat and musical pulse.
640 has a musical location in whole notes (pulses)
641 has a musical location in meter-based beats
642 has a musical location in BBT time
643 has a time location expressed in minutes.
645 TempoSection and MeterSection may be locked to either audio or music (position lock style).
646 The lock style determines the location type to be kept as a reference when location is recalculated.
648 The first tempo and meter are special. they must move together, and are locked to audio.
649 Audio locked tempi which lie before the first meter are made inactive.
651 Recomputing the map is the process where the 'missing' location types are calculated.
652 We construct the tempo map by first using the locked location type of each section
653 to determine non-locked location types (pulse or minute position).
654 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
656 Having done this, we can now traverse the Metrics list by pulse or minute
657 to query its relevant meter/tempo.
659 It is important to keep the _metrics in an order that makes sense.
660 Because ramped MusicTime and AudioTime tempos can interact with each other,
661 reordering is frequent. Care must be taken to keep _metrics in a solved state.
662 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
666 Music and audio-locked objects may seem interchangeable on the surface, but when translating
667 between audio samples and beat, remember that a sample is only a quantised approximation
668 of the actual time (in minutes) of a beat.
669 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
670 mean that this frame is the actual location in time of 1|3|0.
672 You cannot use a frame measurement to determine beat distance except under special circumstances
673 (e.g. where the user has requested that a beat lie on a SMPTE frame or if the tempo is known to be constant over the duration).
675 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
676 sample space the user is operating at to be translated correctly to the object.
678 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
679 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
680 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
682 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
684 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
685 The result is rounded to audio frames.
686 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
689 frame_at_beat (beat_at_frame (frame)) == frame
691 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
693 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
694 frames_between_quarter_notes () eliminats this effect when determining time duration
695 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
697 The above pointless example could instead do:
698 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
700 The Shaggs - Things I Wonder
701 https://www.youtube.com/watch?v=9wQK6zMJOoQ
704 struct MetricSectionSorter {
705 bool operator() (const MetricSection* a, const MetricSection* b) {
706 return a->pulse() < b->pulse();
710 struct MetricSectionFrameSorter {
711 bool operator() (const MetricSection* a, const MetricSection* b) {
712 return a->frame() < b->frame();
716 TempoMap::TempoMap (framecnt_t fr)
719 BBT_Time start (1, 1, 0);
721 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo, AudioTime, fr);
722 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
724 t->set_initial (true);
725 t->set_locked_to_meter (true);
727 m->set_initial (true);
729 /* note: frame time is correct (zero) for both of these */
731 _metrics.push_back (t);
732 _metrics.push_back (m);
737 TempoMap::operator= (TempoMap const & other)
739 if (&other != this) {
740 Glib::Threads::RWLock::ReaderLock lr (other.lock);
741 Glib::Threads::RWLock::WriterLock lm (lock);
742 _frame_rate = other._frame_rate;
744 Metrics::const_iterator d = _metrics.begin();
745 while (d != _metrics.end()) {
751 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
752 TempoSection const * const ts = dynamic_cast<TempoSection const * const> (*m);
753 MeterSection const * const ms = dynamic_cast<MeterSection const * const> (*m);
756 TempoSection* new_section = new TempoSection (*ts);
757 _metrics.push_back (new_section);
759 MeterSection* new_section = new MeterSection (*ms);
760 _metrics.push_back (new_section);
765 PropertyChanged (PropertyChange());
770 TempoMap::~TempoMap ()
772 Metrics::const_iterator d = _metrics.begin();
773 while (d != _metrics.end()) {
781 TempoMap::frame_at_minute (const double time) const
783 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
787 TempoMap::minute_at_frame (const framepos_t frame) const
789 return (frame / (double) _frame_rate) / 60.0;
793 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
795 bool removed = false;
798 Glib::Threads::RWLock::WriterLock lm (lock);
799 if ((removed = remove_tempo_locked (tempo))) {
800 if (complete_operation) {
801 recompute_map (_metrics);
806 if (removed && complete_operation) {
807 PropertyChanged (PropertyChange ());
812 TempoMap::remove_tempo_locked (const TempoSection& tempo)
816 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
817 if (dynamic_cast<TempoSection*> (*i) != 0) {
818 if (tempo.frame() == (*i)->frame()) {
819 if (!(*i)->initial()) {
832 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
834 bool removed = false;
837 Glib::Threads::RWLock::WriterLock lm (lock);
838 if ((removed = remove_meter_locked (tempo))) {
839 if (complete_operation) {
840 recompute_map (_metrics);
845 if (removed && complete_operation) {
846 PropertyChanged (PropertyChange ());
851 TempoMap::remove_meter_locked (const MeterSection& meter)
854 if (meter.position_lock_style() == AudioTime) {
855 /* remove meter-locked tempo */
856 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
858 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
859 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
868 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
869 if (dynamic_cast<MeterSection*> (*i) != 0) {
870 if (meter.frame() == (*i)->frame()) {
871 if (!(*i)->initial()) {
884 TempoMap::do_insert (MetricSection* section)
886 bool need_add = true;
887 /* we only allow new meters to be inserted on beat 1 of an existing
891 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
893 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
895 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
896 corrected.second.beats = 1;
897 corrected.second.ticks = 0;
898 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
899 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
900 m->bbt(), corrected.second) << endmsg;
901 //m->set_pulse (corrected);
905 /* Look for any existing MetricSection that is of the same type and
906 in the same bar as the new one, and remove it before adding
907 the new one. Note that this means that if we find a matching,
908 existing section, we can break out of the loop since we're
909 guaranteed that there is only one such match.
912 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
914 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
915 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
916 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
917 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
919 if (tempo && insert_tempo) {
922 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
923 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
925 if (tempo->initial()) {
927 /* can't (re)move this section, so overwrite
928 * its data content (but not its properties as
932 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
933 (*i)->set_position_lock_style (AudioTime);
942 } else if (meter && insert_meter) {
946 bool const ipm = insert_meter->position_lock_style() == MusicTime;
948 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
950 if (meter->initial()) {
952 /* can't (re)move this section, so overwrite
953 * its data content (but not its properties as
957 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
958 (*i)->set_position_lock_style (AudioTime);
968 /* non-matching types, so we don't care */
972 /* Add the given MetricSection, if we didn't just reset an existing
977 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
978 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
982 TempoSection* prev_t = 0;
984 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
985 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
986 bool const ipm = insert_meter->position_lock_style() == MusicTime;
989 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
993 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
997 prev_t = dynamic_cast<TempoSection*> (*i);
1000 } else if (insert_tempo) {
1001 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1002 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1005 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1006 const bool lm = insert_tempo->locked_to_meter();
1007 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1008 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1015 _metrics.insert (i, section);
1019 /* user supplies the exact pulse if pls == MusicTime */
1021 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t frame, PositionLockStyle pls)
1023 if (tempo.note_types_per_minute() <= 0.0) {
1024 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1028 TempoSection* ts = 0;
1030 Glib::Threads::RWLock::WriterLock lm (lock);
1031 /* here we default to not clamped for a new tempo section. preference? */
1032 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, false, false);
1034 recompute_map (_metrics);
1037 PropertyChanged (PropertyChange ());
1043 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t frame, PositionLockStyle pls)
1045 if (tempo.note_types_per_minute() <= 0.0) {
1046 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1050 bool const locked_to_meter = ts.locked_to_meter();
1051 bool const ts_clamped = ts.clamped();
1052 TempoSection* new_ts = 0;
1055 Glib::Threads::RWLock::WriterLock lm (lock);
1056 TempoSection& first (first_tempo());
1057 if (!ts.initial()) {
1058 if (locked_to_meter) {
1060 /* cannot move a meter-locked tempo section */
1061 *static_cast<Tempo*>(&ts) = tempo;
1062 recompute_map (_metrics);
1065 remove_tempo_locked (ts);
1066 new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter, ts_clamped);
1067 /* enforce clampedness of next tempo section */
1068 TempoSection* next_t = next_tempo_section_locked (_metrics, new_ts);
1069 if (next_t && next_t->clamped()) {
1070 next_t->set_note_types_per_minute (new_ts->end_note_types_per_minute());
1075 first.set_pulse (0.0);
1076 first.set_minute (minute_at_frame (frame));
1077 first.set_position_lock_style (AudioTime);
1078 first.set_locked_to_meter (true);
1079 first.set_clamped (ts_clamped);
1081 /* cannot move the first tempo section */
1082 *static_cast<Tempo*>(&first) = tempo;
1085 recompute_map (_metrics);
1088 PropertyChanged (PropertyChange ());
1092 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1093 , PositionLockStyle pls, bool recompute, bool locked_to_meter, bool clamped)
1095 TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _frame_rate);
1096 t->set_locked_to_meter (locked_to_meter);
1097 t->set_clamped (clamped);
1101 TempoSection* prev_tempo = 0;
1102 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1103 TempoSection* const this_t = dynamic_cast<TempoSection*>(*i);
1106 if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1107 prev_tempo->set_end_note_types_per_minute (t->note_types_per_minute());
1111 prev_tempo = this_t;
1116 if (pls == AudioTime) {
1117 solve_map_minute (_metrics, t, t->minute());
1119 solve_map_pulse (_metrics, t, t->pulse());
1121 recompute_meters (_metrics);
1128 TempoMap::add_meter (const Meter& meter, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1130 MeterSection* m = 0;
1132 Glib::Threads::RWLock::WriterLock lm (lock);
1133 m = add_meter_locked (meter, where, frame, pls, true);
1138 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1143 PropertyChanged (PropertyChange ());
1148 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1151 Glib::Threads::RWLock::WriterLock lm (lock);
1153 if (!ms.initial()) {
1154 remove_meter_locked (ms);
1155 add_meter_locked (meter, where, frame, pls, true);
1157 MeterSection& first (first_meter());
1158 TempoSection& first_t (first_tempo());
1159 /* cannot move the first meter section */
1160 *static_cast<Meter*>(&first) = meter;
1161 first.set_position_lock_style (AudioTime);
1162 first.set_pulse (0.0);
1163 first.set_minute (minute_at_frame (frame));
1164 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1165 first.set_beat (beat);
1166 first_t.set_minute (first.minute());
1167 first_t.set_locked_to_meter (true);
1168 first_t.set_pulse (0.0);
1169 first_t.set_position_lock_style (AudioTime);
1170 recompute_map (_metrics);
1174 PropertyChanged (PropertyChange ());
1178 TempoMap::add_meter_locked (const Meter& meter, const BBT_Time& bbt, framepos_t frame, PositionLockStyle pls, bool recompute)
1180 double const minute_at_bbt = minute_at_bbt_locked (_metrics, bbt);
1181 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_bbt - minute_at_frame (1));
1182 double const pulse = ((bbt.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1183 /* the natural time of the BBT position */
1184 double const time_minutes = minute_at_pulse_locked (_metrics, pulse);
1186 if (pls == AudioTime) {
1187 /* add meter-locked tempo at the natural time in the current map (frame may differ). */
1188 Tempo const tempo_at_time = tempo_at_minute_locked (_metrics, time_minutes);
1189 TempoSection* mlt = add_tempo_locked (tempo_at_time, pulse, time_minutes, AudioTime, true, true, false);
1195 /* still using natural time for the position, ignoring lock style. */
1196 MeterSection* new_meter = new MeterSection (pulse, time_minutes, beat_at_bbt_locked (_metrics, bbt), bbt, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1198 bool solved = false;
1200 do_insert (new_meter);
1204 if (pls == AudioTime) {
1205 /* now set the audio locked meter's position to frame */
1206 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1207 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1208 fudge frame so that the meter ends up at its BBT position instead.
1211 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1214 solved = solve_map_bbt (_metrics, new_meter, bbt);
1215 /* required due to resetting the pulse of meter-locked tempi above.
1216 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1217 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1219 recompute_map (_metrics);
1223 if (!solved && recompute) {
1224 /* if this has failed to solve, there is little we can do other than to ensure that
1225 the new map is recalculated.
1227 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1228 recompute_map (_metrics);
1235 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1237 Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1240 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1241 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1246 Glib::Threads::RWLock::WriterLock lm (lock);
1247 *((Tempo*) t) = newtempo;
1248 recompute_map (_metrics);
1250 PropertyChanged (PropertyChange ());
1257 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1259 Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1262 TempoSection* first;
1263 Metrics::iterator i;
1265 /* find the TempoSection immediately preceding "where"
1268 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1270 if ((*i)->frame() > where) {
1276 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1289 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1299 Glib::Threads::RWLock::WriterLock lm (lock);
1300 /* cannot move the first tempo section */
1301 *((Tempo*)prev) = newtempo;
1302 recompute_map (_metrics);
1305 PropertyChanged (PropertyChange ());
1309 TempoMap::first_meter () const
1311 const MeterSection *m = 0;
1313 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1314 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1319 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1320 abort(); /*NOTREACHED*/
1325 TempoMap::first_meter ()
1327 MeterSection *m = 0;
1329 /* CALLER MUST HOLD LOCK */
1331 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1332 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1337 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1338 abort(); /*NOTREACHED*/
1343 TempoMap::first_tempo () const
1345 const TempoSection *t = 0;
1347 /* CALLER MUST HOLD LOCK */
1349 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1350 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1360 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1361 abort(); /*NOTREACHED*/
1366 TempoMap::first_tempo ()
1368 TempoSection *t = 0;
1370 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1371 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1381 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1382 abort(); /*NOTREACHED*/
1386 TempoMap::recompute_tempi (Metrics& metrics)
1388 TempoSection* prev_t = 0;
1390 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1393 if ((*i)->is_tempo()) {
1394 t = static_cast<TempoSection*> (*i);
1407 if (t->position_lock_style() == AudioTime) {
1408 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1409 if (!t->locked_to_meter()) {
1410 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1414 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1415 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1423 prev_t->set_c (0.0);
1426 /* tempos must be positioned correctly.
1427 the current approach is to use a meter's bbt time as its base position unit.
1428 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1429 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1432 TempoMap::recompute_meters (Metrics& metrics)
1434 MeterSection* meter = 0;
1435 MeterSection* prev_m = 0;
1437 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1438 if (!(*mi)->is_tempo()) {
1439 meter = static_cast<MeterSection*> (*mi);
1440 if (meter->position_lock_style() == AudioTime) {
1442 pair<double, BBT_Time> b_bbt;
1443 TempoSection* meter_locked_tempo = 0;
1444 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1446 if ((*ii)->is_tempo()) {
1447 t = static_cast<TempoSection*> (*ii);
1448 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1449 meter_locked_tempo = t;
1456 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1457 if (beats + prev_m->beat() != meter->beat()) {
1458 /* reordering caused a bbt change */
1460 beats = meter->beat() - prev_m->beat();
1461 b_bbt = make_pair (beats + prev_m->beat()
1462 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1463 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1465 } else if (!meter->initial()) {
1466 b_bbt = make_pair (meter->beat(), meter->bbt());
1467 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1470 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1472 if (meter_locked_tempo) {
1473 meter_locked_tempo->set_pulse (pulse);
1475 meter->set_beat (b_bbt);
1476 meter->set_pulse (pulse);
1481 pair<double, BBT_Time> b_bbt;
1483 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1484 if (beats + prev_m->beat() != meter->beat()) {
1485 /* reordering caused a bbt change */
1486 b_bbt = make_pair (beats + prev_m->beat()
1487 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1489 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1491 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1493 /* shouldn't happen - the first is audio-locked */
1494 pulse = pulse_at_beat_locked (metrics, meter->beat());
1495 b_bbt = make_pair (meter->beat(), meter->bbt());
1498 meter->set_beat (b_bbt);
1499 meter->set_pulse (pulse);
1500 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1509 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1511 /* CALLER MUST HOLD WRITE LOCK */
1513 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1516 /* silly call from Session::process() during startup
1521 recompute_tempi (metrics);
1522 recompute_meters (metrics);
1526 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1528 Glib::Threads::RWLock::ReaderLock lm (lock);
1529 TempoMetric m (first_meter(), first_tempo());
1532 *last = ++_metrics.begin();
1535 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1536 at something, because we insert the default tempo and meter during
1537 TempoMap construction.
1539 now see if we can find better candidates.
1542 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1544 if ((*i)->frame() > frame) {
1558 /* XX meters only */
1560 TempoMap::metric_at (BBT_Time bbt) const
1562 Glib::Threads::RWLock::ReaderLock lm (lock);
1563 TempoMetric m (first_meter(), first_tempo());
1565 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1566 at something, because we insert the default tempo and meter during
1567 TempoMap construction.
1569 now see if we can find better candidates.
1572 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1574 if (!(*i)->is_tempo()) {
1575 mw = static_cast<MeterSection*> (*i);
1576 BBT_Time section_start (mw->bbt());
1578 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1589 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1590 * @param frame The session frame position.
1591 * @return The beat duration according to the tempo map at the supplied frame.
1593 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1594 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1596 * This function uses both tempo and meter.
1599 TempoMap::beat_at_frame (const framecnt_t frame) const
1601 Glib::Threads::RWLock::ReaderLock lm (lock);
1603 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1606 /* This function uses both tempo and meter.*/
1608 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1610 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1611 MeterSection* prev_m = 0;
1612 MeterSection* next_m = 0;
1614 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1615 if (!(*i)->is_tempo()) {
1616 if (prev_m && (*i)->minute() > minute) {
1617 next_m = static_cast<MeterSection*> (*i);
1620 prev_m = static_cast<MeterSection*> (*i);
1624 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1626 /* audio locked meters fake their beat */
1627 if (next_m && next_m->beat() < beat) {
1628 return next_m->beat();
1634 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1635 * @param beat The BBT (meter-based) beat.
1636 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1638 * This function uses both tempo and meter.
1641 TempoMap::frame_at_beat (const double& beat) const
1643 Glib::Threads::RWLock::ReaderLock lm (lock);
1645 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1648 /* meter & tempo section based */
1650 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1652 MeterSection* prev_m = 0;
1653 TempoSection* prev_t = 0;
1657 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1658 if (!(*i)->is_tempo()) {
1659 m = static_cast<MeterSection*> (*i);
1660 if (prev_m && m->beat() > beat) {
1670 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1671 if ((*i)->is_tempo()) {
1672 t = static_cast<TempoSection*> (*i);
1678 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1687 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1690 /** Returns a Tempo corresponding to the supplied frame position.
1691 * @param frame The audio frame.
1692 * @return a Tempo according to the tempo map at the supplied frame.
1696 TempoMap::tempo_at_frame (const framepos_t frame) const
1698 Glib::Threads::RWLock::ReaderLock lm (lock);
1700 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1704 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1706 TempoSection* prev_t = 0;
1710 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1711 if ((*i)->is_tempo()) {
1712 t = static_cast<TempoSection*> (*i);
1716 if ((prev_t) && t->minute() > minute) {
1717 /* t is the section past frame */
1718 return prev_t->tempo_at_minute (minute);
1724 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1727 /** returns the frame at which the supplied tempo occurs, or
1728 * the frame of the last tempo section (search exhausted)
1729 * only the position of the first occurence will be returned
1733 TempoMap::frame_at_tempo (const Tempo& tempo) const
1735 Glib::Threads::RWLock::ReaderLock lm (lock);
1737 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1741 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1743 TempoSection* prev_t = 0;
1744 const double tempo_bpm = tempo.note_types_per_minute();
1746 Metrics::const_iterator i;
1748 for (i = metrics.begin(); i != metrics.end(); ++i) {
1750 if ((*i)->is_tempo()) {
1751 t = static_cast<TempoSection*> (*i);
1759 if (t->note_types_per_minute() == tempo_bpm) {
1764 const double prev_t_bpm = prev_t->note_types_per_minute();
1765 const double prev_t_end_bpm = prev_t->end_note_types_per_minute();
1766 if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm)
1767 || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm)
1768 || (prev_t_end_bpm == tempo_bpm)) {
1770 return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
1777 return prev_t->minute();
1781 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1783 TempoSection* prev_t = 0;
1787 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1788 if ((*i)->is_tempo()) {
1789 t = static_cast<TempoSection*> (*i);
1793 if ((prev_t) && t->pulse() > pulse) {
1794 /* t is the section past frame */
1795 return prev_t->tempo_at_pulse (pulse);
1801 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1805 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1807 TempoSection* prev_t = 0;
1808 const double tempo_bpm = tempo.note_types_per_minute();
1810 Metrics::const_iterator i;
1812 for (i = metrics.begin(); i != metrics.end(); ++i) {
1814 if ((*i)->is_tempo()) {
1815 t = static_cast<TempoSection*> (*i);
1821 const double t_bpm = t->note_types_per_minute();
1823 if (t_bpm == tempo_bpm) {
1828 const double prev_t_bpm = prev_t->note_types_per_minute();
1830 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1831 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1838 return prev_t->pulse();
1841 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1842 * @param qn the position in quarter note beats.
1843 * @return the Tempo at the supplied quarter-note.
1846 TempoMap::tempo_at_quarter_note (const double& qn) const
1848 Glib::Threads::RWLock::ReaderLock lm (lock);
1850 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1853 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1854 * @param tempo the tempo.
1855 * @return the position in quarter-note beats where the map bpm
1856 * is equal to that of the Tempo. currently ignores note_type.
1859 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1861 Glib::Threads::RWLock::ReaderLock lm (lock);
1863 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;
1866 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1867 * @param metrics the list of metric sections used to calculate the pulse.
1868 * @param beat The BBT (meter-based) beat.
1869 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1871 * a pulse or whole note is the base musical position of a MetricSection.
1872 * it is equivalent to four quarter notes.
1876 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1878 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1880 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1883 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1884 * @param metrics the list of metric sections used to calculate the beat.
1885 * @param pulse the whole-note pulse.
1886 * @return the meter-based beat at the supplied whole-note pulse.
1888 * a pulse or whole note is the base musical position of a MetricSection.
1889 * it is equivalent to four quarter notes.
1892 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1894 MeterSection* prev_m = 0;
1896 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1898 if (!(*i)->is_tempo()) {
1899 m = static_cast<MeterSection*> (*i);
1900 if (prev_m && m->pulse() > pulse) {
1908 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1912 /* tempo section based */
1914 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1916 /* HOLD (at least) THE READER LOCK */
1917 TempoSection* prev_t = 0;
1919 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1921 if ((*i)->is_tempo()) {
1922 t = static_cast<TempoSection*> (*i);
1926 if (prev_t && t->minute() > minute) {
1927 /*the previous ts is the one containing the frame */
1928 const double ret = prev_t->pulse_at_minute (minute);
1929 /* audio locked section in new meter*/
1930 if (t->pulse() < ret) {
1939 /* treated as constant for this ts */
1940 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1942 return pulses_in_section + prev_t->pulse();
1945 /* tempo section based */
1947 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1949 /* HOLD THE READER LOCK */
1951 const TempoSection* prev_t = 0;
1953 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1956 if ((*i)->is_tempo()) {
1957 t = static_cast<TempoSection*> (*i);
1961 if (prev_t && t->pulse() > pulse) {
1962 return prev_t->minute_at_pulse (pulse);
1968 /* must be treated as constant, irrespective of _type */
1969 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1971 return dtime + prev_t->minute();
1974 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1975 * @param bbt The BBT time (meter-based).
1976 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1980 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1982 Glib::Threads::RWLock::ReaderLock lm (lock);
1983 return beat_at_bbt_locked (_metrics, bbt);
1988 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1990 /* CALLER HOLDS READ LOCK */
1992 MeterSection* prev_m = 0;
1994 /* because audio-locked meters have 'fake' integral beats,
1995 there is no pulse offset here.
1999 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2000 if (!(*i)->is_tempo()) {
2001 m = static_cast<MeterSection*> (*i);
2003 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2004 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2012 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2013 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2014 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2019 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2020 * @param beat The BBT (meter-based) beat.
2021 * @return The BBT time (meter-based) at the supplied meter-based beat.
2025 TempoMap::bbt_at_beat (const double& beat)
2027 Glib::Threads::RWLock::ReaderLock lm (lock);
2028 return bbt_at_beat_locked (_metrics, beat);
2032 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2034 /* CALLER HOLDS READ LOCK */
2035 MeterSection* prev_m = 0;
2036 const double beats = max (0.0, b);
2038 MeterSection* m = 0;
2040 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2041 if (!(*i)->is_tempo()) {
2042 m = static_cast<MeterSection*> (*i);
2044 if (m->beat() > beats) {
2045 /* this is the meter after the one our beat is on*/
2055 const double beats_in_ms = beats - prev_m->beat();
2056 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2057 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2058 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2059 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2063 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2064 ret.beats = (uint32_t) floor (remaining_beats);
2065 ret.bars = total_bars;
2067 /* 0 0 0 to 1 1 0 - based mapping*/
2071 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2073 ret.ticks -= BBT_Time::ticks_per_beat;
2076 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2084 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2085 * @param bbt The BBT time (meter-based).
2086 * @return the quarter note beat at the supplied BBT time
2088 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2090 * while the input uses meter, the output does not.
2093 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2095 Glib::Threads::RWLock::ReaderLock lm (lock);
2097 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2101 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2103 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2106 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2109 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2113 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2115 /* CALLER HOLDS READ LOCK */
2117 MeterSection* prev_m = 0;
2119 /* because audio-locked meters have 'fake' integral beats,
2120 there is no pulse offset here.
2124 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2125 if (!(*i)->is_tempo()) {
2126 m = static_cast<MeterSection*> (*i);
2128 if (m->bbt().bars > bbt.bars) {
2136 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2137 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2138 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2143 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2144 * @param qn the quarter-note beat.
2145 * @return The BBT time (meter-based) at the supplied meter-based beat.
2147 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2151 TempoMap::bbt_at_quarter_note (const double& qn)
2153 Glib::Threads::RWLock::ReaderLock lm (lock);
2155 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2158 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2159 * @param metrics The list of metric sections used to determine the result.
2160 * @param pulse The whole-note pulse.
2161 * @return The BBT time at the supplied whole-note pulse.
2163 * a pulse or whole note is the basic musical position of a MetricSection.
2164 * it is equivalent to four quarter notes.
2165 * while the output uses meter, the input does not.
2168 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2170 MeterSection* prev_m = 0;
2172 MeterSection* m = 0;
2174 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2176 if (!(*i)->is_tempo()) {
2177 m = static_cast<MeterSection*> (*i);
2180 double const pulses_to_m = m->pulse() - prev_m->pulse();
2181 if (prev_m->pulse() + pulses_to_m > pulse) {
2182 /* this is the meter after the one our beat is on*/
2193 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2194 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2195 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2196 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2197 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2201 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2202 ret.beats = (uint32_t) floor (remaining_beats);
2203 ret.bars = total_bars;
2205 /* 0 0 0 to 1 1 0 mapping*/
2209 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2211 ret.ticks -= BBT_Time::ticks_per_beat;
2214 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2222 /** Returns the BBT time corresponding to the supplied frame position.
2223 * @param frame the position in audio samples.
2224 * @return the BBT time at the frame position .
2228 TempoMap::bbt_at_frame (framepos_t frame)
2236 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2241 const double minute = minute_at_frame (frame);
2243 Glib::Threads::RWLock::ReaderLock lm (lock);
2245 return bbt_at_minute_locked (_metrics, minute);
2249 TempoMap::bbt_at_frame_rt (framepos_t frame)
2251 const double minute = minute_at_frame (frame);
2253 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2256 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2259 return bbt_at_minute_locked (_metrics, minute);
2263 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2273 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2274 MeterSection* prev_m = 0;
2275 MeterSection* next_m = 0;
2279 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2280 if (!(*i)->is_tempo()) {
2281 m = static_cast<MeterSection*> (*i);
2282 if (prev_m && m->minute() > minute) {
2290 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2292 /* handle frame before first meter */
2293 if (minute < prev_m->minute()) {
2296 /* audio locked meters fake their beat */
2297 if (next_m && next_m->beat() < beat) {
2298 beat = next_m->beat();
2301 beat = max (0.0, beat);
2303 const double beats_in_ms = beat - prev_m->beat();
2304 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2305 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2306 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2307 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2311 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2312 ret.beats = (uint32_t) floor (remaining_beats);
2313 ret.bars = total_bars;
2315 /* 0 0 0 to 1 1 0 - based mapping*/
2319 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2321 ret.ticks -= BBT_Time::ticks_per_beat;
2324 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2332 /** Returns the frame position corresponding to the supplied BBT time.
2333 * @param bbt the position in BBT time.
2334 * @return the frame position at bbt.
2338 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2342 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2347 if (bbt.beats < 1) {
2348 throw std::logic_error ("beats are counted from one");
2353 Glib::Threads::RWLock::ReaderLock lm (lock);
2354 minute = minute_at_bbt_locked (_metrics, bbt);
2357 return frame_at_minute (minute);
2360 /* meter & tempo section based */
2362 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2364 /* HOLD THE READER LOCK */
2366 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2371 * Returns the quarter-note beat position corresponding to the supplied frame.
2373 * @param frame the position in frames.
2374 * @return The quarter-note position of the supplied frame. Ignores meter.
2378 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2380 const double minute = minute_at_frame (frame);
2382 Glib::Threads::RWLock::ReaderLock lm (lock);
2384 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2388 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2390 const double minute = minute_at_frame (frame);
2392 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2395 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2398 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2402 * Returns the frame position corresponding to the supplied quarter-note beat.
2404 * @param quarter_note the quarter-note position.
2405 * @return the frame position of the supplied quarter-note. Ignores meter.
2410 TempoMap::frame_at_quarter_note (const double quarter_note) const
2414 Glib::Threads::RWLock::ReaderLock lm (lock);
2416 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2419 return frame_at_minute (minute);
2422 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2423 * @param beat The BBT (meter-based) beat.
2424 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2426 * a quarter-note may be compared with and assigned to Evoral::Beats.
2430 TempoMap::quarter_note_at_beat (const double beat) const
2432 Glib::Threads::RWLock::ReaderLock lm (lock);
2434 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2437 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2438 * @param quarter_note The position in quarter-note beats.
2439 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2441 * a quarter-note is the musical unit of Evoral::Beats.
2445 TempoMap::beat_at_quarter_note (const double quarter_note) const
2447 Glib::Threads::RWLock::ReaderLock lm (lock);
2449 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2452 /** Returns the duration in frames between two supplied quarter-note beat positions.
2453 * @param start the first position in quarter-note beats.
2454 * @param end the end position in quarter-note beats.
2455 * @return the frame distance ober the quarter-note beats duration.
2457 * use this rather than e.g.
2458 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2459 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2463 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2468 Glib::Threads::RWLock::ReaderLock lm (lock);
2469 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2472 return frame_at_minute (minutes);
2476 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2479 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2483 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2485 Glib::Threads::RWLock::ReaderLock lm (lock);
2487 return quarter_notes_between_frames_locked (_metrics, start, end);
2491 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2493 const TempoSection* prev_t = 0;
2495 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2498 if ((*i)->is_tempo()) {
2499 t = static_cast<TempoSection*> (*i);
2503 if (prev_t && t->frame() > start) {
2510 const double start_qn = prev_t->pulse_at_frame (start);
2512 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2515 if ((*i)->is_tempo()) {
2516 t = static_cast<TempoSection*> (*i);
2520 if (prev_t && t->frame() > end) {
2526 const double end_qn = prev_t->pulse_at_frame (end);
2528 return (end_qn - start_qn) * 4.0;
2532 TempoMap::check_solved (const Metrics& metrics) const
2534 TempoSection* prev_t = 0;
2535 MeterSection* prev_m = 0;
2537 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2540 if ((*i)->is_tempo()) {
2541 t = static_cast<TempoSection*> (*i);
2546 /* check ordering */
2547 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2551 /* precision check ensures tempo and frames align.*/
2552 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2553 if (!t->locked_to_meter()) {
2558 /* gradient limit - who knows what it should be?
2559 things are also ok (if a little chaotic) without this
2561 if (fabs (prev_t->c()) > 1000.0) {
2562 //std::cout << "c : " << prev_t->c() << std::endl;
2569 if (!(*i)->is_tempo()) {
2570 m = static_cast<MeterSection*> (*i);
2571 if (prev_m && m->position_lock_style() == AudioTime) {
2572 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2573 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2574 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2576 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2590 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t frame)
2592 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2594 if ((*i)->is_tempo()) {
2595 t = static_cast<TempoSection*> (*i);
2596 if (t->locked_to_meter()) {
2597 t->set_active (true);
2598 } else if (t->position_lock_style() == AudioTime) {
2599 if (t->frame() < frame) {
2600 t->set_active (false);
2601 t->set_pulse (-1.0);
2602 } else if (t->frame() > frame) {
2603 t->set_active (true);
2604 } else if (t->frame() == frame) {
2614 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2616 TempoSection* prev_t = 0;
2617 TempoSection* section_prev = 0;
2618 double first_m_minute = 0.0;
2619 const bool sml = section->locked_to_meter();
2621 /* can't move a tempo before the first meter */
2622 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2624 if (!(*i)->is_tempo()) {
2625 m = static_cast<MeterSection*> (*i);
2627 first_m_minute = m->minute();
2632 if (!section->initial() && minute <= first_m_minute) {
2636 section->set_active (true);
2637 section->set_minute (minute);
2639 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2641 if ((*i)->is_tempo()) {
2642 t = static_cast<TempoSection*> (*i);
2654 if (t->frame() == frame_at_minute (minute)) {
2658 const bool tlm = t->position_lock_style() == MusicTime;
2660 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2661 section_prev = prev_t;
2663 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2664 if (!section->locked_to_meter()) {
2665 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2670 if (t->position_lock_style() == MusicTime) {
2671 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2672 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2674 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2675 if (!t->locked_to_meter()) {
2676 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2684 MetricSectionFrameSorter fcmp;
2685 imaginary.sort (fcmp);
2687 recompute_tempi (imaginary);
2689 if (check_solved (imaginary)) {
2697 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2699 TempoSection* prev_t = 0;
2700 TempoSection* section_prev = 0;
2702 section->set_pulse (pulse);
2704 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2706 if ((*i)->is_tempo()) {
2707 t = static_cast<TempoSection*> (*i);
2718 section_prev = prev_t;
2722 if (t->position_lock_style() == MusicTime) {
2723 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2724 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2726 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2727 if (!t->locked_to_meter()) {
2728 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2737 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2738 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2741 MetricSectionSorter cmp;
2742 imaginary.sort (cmp);
2744 recompute_tempi (imaginary);
2746 * XX need a restriction here, but only for this case,
2747 * as audio locked tempos don't interact in the same way.
2749 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2751 * |50 bpm |250 bpm |60 bpm
2752 * drag 250 to the pulse after 60->
2753 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2755 if (check_solved (imaginary)) {
2763 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2765 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2766 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2767 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2771 if (section->initial()) {
2772 /* lock the first tempo to our first meter */
2773 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2778 TempoSection* meter_locked_tempo = 0;
2780 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2782 if ((*ii)->is_tempo()) {
2783 t = static_cast<TempoSection*> (*ii);
2784 if (t->locked_to_meter() && t->frame() == section->frame()) {
2785 meter_locked_tempo = t;
2791 if (!meter_locked_tempo) {
2795 MeterSection* prev_m = 0;
2797 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2798 bool solved = false;
2800 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2802 if (!(*i)->is_tempo()) {
2803 m = static_cast<MeterSection*> (*i);
2805 if (prev_m && !section->initial()) {
2806 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2807 if (beats + prev_m->beat() < section->beat()) {
2808 /* set the section pulse according to its musical position,
2809 * as an earlier time than this has been requested.
2811 const double new_pulse = ((section->beat() - prev_m->beat())
2812 / prev_m->note_divisor()) + prev_m->pulse();
2814 tempo_copy->set_position_lock_style (MusicTime);
2815 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2816 meter_locked_tempo->set_position_lock_style (MusicTime);
2817 section->set_position_lock_style (MusicTime);
2818 section->set_pulse (new_pulse);
2819 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2820 meter_locked_tempo->set_position_lock_style (AudioTime);
2821 section->set_position_lock_style (AudioTime);
2822 section->set_minute (meter_locked_tempo->minute());
2828 Metrics::const_iterator d = future_map.begin();
2829 while (d != future_map.end()) {
2838 /* all is ok. set section's locked tempo if allowed.
2839 possibly disallow if there is an adjacent audio-locked tempo.
2840 XX this check could possibly go. its never actually happened here.
2842 MeterSection* meter_copy = const_cast<MeterSection*>
2843 (&meter_section_at_minute_locked (future_map, section->minute()));
2845 meter_copy->set_minute (minute);
2847 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2848 section->set_minute (minute);
2849 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2850 / prev_m->note_divisor()) + prev_m->pulse());
2851 solve_map_minute (imaginary, meter_locked_tempo, minute);
2856 Metrics::const_iterator d = future_map.begin();
2857 while (d != future_map.end()) {
2867 /* initial (first meter atm) */
2869 tempo_copy->set_minute (minute);
2870 tempo_copy->set_pulse (0.0);
2872 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2873 section->set_minute (minute);
2874 meter_locked_tempo->set_minute (minute);
2875 meter_locked_tempo->set_pulse (0.0);
2876 solve_map_minute (imaginary, meter_locked_tempo, minute);
2881 Metrics::const_iterator d = future_map.begin();
2882 while (d != future_map.end()) {
2891 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2892 section->set_beat (b_bbt);
2893 section->set_pulse (0.0);
2903 MetricSectionFrameSorter fcmp;
2904 imaginary.sort (fcmp);
2906 recompute_meters (imaginary);
2912 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2914 /* disallow setting section to an existing meter's bbt */
2915 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2917 if (!(*i)->is_tempo()) {
2918 m = static_cast<MeterSection*> (*i);
2919 if (m != section && m->bbt().bars == when.bars) {
2925 MeterSection* prev_m = 0;
2926 MeterSection* section_prev = 0;
2928 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2930 if (!(*i)->is_tempo()) {
2931 m = static_cast<MeterSection*> (*i);
2937 pair<double, BBT_Time> b_bbt;
2938 double new_pulse = 0.0;
2940 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2941 section_prev = prev_m;
2943 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2944 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2945 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2947 section->set_beat (b_bbt);
2948 section->set_pulse (pulse);
2949 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2953 if (m->position_lock_style() == AudioTime) {
2954 TempoSection* meter_locked_tempo = 0;
2956 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2958 if ((*ii)->is_tempo()) {
2959 t = static_cast<TempoSection*> (*ii);
2960 if (t->locked_to_meter() && t->frame() == m->frame()) {
2961 meter_locked_tempo = t;
2967 if (!meter_locked_tempo) {
2972 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2974 if (beats + prev_m->beat() != m->beat()) {
2975 /* tempo/ meter change caused a change in beat (bar). */
2977 /* the user has requested that the previous section of music overlaps this one.
2978 we have no choice but to change the bar number here, as being locked to audio means
2979 we must stay where we are on the timeline.
2981 beats = m->beat() - prev_m->beat();
2982 b_bbt = make_pair (beats + prev_m->beat()
2983 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2984 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2986 } else if (!m->initial()) {
2987 b_bbt = make_pair (m->beat(), m->bbt());
2988 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2991 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2994 meter_locked_tempo->set_pulse (new_pulse);
2995 m->set_beat (b_bbt);
2996 m->set_pulse (new_pulse);
3000 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3001 if (beats + prev_m->beat() != m->beat()) {
3002 /* tempo/ meter change caused a change in beat (bar). */
3003 b_bbt = make_pair (beats + prev_m->beat()
3004 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3006 b_bbt = make_pair (beats + prev_m->beat()
3009 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3010 m->set_beat (b_bbt);
3011 m->set_pulse (new_pulse);
3012 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3019 if (!section_prev) {
3021 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3022 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3023 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3025 section->set_beat (b_bbt);
3026 section->set_pulse (pulse);
3027 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3030 MetricSectionSorter cmp;
3031 imaginary.sort (cmp);
3033 recompute_meters (imaginary);
3038 /** places a copy of _metrics into copy and returns a pointer
3039 * to section's equivalent in copy.
3042 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section) const
3044 TempoSection* ret = 0;
3046 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3047 if ((*i)->is_tempo()) {
3048 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3050 ret = new TempoSection (*t);
3051 copy.push_back (ret);
3055 TempoSection* cp = new TempoSection (*t);
3056 copy.push_back (cp);
3058 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3059 MeterSection* cp = new MeterSection (*m);
3060 copy.push_back (cp);
3068 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section) const
3070 MeterSection* ret = 0;
3072 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3073 if ((*i)->is_tempo()) {
3074 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3075 TempoSection* cp = new TempoSection (*t);
3076 copy.push_back (cp);
3078 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3080 ret = new MeterSection (*m);
3081 copy.push_back (ret);
3084 MeterSection* cp = new MeterSection (*m);
3085 copy.push_back (cp);
3092 /** answers the question "is this a valid beat position for this tempo section?".
3093 * it returns true if the tempo section can be moved to the requested bbt position,
3094 * leaving the tempo map in a solved state.
3095 * @param ts the tempo section to be moved
3096 * @param bbt the requested new position for the tempo section
3097 * @return true if the tempo section can be moved to the position, otherwise false.
3100 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3103 TempoSection* tempo_copy = 0;
3106 Glib::Threads::RWLock::ReaderLock lm (lock);
3107 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3113 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3115 Metrics::const_iterator d = copy.begin();
3116 while (d != copy.end()) {
3125 * This is for a gui that needs to know the pulse or frame of a tempo section if it were to be moved to some bbt time,
3126 * taking any possible reordering as a consequence of this into account.
3127 * @param section - the section to be altered
3128 * @param bbt - the BBT time where the altered tempo will fall
3129 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3131 pair<double, framepos_t>
3132 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3135 pair<double, framepos_t> ret = make_pair (0.0, 0);
3137 Glib::Threads::RWLock::ReaderLock lm (lock);
3139 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3141 const double beat = beat_at_bbt_locked (future_map, bbt);
3143 if (section->position_lock_style() == AudioTime) {
3144 tempo_copy->set_position_lock_style (MusicTime);
3147 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3148 ret.first = tempo_copy->pulse();
3149 ret.second = tempo_copy->frame();
3151 ret.first = section->pulse();
3152 ret.second = section->frame();
3155 Metrics::const_iterator d = future_map.begin();
3156 while (d != future_map.end()) {
3163 /** moves a TempoSection to a specified position.
3164 * @param ts - the section to be moved
3165 * @param frame - the new position in frames for the tempo
3166 * @param sub_num - the snap division to use if using musical time.
3168 * if sub_num is non-zero, the frame position is used to calculate an exact
3171 * -1 | snap to bars (meter-based)
3172 * 0 | no snap - use audio frame for musical position
3173 * 1 | snap to meter-based (BBT) beat
3174 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3176 * this follows the snap convention in the gui.
3177 * if sub_num is zero, the musical position will be taken from the supplied frame.
3180 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t frame, const int& sub_num)
3184 if (ts->position_lock_style() == MusicTime) {
3186 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3187 Glib::Threads::RWLock::WriterLock lm (lock);
3188 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3190 tempo_copy->set_position_lock_style (AudioTime);
3192 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3193 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3194 const double pulse = pulse_at_beat_locked (future_map, beat);
3196 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3197 solve_map_pulse (_metrics, ts, pulse);
3198 recompute_meters (_metrics);
3206 Glib::Threads::RWLock::WriterLock lm (lock);
3207 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3211 /* We're moving the object that defines the grid while snapping to it...
3212 * Placing the ts at the beat corresponding to the requested frame may shift the
3213 * grid in such a way that the mouse is left hovering over a completerly different division,
3214 * causing jittering when the mouse next moves (esp. large tempo deltas).
3215 * We fudge around this by doing this in the musical domain and then swapping back for the recompute.
3217 const double qn = exact_qn_at_frame_locked (_metrics, frame, sub_num);
3218 tempo_copy->set_position_lock_style (MusicTime);
3219 if (solve_map_pulse (future_map, tempo_copy, qn / 4.0)) {
3220 ts->set_position_lock_style (MusicTime);
3221 solve_map_pulse (_metrics, ts, qn / 4.0);
3222 ts->set_position_lock_style (AudioTime);
3223 recompute_meters (_metrics);
3226 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3227 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3228 recompute_meters (_metrics);
3234 Metrics::const_iterator d = future_map.begin();
3235 while (d != future_map.end()) {
3240 MetricPositionChanged (PropertyChange ()); // Emit Signal
3243 /** moves a MeterSection to a specified position.
3244 * @param ms - the section to be moved
3245 * @param frame - the new position in frames for the meter
3247 * as a meter cannot snap to anything but bars,
3248 * the supplied frame is rounded to the nearest bar, possibly
3249 * leaving the meter position unchanged.
3252 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t frame)
3256 if (ms->position_lock_style() == AudioTime) {
3259 Glib::Threads::RWLock::WriterLock lm (lock);
3260 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3262 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3263 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3264 recompute_tempi (_metrics);
3269 Glib::Threads::RWLock::WriterLock lm (lock);
3270 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3272 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3273 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3275 if (solve_map_bbt (future_map, copy, bbt)) {
3276 solve_map_bbt (_metrics, ms, bbt);
3277 recompute_tempi (_metrics);
3282 Metrics::const_iterator d = future_map.begin();
3283 while (d != future_map.end()) {
3288 MetricPositionChanged (PropertyChange ()); // Emit Signal
3292 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3295 bool can_solve = false;
3297 Glib::Threads::RWLock::WriterLock lm (lock);
3298 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3300 if (tempo_copy->type() == TempoSection::Constant) {
3301 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3302 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3304 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3305 tempo_copy->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3308 if (ts->clamped()) {
3309 TempoSection* prev = 0;
3310 if ((prev = previous_tempo_section_locked (future_map, tempo_copy)) != 0) {
3311 prev->set_end_note_types_per_minute (tempo_copy->note_types_per_minute());
3315 recompute_tempi (future_map);
3317 if (check_solved (future_map)) {
3318 if (ts->type() == TempoSection::Constant) {
3319 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3320 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3322 ts->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3323 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3326 if (ts->clamped()) {
3327 TempoSection* prev = 0;
3328 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3329 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3333 recompute_map (_metrics);
3338 Metrics::const_iterator d = future_map.begin();
3339 while (d != future_map.end()) {
3344 MetricPositionChanged (PropertyChange ()); // Emit Signal
3351 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame, const double start_qnote, const double end_qnote)
3354 Ts (future prev_t) Tnext
3357 |----------|----------
3364 Glib::Threads::RWLock::WriterLock lm (lock);
3370 TempoSection* ts_copy = copy_metrics_and_point (_metrics, future_map, ts);
3376 /* minimum allowed measurement distance in frames */
3377 framepos_t const min_dframe = 2;
3380 if (ts_copy->clamped()) {
3381 TempoSection* next_t = next_tempo_section_locked (future_map, ts_copy);
3382 TempoSection* prev_to_ts_copy = previous_tempo_section_locked (future_map, ts_copy);
3383 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3384 constant to constant is straightforward, as the tempo prev to ts_copy has constant slope.
3385 */ double contribution = 0.0;
3386 if (next_t && prev_to_ts_copy && prev_to_ts_copy->type() == TempoSection::Ramp) {
3387 contribution = (ts_copy->pulse() - prev_to_ts_copy->pulse()) / (double) (next_t->pulse() - prev_to_ts_copy->pulse());
3389 framepos_t const fr_off = end_frame - frame;
3390 frameoffset_t const ts_copy_frame_contribution = fr_off - (contribution * (double) fr_off);
3392 if (frame > prev_to_ts_copy->frame() + min_dframe && (frame + ts_copy_frame_contribution) > prev_to_ts_copy->frame() + min_dframe) {
3393 new_bpm = ts_copy->note_types_per_minute() * ((start_qnote - (prev_to_ts_copy->pulse() * 4.0))
3394 / (end_qnote - (prev_to_ts_copy->pulse() * 4.0)));
3396 new_bpm = ts_copy->note_types_per_minute();
3399 if (frame > ts_copy->frame() + min_dframe && end_frame > ts_copy->frame() + min_dframe) {
3401 new_bpm = ts_copy->note_types_per_minute() * ((frame - ts_copy->frame())
3402 / (double) (end_frame - ts_copy->frame()));
3404 new_bpm = ts_copy->note_types_per_minute();
3407 new_bpm = min (new_bpm, (double) 1000.0);
3409 /* don't clamp and proceed here.
3410 testing has revealed that this can go negative,
3411 which is an entirely different thing to just being too low.
3414 if (new_bpm < 0.5) {
3418 ts_copy->set_note_types_per_minute (new_bpm);
3420 if (ts_copy->clamped()) {
3421 TempoSection* prev = 0;
3422 if ((prev = previous_tempo_section_locked (future_map, ts_copy)) != 0) {
3423 prev->set_end_note_types_per_minute (ts_copy->note_types_per_minute());
3427 recompute_tempi (future_map);
3428 recompute_meters (future_map);
3430 if (check_solved (future_map)) {
3431 ts->set_note_types_per_minute (new_bpm);
3433 if (ts->clamped()) {
3434 TempoSection* prev = 0;
3435 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3436 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3440 recompute_tempi (_metrics);
3441 recompute_meters (_metrics);
3447 Metrics::const_iterator d = future_map.begin();
3448 while (d != future_map.end()) {
3452 MetricPositionChanged (PropertyChange ()); // Emit Signal
3457 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3460 Ts (future prev_t) Tnext
3463 |----------|----------
3470 Glib::Threads::RWLock::WriterLock lm (lock);
3476 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3482 /* minimum allowed measurement distance in frames */
3483 framepos_t const min_dframe = 2;
3486 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3487 new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->frame() - frame)
3488 / (double) (prev_t->frame() - end_frame));
3490 new_bpm = prev_t->end_note_types_per_minute();
3493 new_bpm = min (new_bpm, (double) 1000.0);
3495 if (new_bpm < 0.5) {
3499 prev_t->set_end_note_types_per_minute (new_bpm);
3501 TempoSection* next = 0;
3502 if ((next = next_tempo_section_locked (future_map, prev_t)) != 0) {
3503 if (next->clamped()) {
3504 next->set_note_types_per_minute (prev_t->end_note_types_per_minute());
3508 recompute_tempi (future_map);
3509 recompute_meters (future_map);
3511 if (check_solved (future_map)) {
3512 ts->set_end_note_types_per_minute (new_bpm);
3514 TempoSection* true_next = 0;
3515 if ((true_next = next_tempo_section_locked (_metrics, ts)) != 0) {
3516 if (true_next->clamped()) {
3517 true_next->set_note_types_per_minute (ts->end_note_types_per_minute());
3521 recompute_tempi (_metrics);
3522 recompute_meters (_metrics);
3528 Metrics::const_iterator d = future_map.begin();
3529 while (d != future_map.end()) {
3534 MetricPositionChanged (PropertyChange ()); // Emit Signal
3538 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
3540 TempoSection* next_t = 0;
3541 TempoSection* next_to_next_t = 0;
3543 bool can_solve = false;
3545 /* minimum allowed measurement distance in frames */
3546 framepos_t const min_dframe = 2;
3549 Glib::Threads::RWLock::WriterLock lm (lock);
3554 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3555 TempoSection* prev_to_prev_t = 0;
3556 const frameoffset_t fr_off = end_frame - frame;
3562 if (tempo_copy->pulse() > 0.0) {
3563 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
3566 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3567 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3568 next_t = static_cast<TempoSection*> (*i);
3577 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3578 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3579 next_to_next_t = static_cast<TempoSection*> (*i);
3584 if (!next_to_next_t) {
3588 double prev_contribution = 0.0;
3590 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3591 prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3594 const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
3597 framepos_t old_tc_minute = tempo_copy->minute();
3598 double old_next_minute = next_t->minute();
3599 double old_next_to_next_minute = next_to_next_t->minute();
3602 double new_next_bpm;
3603 double new_copy_end_bpm;
3605 if (frame > tempo_copy->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > tempo_copy->frame() + min_dframe) {
3606 new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
3607 / (double) (end_frame - tempo_copy->frame()));
3609 new_bpm = tempo_copy->note_types_per_minute();
3612 /* don't clamp and proceed here.
3613 testing has revealed that this can go negative,
3614 which is an entirely different thing to just being too low.
3616 if (new_bpm < 0.5) {
3620 new_bpm = min (new_bpm, (double) 1000.0);
3622 tempo_copy->set_note_types_per_minute (new_bpm);
3623 if (tempo_copy->type() == TempoSection::Constant) {
3624 tempo_copy->set_end_note_types_per_minute (new_bpm);
3627 recompute_tempi (future_map);
3629 if (check_solved (future_map)) {
3635 ts->set_note_types_per_minute (new_bpm);
3636 if (ts->type() == TempoSection::Constant) {
3637 ts->set_end_note_types_per_minute (new_bpm);
3640 recompute_map (_metrics);
3645 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3646 if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) {
3648 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3649 / (double) ((old_next_to_next_minute) - old_next_minute));
3652 new_next_bpm = next_t->note_types_per_minute();
3655 next_t->set_note_types_per_minute (new_next_bpm);
3656 recompute_tempi (future_map);
3658 if (check_solved (future_map)) {
3659 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3660 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3661 next_t = static_cast<TempoSection*> (*i);
3669 next_t->set_note_types_per_minute (new_next_bpm);
3670 recompute_map (_metrics);
3674 double next_frame_ratio = 1.0;
3675 double copy_frame_ratio = 1.0;
3677 if (next_to_next_t) {
3678 next_frame_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute - old_next_minute);
3680 copy_frame_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute));
3683 new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
3684 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
3686 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3688 if (next_t->clamped()) {
3689 next_t->set_note_types_per_minute (new_copy_end_bpm);
3691 next_t->set_note_types_per_minute (new_next_bpm);
3694 recompute_tempi (future_map);
3696 if (check_solved (future_map)) {
3697 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3698 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3699 next_t = static_cast<TempoSection*> (*i);
3708 if (next_t->clamped()) {
3709 next_t->set_note_types_per_minute (new_copy_end_bpm);
3711 next_t->set_note_types_per_minute (new_next_bpm);
3714 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3715 recompute_map (_metrics);
3721 Metrics::const_iterator d = future_map.begin();
3722 while (d != future_map.end()) {
3727 MetricPositionChanged (PropertyChange ()); // Emit Signal
3732 /** Returns the frame position of the musical position zero */
3734 TempoMap::music_origin ()
3736 Glib::Threads::RWLock::ReaderLock lm (lock);
3738 return first_tempo().frame();
3741 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3742 * the supplied frame, possibly returning a negative value.
3744 * @param frame The session frame position.
3745 * @param sub_num The subdivision to use when rounding the beat.
3746 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3747 * Positive integers indicate quarter note (non BBT) divisions.
3748 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3749 * @return The beat position of the supplied frame.
3751 * when working to a musical grid, the use of sub_nom indicates that
3752 * the position should be interpreted musically.
3754 * it effectively snaps to meter bars, meter beats or quarter note divisions
3755 * (as per current gui convention) and returns a musical position independent of frame rate.
3757 * If the supplied frame lies before the first meter, the return will be negative,
3758 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3759 * the continuation of the tempo curve (backwards).
3761 * This function is sensitive to tempo and meter.
3764 TempoMap::exact_beat_at_frame (const framepos_t frame, const int32_t sub_num) const
3766 Glib::Threads::RWLock::ReaderLock lm (lock);
3768 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3772 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t frame, const int32_t divisions) const
3774 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3777 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3778 * the supplied frame, possibly returning a negative value.
3780 * @param frame The session frame position.
3781 * @param sub_num The subdivision to use when rounding the quarter note.
3782 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3783 * Positive integers indicate quarter note (non BBT) divisions.
3784 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3785 * @return The quarter note position of the supplied frame.
3787 * When working to a musical grid, the use of sub_nom indicates that
3788 * the frame position should be interpreted musically.
3790 * it effectively snaps to meter bars, meter beats or quarter note divisions
3791 * (as per current gui convention) and returns a musical position independent of frame rate.
3793 * If the supplied frame lies before the first meter, the return will be negative,
3794 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3795 * the continuation of the tempo curve (backwards).
3797 * This function is tempo-sensitive.
3800 TempoMap::exact_qn_at_frame (const framepos_t frame, const int32_t sub_num) const
3802 Glib::Threads::RWLock::ReaderLock lm (lock);
3804 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3808 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t frame, const int32_t sub_num) const
3810 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3813 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3814 } else if (sub_num == 1) {
3815 /* the gui requested exact musical (BBT) beat */
3816 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3817 } else if (sub_num == -1) {
3819 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3823 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3825 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3827 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3837 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3838 * @param pos the frame position in the tempo map.
3839 * @param bbt the distance in BBT time from pos to calculate.
3840 * @param dir the rounding direction..
3841 * @return the duration in frames between pos and bbt
3844 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3846 Glib::Threads::RWLock::ReaderLock lm (lock);
3848 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3850 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3853 pos_bbt.bars += bbt.bars;
3855 pos_bbt.ticks += bbt.ticks;
3856 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3858 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3861 pos_bbt.beats += bbt.beats;
3862 if ((double) pos_bbt.beats > divisions) {
3864 pos_bbt.beats -= divisions;
3866 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3868 return pos_bbt_frame - pos;
3872 if (pos_bbt.bars <= bbt.bars) {
3875 pos_bbt.bars -= bbt.bars;
3878 if (pos_bbt.ticks < bbt.ticks) {
3879 if (pos_bbt.bars > 1) {
3880 if (pos_bbt.beats == 1) {
3882 pos_bbt.beats = divisions;
3886 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3892 pos_bbt.ticks -= bbt.ticks;
3895 if (pos_bbt.beats <= bbt.beats) {
3896 if (pos_bbt.bars > 1) {
3898 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3903 pos_bbt.beats -= bbt.beats;
3906 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3913 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3915 return round_to_type (fr, dir, Bar);
3919 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3921 return round_to_type (fr, dir, Beat);
3925 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3927 Glib::Threads::RWLock::ReaderLock lm (lock);
3928 uint32_t ticks = (uint32_t) floor (max (0.0, pulse_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat * 4.0);
3929 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3930 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3932 ticks -= beats * BBT_Time::ticks_per_beat;
3935 /* round to next (or same iff dir == RoundUpMaybe) */
3937 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3939 if (mod == 0 && dir == RoundUpMaybe) {
3940 /* right on the subdivision, which is fine, so do nothing */
3942 } else if (mod == 0) {
3943 /* right on the subdivision, so the difference is just the subdivision ticks */
3944 ticks += ticks_one_subdivisions_worth;
3947 /* not on subdivision, compute distance to next subdivision */
3949 ticks += ticks_one_subdivisions_worth - mod;
3952 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3953 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3954 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3955 // But I'm keeping it around, until we determine there are no terrible consequences.
3956 // if (ticks >= BBT_Time::ticks_per_beat) {
3957 // ticks -= BBT_Time::ticks_per_beat;
3960 } else if (dir < 0) {
3962 /* round to previous (or same iff dir == RoundDownMaybe) */
3964 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3966 if (difference == 0 && dir == RoundDownAlways) {
3967 /* right on the subdivision, but force-rounding down,
3968 so the difference is just the subdivision ticks */
3969 difference = ticks_one_subdivisions_worth;
3972 if (ticks < difference) {
3973 ticks = BBT_Time::ticks_per_beat - ticks;
3975 ticks -= difference;
3979 /* round to nearest */
3982 /* compute the distance to the previous and next subdivision */
3984 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3986 /* closer to the next subdivision, so shift forward */
3988 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3990 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3992 if (ticks > BBT_Time::ticks_per_beat) {
3994 ticks -= BBT_Time::ticks_per_beat;
3995 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3998 } else if (rem > 0) {
4000 /* closer to previous subdivision, so shift backward */
4004 /* can't go backwards past zero, so ... */
4005 return MusicFrame (0, 0);
4007 /* step back to previous beat */
4009 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4010 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4012 ticks = lrint (ticks - rem);
4013 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4016 /* on the subdivision, do nothing */
4020 MusicFrame ret (0, 0);
4021 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4022 ret.division = sub_num;
4028 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
4030 Glib::Threads::RWLock::ReaderLock lm (lock);
4031 const double minute = minute_at_frame (frame);
4032 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4033 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
4034 MusicFrame ret (0, 0);
4041 /* find bar previous to 'frame' */
4047 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4051 } else if (dir > 0) {
4052 /* find bar following 'frame' */
4057 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4061 /* true rounding: find nearest bar */
4062 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4065 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4067 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4069 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4070 ret.frame = next_ft;
4075 ret.frame = prev_ft;
4087 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
4090 } else if (dir > 0) {
4091 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
4095 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
4102 return MusicFrame (0, 0);
4106 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4107 framepos_t lower, framepos_t upper, uint32_t bar_mod)
4109 Glib::Threads::RWLock::ReaderLock lm (lock);
4110 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
4112 /* although the map handles negative beats, bbt doesn't. */
4117 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
4121 while (pos >= 0 && pos < upper) {
4122 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
4123 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4124 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4125 const double qn = pulse_at_beat_locked (_metrics, cnt) * 4.0;
4127 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, qn));
4131 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
4136 bbt.bars -= bbt.bars % bar_mod;
4140 while (pos >= 0 && pos < upper) {
4141 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4142 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4143 const double qn = pulse_at_bbt_locked (_metrics, bbt) * 4.0;
4145 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, qn));
4146 bbt.bars += bar_mod;
4152 TempoMap::tempo_section_at_frame (framepos_t frame) const
4154 Glib::Threads::RWLock::ReaderLock lm (lock);
4156 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4160 TempoMap::tempo_section_at_frame (framepos_t frame)
4162 Glib::Threads::RWLock::ReaderLock lm (lock);
4164 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4168 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4170 TempoSection* prev = 0;
4174 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4176 if ((*i)->is_tempo()) {
4177 t = static_cast<TempoSection*> (*i);
4181 if (prev && t->minute() > minute) {
4191 abort(); /*NOTREACHED*/
4197 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4199 TempoSection* prev = 0;
4203 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4205 if ((*i)->is_tempo()) {
4206 t = static_cast<TempoSection*> (*i);
4210 if (prev && t->minute() > minute) {
4220 abort(); /*NOTREACHED*/
4226 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4228 TempoSection* prev_t = 0;
4229 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4233 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4234 if ((*i)->is_tempo()) {
4235 t = static_cast<TempoSection*> (*i);
4241 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4252 TempoMap::previous_tempo_section (TempoSection* ts) const
4254 Glib::Threads::RWLock::ReaderLock lm (lock);
4256 return previous_tempo_section_locked (_metrics, ts);
4261 TempoMap::previous_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4267 TempoSection* prev = 0;
4269 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4271 if ((*i)->is_tempo()) {
4272 TempoSection* t = static_cast<TempoSection*> (*i);
4278 if (prev && t == ts) {
4289 abort(); /*NOTREACHED*/
4296 TempoMap::next_tempo_section (TempoSection* ts) const
4298 Glib::Threads::RWLock::ReaderLock lm (lock);
4300 return next_tempo_section_locked (_metrics, ts);
4304 TempoMap::next_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4310 TempoSection* prev = 0;
4312 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4314 if ((*i)->is_tempo()) {
4315 TempoSection* t = static_cast<TempoSection*> (*i);
4321 if (prev && prev == ts) {
4332 abort(); /*NOTREACHED*/
4337 /* don't use this to calculate length (the tempo is only correct for this frame).
4338 do that stuff based on the beat_at_frame and frame_at_beat api
4341 TempoMap::frames_per_quarter_note_at (const framepos_t frame, const framecnt_t sr) const
4343 Glib::Threads::RWLock::ReaderLock lm (lock);
4345 const TempoSection* ts_at = 0;
4346 const TempoSection* ts_after = 0;
4347 Metrics::const_iterator i;
4350 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4352 if ((*i)->is_tempo()) {
4353 t = static_cast<TempoSection*> (*i);
4357 if (ts_at && (*i)->frame() > frame) {
4367 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4369 /* must be treated as constant tempo */
4370 return ts_at->frames_per_quarter_note (_frame_rate);
4374 TempoMap::meter_section_at_frame (framepos_t frame) const
4376 Glib::Threads::RWLock::ReaderLock lm (lock);
4377 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4381 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4383 Metrics::const_iterator i;
4384 MeterSection* prev = 0;
4388 for (i = metrics.begin(); i != metrics.end(); ++i) {
4390 if (!(*i)->is_tempo()) {
4391 m = static_cast<MeterSection*> (*i);
4393 if (prev && (*i)->minute() > minute) {
4403 abort(); /*NOTREACHED*/
4410 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4412 MeterSection* prev_m = 0;
4414 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4416 if (!(*i)->is_tempo()) {
4417 m = static_cast<MeterSection*> (*i);
4418 if (prev_m && m->beat() > beat) {
4429 TempoMap::meter_section_at_beat (double beat) const
4431 Glib::Threads::RWLock::ReaderLock lm (lock);
4432 return meter_section_at_beat_locked (_metrics, beat);
4436 TempoMap::meter_at_frame (framepos_t frame) const
4438 TempoMetric m (metric_at (frame));
4443 TempoMap::fix_legacy_session ()
4445 MeterSection* prev_m = 0;
4446 TempoSection* prev_t = 0;
4447 bool have_initial_t = false;
4449 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4453 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4455 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4458 m->set_minute (0.0);
4459 m->set_position_lock_style (AudioTime);
4464 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4465 + (m->bbt().beats - 1)
4466 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4468 m->set_beat (start);
4469 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4470 + (m->bbt().beats - 1)
4471 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4472 m->set_pulse (start_beat / prev_m->note_divisor());
4475 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4480 /* Ramp type never existed in the era of this tempo section */
4481 t->set_end_note_types_per_minute (t->note_types_per_minute());
4485 t->set_minute (0.0);
4486 t->set_position_lock_style (AudioTime);
4488 have_initial_t = true;
4493 /* some 4.x sessions have no initial (non-movable) tempo. */
4494 if (!have_initial_t) {
4495 prev_t->set_pulse (0.0);
4496 prev_t->set_minute (0.0);
4497 prev_t->set_position_lock_style (AudioTime);
4498 prev_t->set_initial (true);
4499 prev_t->set_locked_to_meter (true);
4500 have_initial_t = true;
4503 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4504 + (t->legacy_bbt().beats - 1)
4505 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4507 t->set_pulse (beat / prev_m->note_divisor());
4509 /* really shouldn't happen but.. */
4510 t->set_pulse (beat / 4.0);
4518 TempoMap::fix_legacy_end_session ()
4520 TempoSection* prev_t = 0;
4522 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4525 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4532 if (prev_t->end_note_types_per_minute() < 0.0) {
4533 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4542 prev_t->set_end_note_types_per_minute (prev_t->note_types_per_minute());
4547 TempoMap::get_state ()
4549 Metrics::const_iterator i;
4550 XMLNode *root = new XMLNode ("TempoMap");
4553 Glib::Threads::RWLock::ReaderLock lm (lock);
4554 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4555 root->add_child_nocopy ((*i)->get_state());
4563 TempoMap::set_state (const XMLNode& node, int /*version*/)
4566 Glib::Threads::RWLock::WriterLock lm (lock);
4569 XMLNodeConstIterator niter;
4570 Metrics old_metrics (_metrics);
4573 nlist = node.children();
4575 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4576 XMLNode* child = *niter;
4578 if (child->name() == TempoSection::xml_state_node_name) {
4581 TempoSection* ts = new TempoSection (*child, _frame_rate);
4582 _metrics.push_back (ts);
4585 catch (failed_constructor& err){
4586 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4587 _metrics = old_metrics;
4588 old_metrics.clear();
4592 } else if (child->name() == MeterSection::xml_state_node_name) {
4595 MeterSection* ms = new MeterSection (*child, _frame_rate);
4596 _metrics.push_back (ms);
4599 catch (failed_constructor& err) {
4600 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4601 _metrics = old_metrics;
4602 old_metrics.clear();
4608 /* check for legacy sessions where bbt was the base musical unit for tempo */
4609 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4611 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4612 if (t->legacy_bbt().bars != 0) {
4613 fix_legacy_session();
4617 if (t->end_note_types_per_minute() < 0.0) {
4618 fix_legacy_end_session();
4624 if (niter == nlist.end()) {
4625 MetricSectionSorter cmp;
4626 _metrics.sort (cmp);
4629 /* check for multiple tempo/meters at the same location, which
4630 ardour2 somehow allowed.
4633 Metrics::iterator prev = _metrics.end();
4634 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4635 if (prev != _metrics.end()) {
4637 MeterSection* prev_m;
4639 TempoSection* prev_t;
4640 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4641 if (prev_m->beat() == ms->beat()) {
4642 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
4643 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
4646 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4647 if (prev_t->pulse() == ts->pulse()) {
4648 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4649 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4657 recompute_map (_metrics);
4659 Metrics::const_iterator d = old_metrics.begin();
4660 while (d != old_metrics.end()) {
4664 old_metrics.clear ();
4667 PropertyChanged (PropertyChange ());
4673 TempoMap::dump (std::ostream& o) const
4675 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4676 const MeterSection* m;
4677 const TempoSection* t;
4678 const TempoSection* prev_t = 0;
4680 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4682 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4683 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4684 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4685 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4686 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4688 o << " current start : " << t->note_types_per_minute()
4689 << " current end : " << t->end_note_types_per_minute()
4690 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4691 o << " previous : " << prev_t->note_types_per_minute()
4692 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4693 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4694 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4695 << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4696 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4699 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4700 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4701 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4702 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4705 o << "------" << std::endl;
4709 TempoMap::n_tempos() const
4711 Glib::Threads::RWLock::ReaderLock lm (lock);
4714 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4715 if ((*i)->is_tempo()) {
4724 TempoMap::n_meters() const
4726 Glib::Threads::RWLock::ReaderLock lm (lock);
4729 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4730 if (!(*i)->is_tempo()) {
4739 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4741 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4742 if ((*i)->frame() >= where && !(*i)->initial ()) {
4746 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4747 gui_set_meter_position (ms, (*i)->frame() + amount);
4750 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4751 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4756 PropertyChanged (PropertyChange ());
4760 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4764 std::list<MetricSection*> metric_kill_list;
4766 TempoSection* last_tempo = NULL;
4767 MeterSection* last_meter = NULL;
4768 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4769 bool meter_after = false; // is there a meter marker likewise?
4771 Glib::Threads::RWLock::WriterLock lm (lock);
4772 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4773 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4774 metric_kill_list.push_back(*i);
4775 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4778 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4782 else if ((*i)->frame() >= where) {
4783 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4784 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4785 if ((*i)->frame() == where) {
4786 // marker was immediately after end of range
4787 tempo_after = dynamic_cast<TempoSection*> (*i);
4788 meter_after = dynamic_cast<MeterSection*> (*i);
4794 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4795 if (last_tempo && !tempo_after) {
4796 metric_kill_list.remove(last_tempo);
4797 last_tempo->set_minute (minute_at_frame (where));
4800 if (last_meter && !meter_after) {
4801 metric_kill_list.remove(last_meter);
4802 last_meter->set_minute (minute_at_frame (where));
4806 //remove all the remaining metrics
4807 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4808 _metrics.remove(*i);
4813 recompute_map (_metrics);
4816 PropertyChanged (PropertyChange ());
4820 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4821 * pos can be -ve, if required.
4824 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4826 Glib::Threads::RWLock::ReaderLock lm (lock);
4827 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4829 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4833 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4835 Glib::Threads::RWLock::ReaderLock lm (lock);
4837 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4838 pos_bbt.ticks += op.ticks;
4839 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4841 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4843 pos_bbt.beats += op.beats;
4844 /* the meter in effect will start on the bar */
4845 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();
4846 while (pos_bbt.beats >= divisions_per_bar + 1) {
4848 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4849 pos_bbt.beats -= divisions_per_bar;
4851 pos_bbt.bars += op.bars;
4853 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4856 /** Count the number of beats that are equivalent to distance when going forward,
4860 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4862 Glib::Threads::RWLock::ReaderLock lm (lock);
4864 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4868 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4874 operator<< (std::ostream& o, const Meter& m) {
4875 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4879 operator<< (std::ostream& o, const Tempo& t) {
4880 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4884 operator<< (std::ostream& o, const MetricSection& section) {
4886 o << "MetricSection @ " << section.frame() << ' ';
4888 const TempoSection* ts;
4889 const MeterSection* ms;
4891 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4892 o << *((const Tempo*) ts);
4893 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4894 o << *((const Meter*) ms);