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)
164 _legacy_bbt = BBT_Time (0, 0, 0);
167 std::string start_bbt;
168 if (node.get_property ("start", start_bbt)) {
169 if (string_to_bbt_time (start_bbt, bbt)) {
170 /* legacy session - start used to be in bbt*/
173 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
177 // Don't worry about return value, exception will be thrown on error
178 MetricSection::set_state (node, Stateful::loading_state_version);
180 if (node.get_property ("beats-per-minute", _note_types_per_minute)) {
181 if (_note_types_per_minute < 0.0) {
182 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
183 throw failed_constructor();
187 if (node.get_property ("note-type", _note_type)) {
188 if (_note_type < 1.0) {
189 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
190 throw failed_constructor();
193 /* older session, make note type be quarter by default */
197 if (!node.get_property ("clamped", _clamped)) {
201 if (node.get_property ("end-beats-per-minute", _end_note_types_per_minute)) {
202 if (_end_note_types_per_minute < 0.0) {
203 info << _("TempoSection XML node has an illegal \"end-beats-per-minute\" value") << endmsg;
204 throw failed_constructor();
208 TempoSection::Type old_type;
209 if (node.get_property ("tempo-type", old_type)) {
210 /* sessions with a tempo-type node contain no end-beats-per-minute.
211 if the legacy node indicates a constant tempo, simply fill this in with the
212 start tempo. otherwise we need the next neighbour to know what it will be.
215 if (old_type == TempoSection::Constant) {
216 _end_note_types_per_minute = _note_types_per_minute;
218 _end_note_types_per_minute = -1.0;
222 if (!node.get_property ("active", _active)) {
223 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
227 if (!node.get_property ("locked-to-meter", _locked_to_meter)) {
229 set_locked_to_meter (true);
231 set_locked_to_meter (false);
235 /* 5.5 marked initial tempo as not locked to meter. this should always be true anyway */
237 set_locked_to_meter (true);
242 TempoSection::get_state() const
244 XMLNode *root = new XMLNode (xml_state_node_name);
247 MetricSection::add_state_to_node (*root);
249 root->set_property ("beats-per-minute", _note_types_per_minute);
250 root->set_property ("note-type", _note_type);
251 root->set_property ("clamped", _clamped);
252 root->set_property ("end-beats-per-minute", _end_note_types_per_minute);
253 root->set_property ("active", _active);
254 root->set_property ("locked-to-meter", _locked_to_meter);
259 /** returns the Tempo at the session-relative minute.
262 TempoSection::tempo_at_minute (const double& m) const
264 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
266 return Tempo (note_types_per_minute(), note_type());
269 return Tempo (_tempo_at_time (m - minute()), _note_type, _end_note_types_per_minute);
272 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
273 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
274 * @param p the pulse used to calculate the returned minute for constant tempi
275 * @return the minute at the supplied tempo
277 * note that the note_type is currently ignored in this function. see below.
281 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
282 * there should be no ramp between the two even if we are ramped.
283 * in other words a ramp should only place a curve on note_types_per_minute.
284 * we should be able to use Tempo note type here, but the above
285 * complicates things a bit.
288 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
290 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
292 return ((p - pulse()) / pulses_per_minute()) + minute();
295 return _time_at_tempo (ntpm) + minute();
298 /** returns the Tempo at the supplied whole-note pulse.
301 TempoSection::tempo_at_pulse (const double& p) const
303 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
306 return Tempo (note_types_per_minute(), note_type());
309 return Tempo (_tempo_at_pulse (p - pulse()), _note_type, _end_note_types_per_minute);
312 /** returns the whole-note pulse where a tempo in note types per minute occurs.
313 * constant tempi require minute m.
314 * @param ntpm the note types per minute value used to calculate the returned pulse
315 * @param m the minute used to calculate the returned pulse if the tempo is constant
316 * @return the whole-note pulse at the supplied tempo
318 * note that note_type is currently ignored in this function. see minute_at_tempo().
320 * for constant tempi, this is anaologous to pulse_at_minute().
323 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
325 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
327 return ((m - minute()) * pulses_per_minute()) + pulse();
330 return _pulse_at_tempo (ntpm) + pulse();
333 /** returns the whole-note pulse at the supplied session-relative minute.
336 TempoSection::pulse_at_minute (const double& m) const
338 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
340 return ((m - minute()) * pulses_per_minute()) + pulse();
343 return _pulse_at_time (m - minute()) + pulse();
346 /** returns the session-relative minute at the supplied whole-note pulse.
349 TempoSection::minute_at_pulse (const double& p) const
351 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
353 return ((p - pulse()) / pulses_per_minute()) + minute();
356 return _time_at_pulse (p - pulse()) + minute();
359 /** returns thw whole-note pulse at session frame position f.
360 * @param f the frame position.
361 * @return the position in whole-note pulses corresponding to f
363 * for use with musical units whose granularity is coarser than frames (e.g. ticks)
366 TempoSection::pulse_at_frame (const framepos_t& f) const
368 const bool constant = type() == Constant || _c == 0.0 || (initial() && f < frame());
370 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
373 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
377 TempoSection::frame_at_pulse (const double& p) const
379 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
381 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
384 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
392 Tt----|-----------------*|
393 Ta----|--------------|* |
399 _______________|___|____
400 time a t (next tempo)
403 Duration in beats at time a is the integral of some Tempo function.
404 In our case, the Tempo function (Tempo at time t) is
407 with function constant
412 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
413 b(t) = T0(e^(ct) - 1) / c
415 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:
416 t(b) = log((c.b / T0) + 1) / c
418 The time t at which Tempo T occurs is a as above:
419 t(T) = log(T / T0) / c
421 The beat at which a Tempo T occurs is:
424 The Tempo at which beat b occurs is:
427 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
428 Our problem is that we usually don't know t.
429 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.
430 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
431 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
433 By substituting our expanded t as a in the c function above, our problem is reduced to:
434 c = T0 (e^(log (Ta / T0)) - 1) / b
436 Of course the word 'beat' has been left loosely defined above.
437 In music, a beat is defined by the musical pulse (which comes from the tempo)
438 and the meter in use at a particular time (how many pulse divisions there are in one bar).
439 It would be more accurate to substitute the work 'pulse' for 'beat' above.
443 We can now store c for future time calculations.
444 If the following tempo section (the one that defines c in conjunction with this one)
445 is changed or moved, c is no longer valid.
447 The public methods are session-relative.
449 Most of this stuff is taken from this paper:
452 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
455 Zurich University of Arts
456 Institute for Computer Music and Sound Technology
458 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
462 /** compute this ramp's function constant from some tempo-pulse point
463 * @param end_npm end tempo (in note types per minute)
464 * @param end_pulse duration (pulses into global start) of some other position.
465 * @return the calculated function constant
468 TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const
470 if (note_types_per_minute() == end_npm || type() == Constant) {
474 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
475 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
478 /** compute the function constant from some tempo-time point.
479 * @param end_npm tempo (note types/min.)
480 * @param end_minute distance (in minutes) from session origin
481 * @return the calculated function constant
484 TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const
486 if (note_types_per_minute() == end_npm || type() == Constant) {
490 return c_func (end_npm, end_minute - minute());
493 /* position function */
495 TempoSection::a_func (double end_npm, double c) const
497 return log (end_npm / note_types_per_minute()) / c;
500 /*function constant*/
502 TempoSection::c_func (double end_npm, double end_time) const
504 return log (end_npm / note_types_per_minute()) / end_time;
507 /* tempo in note types per minute at time in minutes */
509 TempoSection::_tempo_at_time (const double& time) const
511 return exp (_c * time) * note_types_per_minute();
514 /* time in minutes at tempo in note types per minute */
516 TempoSection::_time_at_tempo (const double& npm) const
518 return log (npm / note_types_per_minute()) / _c;
521 /* pulse at tempo in note types per minute */
523 TempoSection::_pulse_at_tempo (const double& npm) const
525 return ((npm - note_types_per_minute()) / _c) / _note_type;
528 /* tempo in note types per minute at pulse */
530 TempoSection::_tempo_at_pulse (const double& pulse) const
532 return (pulse * _note_type * _c) + note_types_per_minute();
535 /* pulse at time in minutes */
537 TempoSection::_pulse_at_time (const double& time) const
539 return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
542 /* time in minutes at pulse */
544 TempoSection::_time_at_pulse (const double& pulse) const
546 return log1p ((_c * pulse * _note_type) / note_types_per_minute()) / _c;
549 /***********************************************************************/
551 const string MeterSection::xml_state_node_name = "Meter";
553 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
554 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
557 pair<double, BBT_Time> start;
561 if (node.get_property ("start", bbt_str)) {
562 if (string_to_bbt_time (bbt_str, start.second)) {
563 /* legacy session - start used to be in bbt*/
564 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
567 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
571 MetricSection::set_state (node, Stateful::loading_state_version);
573 node.get_property ("beat", start.first);
575 if (node.get_property ("bbt", bbt_str)) {
576 if (!string_to_bbt_time (bbt_str, start.second)) {
577 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
578 throw failed_constructor();
581 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
586 /* beats-per-bar is old; divisions-per-bar is new */
588 if (!node.get_property ("divisions-per-bar", _divisions_per_bar)) {
589 if (!node.get_property ("beats-per-bar", _divisions_per_bar)) {
590 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
591 throw failed_constructor();
595 if (_divisions_per_bar < 0.0) {
596 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
597 throw failed_constructor();
600 if (!node.get_property ("note-type", _note_type)) {
601 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
602 throw failed_constructor();
605 if (_note_type < 0.0) {
606 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
607 throw failed_constructor();
612 MeterSection::get_state() const
614 XMLNode *root = new XMLNode (xml_state_node_name);
617 MetricSection::add_state_to_node (*root);
620 bbt_time_to_string (_bbt, bbt_str);
621 root->set_property ("bbt", bbt_str);
622 root->set_property ("beat", beat());
623 root->set_property ("note-type", _note_type);
624 root->set_property ("divisions-per-bar", _divisions_per_bar);
629 /***********************************************************************/
633 Tempo determines the rate of musical pulse determined by its components
634 note types per minute - the rate per minute of the whole note divisor _note_type
635 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
636 Meter divides the musical pulse into measures and beats according to its components
640 TempoSection - translates between time, musical pulse and tempo.
641 has a musical location in whole notes (pulses).
642 has a time location in minutes.
643 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
644 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
646 MeterSection - translates between BBT, meter-based beat and musical pulse.
647 has a musical location in whole notes (pulses)
648 has a musical location in meter-based beats
649 has a musical location in BBT time
650 has a time location expressed in minutes.
652 TempoSection and MeterSection may be locked to either audio or music (position lock style).
653 The lock style determines the location type to be kept as a reference when location is recalculated.
655 The first tempo and meter are special. they must move together, and are locked to audio.
656 Audio locked tempi which lie before the first meter are made inactive.
658 Recomputing the map is the process where the 'missing' location types are calculated.
659 We construct the tempo map by first using the locked location type of each section
660 to determine non-locked location types (pulse or minute position).
661 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
663 Having done this, we can now traverse the Metrics list by pulse or minute
664 to query its relevant meter/tempo.
666 It is important to keep the _metrics in an order that makes sense.
667 Because ramped MusicTime and AudioTime tempos can interact with each other,
668 reordering is frequent. Care must be taken to keep _metrics in a solved state.
669 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
673 Music and audio-locked objects may seem interchangeable on the surface, but when translating
674 between audio samples and beat, remember that a sample is only a quantised approximation
675 of the actual time (in minutes) of a beat.
676 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
677 mean that this frame is the actual location in time of 1|3|0.
679 You cannot use a frame measurement to determine beat distance except under special circumstances
680 (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).
682 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
683 sample space the user is operating at to be translated correctly to the object.
685 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
686 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
687 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
689 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
691 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
692 The result is rounded to audio frames.
693 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
696 frame_at_beat (beat_at_frame (frame)) == frame
698 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
700 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
701 frames_between_quarter_notes () eliminats this effect when determining time duration
702 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
704 The above pointless example could instead do:
705 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
707 The Shaggs - Things I Wonder
708 https://www.youtube.com/watch?v=9wQK6zMJOoQ
711 struct MetricSectionSorter {
712 bool operator() (const MetricSection* a, const MetricSection* b) {
713 return a->pulse() < b->pulse();
717 struct MetricSectionFrameSorter {
718 bool operator() (const MetricSection* a, const MetricSection* b) {
719 return a->frame() < b->frame();
723 TempoMap::TempoMap (framecnt_t fr)
726 BBT_Time start (1, 1, 0);
728 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo, AudioTime, fr);
729 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
731 t->set_initial (true);
732 t->set_locked_to_meter (true);
734 m->set_initial (true);
736 /* note: frame time is correct (zero) for both of these */
738 _metrics.push_back (t);
739 _metrics.push_back (m);
744 TempoMap::operator= (TempoMap const & other)
746 if (&other != this) {
747 Glib::Threads::RWLock::ReaderLock lr (other.lock);
748 Glib::Threads::RWLock::WriterLock lm (lock);
749 _frame_rate = other._frame_rate;
751 Metrics::const_iterator d = _metrics.begin();
752 while (d != _metrics.end()) {
758 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
759 TempoSection const * const ts = dynamic_cast<TempoSection const * const> (*m);
760 MeterSection const * const ms = dynamic_cast<MeterSection const * const> (*m);
763 TempoSection* new_section = new TempoSection (*ts);
764 _metrics.push_back (new_section);
766 MeterSection* new_section = new MeterSection (*ms);
767 _metrics.push_back (new_section);
772 PropertyChanged (PropertyChange());
777 TempoMap::~TempoMap ()
779 Metrics::const_iterator d = _metrics.begin();
780 while (d != _metrics.end()) {
788 TempoMap::frame_at_minute (const double time) const
790 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
794 TempoMap::minute_at_frame (const framepos_t frame) const
796 return (frame / (double) _frame_rate) / 60.0;
800 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
802 bool removed = false;
805 Glib::Threads::RWLock::WriterLock lm (lock);
806 if ((removed = remove_tempo_locked (tempo))) {
807 if (complete_operation) {
808 recompute_map (_metrics);
813 if (removed && complete_operation) {
814 PropertyChanged (PropertyChange ());
819 TempoMap::remove_tempo_locked (const TempoSection& tempo)
823 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
824 if (dynamic_cast<TempoSection*> (*i) != 0) {
825 if (tempo.frame() == (*i)->frame()) {
826 if (!(*i)->initial()) {
839 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
841 bool removed = false;
844 Glib::Threads::RWLock::WriterLock lm (lock);
845 if ((removed = remove_meter_locked (tempo))) {
846 if (complete_operation) {
847 recompute_map (_metrics);
852 if (removed && complete_operation) {
853 PropertyChanged (PropertyChange ());
858 TempoMap::remove_meter_locked (const MeterSection& meter)
861 if (meter.position_lock_style() == AudioTime) {
862 /* remove meter-locked tempo */
863 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
865 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
866 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
875 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
876 if (dynamic_cast<MeterSection*> (*i) != 0) {
877 if (meter.frame() == (*i)->frame()) {
878 if (!(*i)->initial()) {
891 TempoMap::do_insert (MetricSection* section)
893 bool need_add = true;
894 /* we only allow new meters to be inserted on beat 1 of an existing
898 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
900 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
902 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
903 corrected.second.beats = 1;
904 corrected.second.ticks = 0;
905 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
906 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
907 m->bbt(), corrected.second) << endmsg;
908 //m->set_pulse (corrected);
912 /* Look for any existing MetricSection that is of the same type and
913 in the same bar as the new one, and remove it before adding
914 the new one. Note that this means that if we find a matching,
915 existing section, we can break out of the loop since we're
916 guaranteed that there is only one such match.
919 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
921 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
922 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
923 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
924 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
926 if (tempo && insert_tempo) {
929 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
930 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
932 if (tempo->initial()) {
934 /* can't (re)move this section, so overwrite
935 * its data content (but not its properties as
939 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
940 (*i)->set_position_lock_style (AudioTime);
949 } else if (meter && insert_meter) {
953 bool const ipm = insert_meter->position_lock_style() == MusicTime;
955 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
957 if (meter->initial()) {
959 /* can't (re)move this section, so overwrite
960 * its data content (but not its properties as
964 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
965 (*i)->set_position_lock_style (AudioTime);
975 /* non-matching types, so we don't care */
979 /* Add the given MetricSection, if we didn't just reset an existing
984 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
985 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
989 TempoSection* prev_t = 0;
991 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
992 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
993 bool const ipm = insert_meter->position_lock_style() == MusicTime;
996 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
1000 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
1004 prev_t = dynamic_cast<TempoSection*> (*i);
1007 } else if (insert_tempo) {
1008 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1009 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1012 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1013 const bool lm = insert_tempo->locked_to_meter();
1014 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1015 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1022 _metrics.insert (i, section);
1026 /* user supplies the exact pulse if pls == MusicTime */
1028 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1030 if (tempo.note_types_per_minute() <= 0.0) {
1031 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1035 TempoSection* ts = 0;
1036 TempoSection* prev_tempo = 0;
1038 Glib::Threads::RWLock::WriterLock lm (lock);
1039 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true);
1040 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1042 if ((*i)->is_tempo()) {
1043 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1045 bool const ipm = ts->position_lock_style() == MusicTime;
1046 bool const lm = ts->locked_to_meter();
1047 if ((ipm && this_t->pulse() == ts->pulse()) || (!ipm && this_t->frame() == ts->frame())
1048 || (lm && this_t->pulse() == ts->pulse())) {
1049 if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1050 prev_tempo->set_end_note_types_per_minute (ts->note_types_per_minute());
1054 prev_tempo = this_t;
1057 recompute_map (_metrics);
1060 PropertyChanged (PropertyChange ());
1066 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1068 if (tempo.note_types_per_minute() <= 0.0) {
1069 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1073 bool const locked_to_meter = ts.locked_to_meter();
1074 bool const ts_clamped = ts.clamped();
1075 TempoSection* new_ts = 0;
1078 Glib::Threads::RWLock::WriterLock lm (lock);
1079 TempoSection& first (first_tempo());
1080 if (!ts.initial()) {
1081 if (locked_to_meter) {
1083 /* cannot move a meter-locked tempo section */
1084 *static_cast<Tempo*>(&ts) = tempo;
1085 recompute_map (_metrics);
1088 remove_tempo_locked (ts);
1089 new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter);
1090 new_ts->set_clamped (ts_clamped);
1092 if (new_ts && new_ts->type() == TempoSection::Constant) {
1093 new_ts->set_end_note_types_per_minute (new_ts->note_types_per_minute());
1095 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1097 if ((*i)->is_tempo()) {
1098 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1100 bool const ipm = new_ts->position_lock_style() == MusicTime;
1101 bool const lm = new_ts->locked_to_meter();
1102 if ((ipm && this_t->pulse() > new_ts->pulse()) || (!ipm && this_t->frame() > new_ts->frame())
1103 || (lm && this_t->pulse() > new_ts->pulse())) {
1104 new_ts->set_end_note_types_per_minute (tempo.end_note_types_per_minute());
1114 first.set_pulse (0.0);
1115 first.set_minute (minute_at_frame (frame));
1116 first.set_position_lock_style (AudioTime);
1117 first.set_locked_to_meter (true);
1118 first.set_clamped (ts_clamped);
1120 /* cannot move the first tempo section */
1121 *static_cast<Tempo*>(&first) = tempo;
1124 recompute_map (_metrics);
1127 PropertyChanged (PropertyChange ());
1131 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1132 , PositionLockStyle pls, bool recompute, bool locked_to_meter)
1134 TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _frame_rate);
1135 t->set_locked_to_meter (locked_to_meter);
1140 if (pls == AudioTime) {
1141 solve_map_minute (_metrics, t, t->minute());
1143 solve_map_pulse (_metrics, t, t->pulse());
1145 recompute_meters (_metrics);
1152 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1154 MeterSection* m = 0;
1156 Glib::Threads::RWLock::WriterLock lm (lock);
1157 m = add_meter_locked (meter, beat, where, frame, pls, true);
1162 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1167 PropertyChanged (PropertyChange ());
1172 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1175 Glib::Threads::RWLock::WriterLock lm (lock);
1176 const double beat = beat_at_bbt_locked (_metrics, where);
1178 if (!ms.initial()) {
1179 remove_meter_locked (ms);
1180 add_meter_locked (meter, beat, where, frame, pls, true);
1182 MeterSection& first (first_meter());
1183 TempoSection& first_t (first_tempo());
1184 /* cannot move the first meter section */
1185 *static_cast<Meter*>(&first) = meter;
1186 first.set_position_lock_style (AudioTime);
1187 first.set_pulse (0.0);
1188 first.set_minute (minute_at_frame (frame));
1189 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1190 first.set_beat (beat);
1191 first_t.set_minute (first.minute());
1192 first_t.set_locked_to_meter (true);
1193 first_t.set_pulse (0.0);
1194 first_t.set_position_lock_style (AudioTime);
1195 recompute_map (_metrics);
1199 PropertyChanged (PropertyChange ());
1203 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1205 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1206 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1207 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1208 TempoSection* mlt = 0;
1210 if (pls == AudioTime) {
1211 /* add meter-locked tempo */
1212 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), AudioTime, true, true);
1220 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1222 bool solved = false;
1224 do_insert (new_meter);
1228 if (pls == AudioTime) {
1229 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1230 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1231 fudge frame so that the meter ends up at its BBT position instead.
1234 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1237 solved = solve_map_bbt (_metrics, new_meter, where);
1238 /* required due to resetting the pulse of meter-locked tempi above.
1239 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1240 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1242 recompute_map (_metrics);
1246 if (!solved && recompute) {
1247 /* if this has failed to solve, there is little we can do other than to ensure that
1248 the new map is recalculated.
1250 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1251 recompute_map (_metrics);
1258 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1260 Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1263 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1264 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1269 Glib::Threads::RWLock::WriterLock lm (lock);
1270 *((Tempo*) t) = newtempo;
1271 recompute_map (_metrics);
1273 PropertyChanged (PropertyChange ());
1280 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1282 Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1285 TempoSection* first;
1286 Metrics::iterator i;
1288 /* find the TempoSection immediately preceding "where"
1291 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1293 if ((*i)->frame() > where) {
1299 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1312 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1322 Glib::Threads::RWLock::WriterLock lm (lock);
1323 /* cannot move the first tempo section */
1324 *((Tempo*)prev) = newtempo;
1325 recompute_map (_metrics);
1328 PropertyChanged (PropertyChange ());
1332 TempoMap::first_meter () const
1334 const MeterSection *m = 0;
1336 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1337 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1342 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1343 abort(); /*NOTREACHED*/
1348 TempoMap::first_meter ()
1350 MeterSection *m = 0;
1352 /* CALLER MUST HOLD LOCK */
1354 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1355 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1360 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1361 abort(); /*NOTREACHED*/
1366 TempoMap::first_tempo () const
1368 const TempoSection *t = 0;
1370 /* CALLER MUST HOLD LOCK */
1372 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1373 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1383 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1384 abort(); /*NOTREACHED*/
1389 TempoMap::first_tempo ()
1391 TempoSection *t = 0;
1393 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1394 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1404 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1405 abort(); /*NOTREACHED*/
1409 TempoMap::recompute_tempi (Metrics& metrics)
1411 TempoSection* prev_t = 0;
1413 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1416 if ((*i)->is_tempo()) {
1417 t = static_cast<TempoSection*> (*i);
1429 if (t->position_lock_style() == AudioTime) {
1430 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1431 if (!t->locked_to_meter()) {
1432 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1436 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1437 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1445 prev_t->set_c (0.0);
1448 /* tempos must be positioned correctly.
1449 the current approach is to use a meter's bbt time as its base position unit.
1450 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1451 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1454 TempoMap::recompute_meters (Metrics& metrics)
1456 MeterSection* meter = 0;
1457 MeterSection* prev_m = 0;
1459 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1460 if (!(*mi)->is_tempo()) {
1461 meter = static_cast<MeterSection*> (*mi);
1462 if (meter->position_lock_style() == AudioTime) {
1464 pair<double, BBT_Time> b_bbt;
1465 TempoSection* meter_locked_tempo = 0;
1466 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1468 if ((*ii)->is_tempo()) {
1469 t = static_cast<TempoSection*> (*ii);
1470 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1471 meter_locked_tempo = t;
1478 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1479 if (beats + prev_m->beat() != meter->beat()) {
1480 /* reordering caused a bbt change */
1482 beats = meter->beat() - prev_m->beat();
1483 b_bbt = make_pair (beats + prev_m->beat()
1484 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1485 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1487 } else if (!meter->initial()) {
1488 b_bbt = make_pair (meter->beat(), meter->bbt());
1489 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1492 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1494 if (meter_locked_tempo) {
1495 meter_locked_tempo->set_pulse (pulse);
1497 meter->set_beat (b_bbt);
1498 meter->set_pulse (pulse);
1503 pair<double, BBT_Time> b_bbt;
1505 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1506 if (beats + prev_m->beat() != meter->beat()) {
1507 /* reordering caused a bbt change */
1508 b_bbt = make_pair (beats + prev_m->beat()
1509 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1511 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1513 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1515 /* shouldn't happen - the first is audio-locked */
1516 pulse = pulse_at_beat_locked (metrics, meter->beat());
1517 b_bbt = make_pair (meter->beat(), meter->bbt());
1520 meter->set_beat (b_bbt);
1521 meter->set_pulse (pulse);
1522 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1531 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1533 /* CALLER MUST HOLD WRITE LOCK */
1535 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1538 /* silly call from Session::process() during startup
1543 recompute_tempi (metrics);
1544 recompute_meters (metrics);
1548 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1550 Glib::Threads::RWLock::ReaderLock lm (lock);
1551 TempoMetric m (first_meter(), first_tempo());
1554 *last = ++_metrics.begin();
1557 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1558 at something, because we insert the default tempo and meter during
1559 TempoMap construction.
1561 now see if we can find better candidates.
1564 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1566 if ((*i)->frame() > frame) {
1580 /* XX meters only */
1582 TempoMap::metric_at (BBT_Time bbt) const
1584 Glib::Threads::RWLock::ReaderLock lm (lock);
1585 TempoMetric m (first_meter(), first_tempo());
1587 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1588 at something, because we insert the default tempo and meter during
1589 TempoMap construction.
1591 now see if we can find better candidates.
1594 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1596 if (!(*i)->is_tempo()) {
1597 mw = static_cast<MeterSection*> (*i);
1598 BBT_Time section_start (mw->bbt());
1600 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1611 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1612 * @param frame The session frame position.
1613 * @return The beat duration according to the tempo map at the supplied frame.
1615 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1616 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1618 * This function uses both tempo and meter.
1621 TempoMap::beat_at_frame (const framecnt_t& frame) const
1623 Glib::Threads::RWLock::ReaderLock lm (lock);
1625 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1628 /* This function uses both tempo and meter.*/
1630 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1632 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1633 MeterSection* prev_m = 0;
1634 MeterSection* next_m = 0;
1636 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1637 if (!(*i)->is_tempo()) {
1638 if (prev_m && (*i)->minute() > minute) {
1639 next_m = static_cast<MeterSection*> (*i);
1642 prev_m = static_cast<MeterSection*> (*i);
1646 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1648 /* audio locked meters fake their beat */
1649 if (next_m && next_m->beat() < beat) {
1650 return next_m->beat();
1656 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1657 * @param beat The BBT (meter-based) beat.
1658 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1660 * This function uses both tempo and meter.
1663 TempoMap::frame_at_beat (const double& beat) const
1665 Glib::Threads::RWLock::ReaderLock lm (lock);
1667 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1670 /* meter & tempo section based */
1672 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1674 MeterSection* prev_m = 0;
1675 TempoSection* prev_t = 0;
1679 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1680 if (!(*i)->is_tempo()) {
1681 m = static_cast<MeterSection*> (*i);
1682 if (prev_m && m->beat() > beat) {
1692 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1693 if ((*i)->is_tempo()) {
1694 t = static_cast<TempoSection*> (*i);
1700 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1709 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1712 /** Returns a Tempo corresponding to the supplied frame position.
1713 * @param frame The audio frame.
1714 * @return a Tempo according to the tempo map at the supplied frame.
1718 TempoMap::tempo_at_frame (const framepos_t& frame) const
1720 Glib::Threads::RWLock::ReaderLock lm (lock);
1722 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1726 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1728 TempoSection* prev_t = 0;
1732 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1733 if ((*i)->is_tempo()) {
1734 t = static_cast<TempoSection*> (*i);
1738 if ((prev_t) && t->minute() > minute) {
1739 /* t is the section past frame */
1740 return prev_t->tempo_at_minute (minute);
1746 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1749 /** returns the frame at which the supplied tempo occurs, or
1750 * the frame of the last tempo section (search exhausted)
1751 * only the position of the first occurence will be returned
1755 TempoMap::frame_at_tempo (const Tempo& tempo) const
1757 Glib::Threads::RWLock::ReaderLock lm (lock);
1759 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1763 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1765 TempoSection* prev_t = 0;
1766 const double tempo_bpm = tempo.note_types_per_minute();
1768 Metrics::const_iterator i;
1770 for (i = metrics.begin(); i != metrics.end(); ++i) {
1772 if ((*i)->is_tempo()) {
1773 t = static_cast<TempoSection*> (*i);
1781 if (t->note_types_per_minute() == tempo_bpm) {
1786 const double prev_t_bpm = prev_t->note_types_per_minute();
1787 const double prev_t_end_bpm = prev_t->end_note_types_per_minute();
1788 if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm)
1789 || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm)
1790 || (prev_t_end_bpm == tempo_bpm)) {
1792 return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
1799 return prev_t->minute();
1803 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1805 TempoSection* prev_t = 0;
1809 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1810 if ((*i)->is_tempo()) {
1811 t = static_cast<TempoSection*> (*i);
1815 if ((prev_t) && t->pulse() > pulse) {
1816 /* t is the section past frame */
1817 return prev_t->tempo_at_pulse (pulse);
1823 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1827 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1829 TempoSection* prev_t = 0;
1830 const double tempo_bpm = tempo.note_types_per_minute();
1832 Metrics::const_iterator i;
1834 for (i = metrics.begin(); i != metrics.end(); ++i) {
1836 if ((*i)->is_tempo()) {
1837 t = static_cast<TempoSection*> (*i);
1843 const double t_bpm = t->note_types_per_minute();
1845 if (t_bpm == tempo_bpm) {
1850 const double prev_t_bpm = prev_t->note_types_per_minute();
1852 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1853 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1860 return prev_t->pulse();
1863 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1864 * @param qn the position in quarter note beats.
1865 * @return the Tempo at the supplied quarter-note.
1868 TempoMap::tempo_at_quarter_note (const double& qn) const
1870 Glib::Threads::RWLock::ReaderLock lm (lock);
1872 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1875 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1876 * @param tempo the tempo.
1877 * @return the position in quarter-note beats where the map bpm
1878 * is equal to that of the Tempo. currently ignores note_type.
1881 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1883 Glib::Threads::RWLock::ReaderLock lm (lock);
1885 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;
1888 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1889 * @param metrics the list of metric sections used to calculate the pulse.
1890 * @param beat The BBT (meter-based) beat.
1891 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1893 * a pulse or whole note is the base musical position of a MetricSection.
1894 * it is equivalent to four quarter notes.
1898 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1900 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1902 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1905 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1906 * @param metrics the list of metric sections used to calculate the beat.
1907 * @param pulse the whole-note pulse.
1908 * @return the meter-based beat at the supplied whole-note pulse.
1910 * a pulse or whole note is the base musical position of a MetricSection.
1911 * it is equivalent to four quarter notes.
1914 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1916 MeterSection* prev_m = 0;
1918 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1920 if (!(*i)->is_tempo()) {
1921 m = static_cast<MeterSection*> (*i);
1922 if (prev_m && m->pulse() > pulse) {
1930 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1934 /* tempo section based */
1936 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1938 /* HOLD (at least) THE READER LOCK */
1939 TempoSection* prev_t = 0;
1941 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1943 if ((*i)->is_tempo()) {
1944 t = static_cast<TempoSection*> (*i);
1948 if (prev_t && t->minute() > minute) {
1949 /*the previous ts is the one containing the frame */
1950 const double ret = prev_t->pulse_at_minute (minute);
1951 /* audio locked section in new meter*/
1952 if (t->pulse() < ret) {
1961 /* treated as constant for this ts */
1962 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1964 return pulses_in_section + prev_t->pulse();
1967 /* tempo section based */
1969 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1971 /* HOLD THE READER LOCK */
1973 const TempoSection* prev_t = 0;
1975 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1978 if ((*i)->is_tempo()) {
1979 t = static_cast<TempoSection*> (*i);
1983 if (prev_t && t->pulse() > pulse) {
1984 return prev_t->minute_at_pulse (pulse);
1990 /* must be treated as constant, irrespective of _type */
1991 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1993 return dtime + prev_t->minute();
1996 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1997 * @param bbt The BBT time (meter-based).
1998 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
2002 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
2004 Glib::Threads::RWLock::ReaderLock lm (lock);
2005 return beat_at_bbt_locked (_metrics, bbt);
2010 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2012 /* CALLER HOLDS READ LOCK */
2014 MeterSection* prev_m = 0;
2016 /* because audio-locked meters have 'fake' integral beats,
2017 there is no pulse offset here.
2021 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2022 if (!(*i)->is_tempo()) {
2023 m = static_cast<MeterSection*> (*i);
2025 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2026 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2034 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2035 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2036 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2041 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2042 * @param beat The BBT (meter-based) beat.
2043 * @return The BBT time (meter-based) at the supplied meter-based beat.
2047 TempoMap::bbt_at_beat (const double& beat)
2049 Glib::Threads::RWLock::ReaderLock lm (lock);
2050 return bbt_at_beat_locked (_metrics, beat);
2054 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2056 /* CALLER HOLDS READ LOCK */
2057 MeterSection* prev_m = 0;
2058 const double beats = max (0.0, b);
2060 MeterSection* m = 0;
2062 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2063 if (!(*i)->is_tempo()) {
2064 m = static_cast<MeterSection*> (*i);
2066 if (m->beat() > beats) {
2067 /* this is the meter after the one our beat is on*/
2077 const double beats_in_ms = beats - prev_m->beat();
2078 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2079 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2080 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2081 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2085 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2086 ret.beats = (uint32_t) floor (remaining_beats);
2087 ret.bars = total_bars;
2089 /* 0 0 0 to 1 1 0 - based mapping*/
2093 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2095 ret.ticks -= BBT_Time::ticks_per_beat;
2098 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2106 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2107 * @param bbt The BBT time (meter-based).
2108 * @return the quarter note beat at the supplied BBT time
2110 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2112 * while the input uses meter, the output does not.
2115 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2117 Glib::Threads::RWLock::ReaderLock lm (lock);
2119 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2123 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2125 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2128 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2131 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2135 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2137 /* CALLER HOLDS READ LOCK */
2139 MeterSection* prev_m = 0;
2141 /* because audio-locked meters have 'fake' integral beats,
2142 there is no pulse offset here.
2146 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2147 if (!(*i)->is_tempo()) {
2148 m = static_cast<MeterSection*> (*i);
2150 if (m->bbt().bars > bbt.bars) {
2158 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2159 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2160 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2165 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2166 * @param qn the quarter-note beat.
2167 * @return The BBT time (meter-based) at the supplied meter-based beat.
2169 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2173 TempoMap::bbt_at_quarter_note (const double& qn)
2175 Glib::Threads::RWLock::ReaderLock lm (lock);
2177 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2180 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2181 * @param metrics The list of metric sections used to determine the result.
2182 * @param pulse The whole-note pulse.
2183 * @return The BBT time at the supplied whole-note pulse.
2185 * a pulse or whole note is the basic musical position of a MetricSection.
2186 * it is equivalent to four quarter notes.
2187 * while the output uses meter, the input does not.
2190 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2192 MeterSection* prev_m = 0;
2194 MeterSection* m = 0;
2196 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2198 if (!(*i)->is_tempo()) {
2199 m = static_cast<MeterSection*> (*i);
2202 double const pulses_to_m = m->pulse() - prev_m->pulse();
2203 if (prev_m->pulse() + pulses_to_m > pulse) {
2204 /* this is the meter after the one our beat is on*/
2215 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2216 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2217 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2218 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2219 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2223 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2224 ret.beats = (uint32_t) floor (remaining_beats);
2225 ret.bars = total_bars;
2227 /* 0 0 0 to 1 1 0 mapping*/
2231 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2233 ret.ticks -= BBT_Time::ticks_per_beat;
2236 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2244 /** Returns the BBT time corresponding to the supplied frame position.
2245 * @param frame the position in audio samples.
2246 * @return the BBT time at the frame position .
2250 TempoMap::bbt_at_frame (framepos_t frame)
2258 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2263 const double minute = minute_at_frame (frame);
2265 Glib::Threads::RWLock::ReaderLock lm (lock);
2267 return bbt_at_minute_locked (_metrics, minute);
2271 TempoMap::bbt_at_frame_rt (framepos_t frame)
2273 const double minute = minute_at_frame (frame);
2275 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2278 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2281 return bbt_at_minute_locked (_metrics, minute);
2285 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2295 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2296 MeterSection* prev_m = 0;
2297 MeterSection* next_m = 0;
2301 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2302 if (!(*i)->is_tempo()) {
2303 m = static_cast<MeterSection*> (*i);
2304 if (prev_m && m->minute() > minute) {
2312 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2314 /* handle frame before first meter */
2315 if (minute < prev_m->minute()) {
2318 /* audio locked meters fake their beat */
2319 if (next_m && next_m->beat() < beat) {
2320 beat = next_m->beat();
2323 beat = max (0.0, beat);
2325 const double beats_in_ms = beat - prev_m->beat();
2326 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2327 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2328 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2329 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2333 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2334 ret.beats = (uint32_t) floor (remaining_beats);
2335 ret.bars = total_bars;
2337 /* 0 0 0 to 1 1 0 - based mapping*/
2341 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2343 ret.ticks -= BBT_Time::ticks_per_beat;
2346 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2354 /** Returns the frame position corresponding to the supplied BBT time.
2355 * @param bbt the position in BBT time.
2356 * @return the frame position at bbt.
2360 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2364 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2369 if (bbt.beats < 1) {
2370 throw std::logic_error ("beats are counted from one");
2375 Glib::Threads::RWLock::ReaderLock lm (lock);
2376 minute = minute_at_bbt_locked (_metrics, bbt);
2379 return frame_at_minute (minute);
2382 /* meter & tempo section based */
2384 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2386 /* HOLD THE READER LOCK */
2388 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2393 * Returns the quarter-note beat position corresponding to the supplied frame.
2395 * @param frame the position in frames.
2396 * @return The quarter-note position of the supplied frame. Ignores meter.
2400 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2402 const double minute = minute_at_frame (frame);
2404 Glib::Threads::RWLock::ReaderLock lm (lock);
2406 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2410 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2412 const double minute = minute_at_frame (frame);
2414 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2417 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2420 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2424 * Returns the frame position corresponding to the supplied quarter-note beat.
2426 * @param quarter_note the quarter-note position.
2427 * @return the frame position of the supplied quarter-note. Ignores meter.
2432 TempoMap::frame_at_quarter_note (const double quarter_note) const
2436 Glib::Threads::RWLock::ReaderLock lm (lock);
2438 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2441 return frame_at_minute (minute);
2444 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2445 * @param beat The BBT (meter-based) beat.
2446 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2448 * a quarter-note may be compared with and assigned to Evoral::Beats.
2452 TempoMap::quarter_note_at_beat (const double beat) const
2454 Glib::Threads::RWLock::ReaderLock lm (lock);
2456 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2459 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2460 * @param quarter_note The position in quarter-note beats.
2461 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2463 * a quarter-note is the musical unit of Evoral::Beats.
2467 TempoMap::beat_at_quarter_note (const double quarter_note) const
2469 Glib::Threads::RWLock::ReaderLock lm (lock);
2471 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2474 /** Returns the duration in frames between two supplied quarter-note beat positions.
2475 * @param start the first position in quarter-note beats.
2476 * @param end the end position in quarter-note beats.
2477 * @return the frame distance ober the quarter-note beats duration.
2479 * use this rather than e.g.
2480 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2481 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2485 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2490 Glib::Threads::RWLock::ReaderLock lm (lock);
2491 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2494 return frame_at_minute (minutes);
2498 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2501 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2505 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2507 Glib::Threads::RWLock::ReaderLock lm (lock);
2509 return quarter_notes_between_frames_locked (_metrics, start, end);
2513 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2515 const TempoSection* prev_t = 0;
2517 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2520 if ((*i)->is_tempo()) {
2521 t = static_cast<TempoSection*> (*i);
2525 if (prev_t && t->frame() > start) {
2532 const double start_qn = prev_t->pulse_at_frame (start);
2534 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2537 if ((*i)->is_tempo()) {
2538 t = static_cast<TempoSection*> (*i);
2542 if (prev_t && t->frame() > end) {
2548 const double end_qn = prev_t->pulse_at_frame (end);
2550 return (end_qn - start_qn) * 4.0;
2554 TempoMap::check_solved (const Metrics& metrics) const
2556 TempoSection* prev_t = 0;
2557 MeterSection* prev_m = 0;
2559 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2562 if ((*i)->is_tempo()) {
2563 t = static_cast<TempoSection*> (*i);
2568 /* check ordering */
2569 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2573 /* precision check ensures tempo and frames align.*/
2574 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2575 if (!t->locked_to_meter()) {
2580 /* gradient limit - who knows what it should be?
2581 things are also ok (if a little chaotic) without this
2583 if (fabs (prev_t->c()) > 1000.0) {
2584 //std::cout << "c : " << prev_t->c() << std::endl;
2591 if (!(*i)->is_tempo()) {
2592 m = static_cast<MeterSection*> (*i);
2593 if (prev_m && m->position_lock_style() == AudioTime) {
2594 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2595 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2596 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2598 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2612 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2614 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2616 if ((*i)->is_tempo()) {
2617 t = static_cast<TempoSection*> (*i);
2618 if (t->locked_to_meter()) {
2619 t->set_active (true);
2620 } else if (t->position_lock_style() == AudioTime) {
2621 if (t->frame() < frame) {
2622 t->set_active (false);
2623 t->set_pulse (-1.0);
2624 } else if (t->frame() > frame) {
2625 t->set_active (true);
2626 } else if (t->frame() == frame) {
2636 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2638 TempoSection* prev_t = 0;
2639 TempoSection* section_prev = 0;
2640 double first_m_minute = 0.0;
2641 const bool sml = section->locked_to_meter();
2643 /* can't move a tempo before the first meter */
2644 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2646 if (!(*i)->is_tempo()) {
2647 m = static_cast<MeterSection*> (*i);
2649 first_m_minute = m->minute();
2654 if (!section->initial() && minute <= first_m_minute) {
2658 section->set_active (true);
2659 section->set_minute (minute);
2661 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2663 if ((*i)->is_tempo()) {
2664 t = static_cast<TempoSection*> (*i);
2676 if (t->frame() == frame_at_minute (minute)) {
2680 const bool tlm = t->position_lock_style() == MusicTime;
2682 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2683 section_prev = prev_t;
2685 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2686 if (!section->locked_to_meter()) {
2687 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2692 if (t->position_lock_style() == MusicTime) {
2693 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2694 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2696 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2697 if (!t->locked_to_meter()) {
2698 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2707 recompute_tempi (imaginary);
2709 if (check_solved (imaginary)) {
2712 dunp (imaginary, std::cout);
2716 MetricSectionFrameSorter fcmp;
2717 imaginary.sort (fcmp);
2719 recompute_tempi (imaginary);
2721 if (check_solved (imaginary)) {
2729 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2731 TempoSection* prev_t = 0;
2732 TempoSection* section_prev = 0;
2734 section->set_pulse (pulse);
2736 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2738 if ((*i)->is_tempo()) {
2739 t = static_cast<TempoSection*> (*i);
2750 section_prev = prev_t;
2754 if (t->position_lock_style() == MusicTime) {
2755 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2756 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2758 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2759 if (!t->locked_to_meter()) {
2760 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2769 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2770 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2774 recompute_tempi (imaginary);
2776 if (check_solved (imaginary)) {
2779 dunp (imaginary, std::cout);
2783 MetricSectionSorter cmp;
2784 imaginary.sort (cmp);
2786 recompute_tempi (imaginary);
2788 * XX need a restriction here, but only for this case,
2789 * as audio locked tempos don't interact in the same way.
2791 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2793 * |50 bpm |250 bpm |60 bpm
2794 * drag 250 to the pulse after 60->
2795 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2797 if (check_solved (imaginary)) {
2805 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2807 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2808 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2809 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2813 if (section->initial()) {
2814 /* lock the first tempo to our first meter */
2815 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2820 TempoSection* meter_locked_tempo = 0;
2822 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2824 if ((*ii)->is_tempo()) {
2825 t = static_cast<TempoSection*> (*ii);
2826 if (t->locked_to_meter() && t->frame() == section->frame()) {
2827 meter_locked_tempo = t;
2833 if (!meter_locked_tempo) {
2837 MeterSection* prev_m = 0;
2839 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2840 bool solved = false;
2842 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2844 if (!(*i)->is_tempo()) {
2845 m = static_cast<MeterSection*> (*i);
2847 if (prev_m && !section->initial()) {
2848 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2849 if (beats + prev_m->beat() < section->beat()) {
2850 /* set the section pulse according to its musical position,
2851 * as an earlier time than this has been requested.
2853 const double new_pulse = ((section->beat() - prev_m->beat())
2854 / prev_m->note_divisor()) + prev_m->pulse();
2856 tempo_copy->set_position_lock_style (MusicTime);
2857 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2858 meter_locked_tempo->set_position_lock_style (MusicTime);
2859 section->set_position_lock_style (MusicTime);
2860 section->set_pulse (new_pulse);
2861 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2862 meter_locked_tempo->set_position_lock_style (AudioTime);
2863 section->set_position_lock_style (AudioTime);
2864 section->set_minute (meter_locked_tempo->minute());
2870 Metrics::const_iterator d = future_map.begin();
2871 while (d != future_map.end()) {
2880 /* all is ok. set section's locked tempo if allowed.
2881 possibly disallow if there is an adjacent audio-locked tempo.
2882 XX this check could possibly go. its never actually happened here.
2884 MeterSection* meter_copy = const_cast<MeterSection*>
2885 (&meter_section_at_minute_locked (future_map, section->minute()));
2887 meter_copy->set_minute (minute);
2889 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2890 section->set_minute (minute);
2891 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2892 / prev_m->note_divisor()) + prev_m->pulse());
2893 solve_map_minute (imaginary, meter_locked_tempo, minute);
2898 Metrics::const_iterator d = future_map.begin();
2899 while (d != future_map.end()) {
2909 /* initial (first meter atm) */
2911 tempo_copy->set_minute (minute);
2912 tempo_copy->set_pulse (0.0);
2914 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2915 section->set_minute (minute);
2916 meter_locked_tempo->set_minute (minute);
2917 meter_locked_tempo->set_pulse (0.0);
2918 solve_map_minute (imaginary, meter_locked_tempo, minute);
2923 Metrics::const_iterator d = future_map.begin();
2924 while (d != future_map.end()) {
2933 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2934 section->set_beat (b_bbt);
2935 section->set_pulse (0.0);
2945 MetricSectionFrameSorter fcmp;
2946 imaginary.sort (fcmp);
2948 recompute_meters (imaginary);
2954 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2956 /* disallow setting section to an existing meter's bbt */
2957 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2959 if (!(*i)->is_tempo()) {
2960 m = static_cast<MeterSection*> (*i);
2961 if (m != section && m->bbt().bars == when.bars) {
2967 MeterSection* prev_m = 0;
2968 MeterSection* section_prev = 0;
2970 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2972 if (!(*i)->is_tempo()) {
2973 m = static_cast<MeterSection*> (*i);
2979 pair<double, BBT_Time> b_bbt;
2980 double new_pulse = 0.0;
2982 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2983 section_prev = prev_m;
2985 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2986 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2987 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2989 section->set_beat (b_bbt);
2990 section->set_pulse (pulse);
2991 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2995 if (m->position_lock_style() == AudioTime) {
2996 TempoSection* meter_locked_tempo = 0;
2998 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
3000 if ((*ii)->is_tempo()) {
3001 t = static_cast<TempoSection*> (*ii);
3002 if (t->locked_to_meter() && t->frame() == m->frame()) {
3003 meter_locked_tempo = t;
3009 if (!meter_locked_tempo) {
3014 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3016 if (beats + prev_m->beat() != m->beat()) {
3017 /* tempo/ meter change caused a change in beat (bar). */
3019 /* the user has requested that the previous section of music overlaps this one.
3020 we have no choice but to change the bar number here, as being locked to audio means
3021 we must stay where we are on the timeline.
3023 beats = m->beat() - prev_m->beat();
3024 b_bbt = make_pair (beats + prev_m->beat()
3025 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3026 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3028 } else if (!m->initial()) {
3029 b_bbt = make_pair (m->beat(), m->bbt());
3030 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3033 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3036 meter_locked_tempo->set_pulse (new_pulse);
3037 m->set_beat (b_bbt);
3038 m->set_pulse (new_pulse);
3042 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3043 if (beats + prev_m->beat() != m->beat()) {
3044 /* tempo/ meter change caused a change in beat (bar). */
3045 b_bbt = make_pair (beats + prev_m->beat()
3046 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3048 b_bbt = make_pair (beats + prev_m->beat()
3051 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3052 m->set_beat (b_bbt);
3053 m->set_pulse (new_pulse);
3054 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3061 if (!section_prev) {
3063 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3064 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3065 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3067 section->set_beat (b_bbt);
3068 section->set_pulse (pulse);
3069 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3072 MetricSectionSorter cmp;
3073 imaginary.sort (cmp);
3075 recompute_meters (imaginary);
3080 /** places a copy of _metrics into copy and returns a pointer
3081 * to section's equivalent in copy.
3084 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section) const
3086 TempoSection* ret = 0;
3088 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3089 if ((*i)->is_tempo()) {
3090 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3092 ret = new TempoSection (*t);
3093 copy.push_back (ret);
3097 TempoSection* cp = new TempoSection (*t);
3098 copy.push_back (cp);
3100 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3101 MeterSection* cp = new MeterSection (*m);
3102 copy.push_back (cp);
3110 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section) const
3112 MeterSection* ret = 0;
3114 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3115 if ((*i)->is_tempo()) {
3116 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3117 TempoSection* cp = new TempoSection (*t);
3118 copy.push_back (cp);
3120 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3122 ret = new MeterSection (*m);
3123 copy.push_back (ret);
3126 MeterSection* cp = new MeterSection (*m);
3127 copy.push_back (cp);
3134 /** answers the question "is this a valid beat position for this tempo section?".
3135 * it returns true if the tempo section can be moved to the requested bbt position,
3136 * leaving the tempo map in a solved state.
3137 * @param ts the tempo section to be moved
3138 * @param bbt the requested new position for the tempo section
3139 * @return true if the tempo section can be moved to the position, otherwise false.
3142 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3145 TempoSection* tempo_copy = 0;
3148 Glib::Threads::RWLock::ReaderLock lm (lock);
3149 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3155 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3157 Metrics::const_iterator d = copy.begin();
3158 while (d != copy.end()) {
3167 * 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,
3168 * taking any possible reordering as a consequence of this into account.
3169 * @param section - the section to be altered
3170 * @param bbt - the BBT time where the altered tempo will fall
3171 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3173 pair<double, framepos_t>
3174 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3177 pair<double, framepos_t> ret = make_pair (0.0, 0);
3179 Glib::Threads::RWLock::ReaderLock lm (lock);
3181 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3183 const double beat = beat_at_bbt_locked (future_map, bbt);
3185 if (section->position_lock_style() == AudioTime) {
3186 tempo_copy->set_position_lock_style (MusicTime);
3189 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3190 ret.first = tempo_copy->pulse();
3191 ret.second = tempo_copy->frame();
3193 ret.first = section->pulse();
3194 ret.second = section->frame();
3197 Metrics::const_iterator d = future_map.begin();
3198 while (d != future_map.end()) {
3205 /** moves a TempoSection to a specified position.
3206 * @param ts - the section to be moved
3207 * @param frame - the new position in frames for the tempo
3208 * @param sub_num - the snap division to use if using musical time.
3210 * if sub_num is non-zero, the frame position is used to calculate an exact
3213 * -1 | snap to bars (meter-based)
3214 * 0 | no snap - use audio frame for musical position
3215 * 1 | snap to meter-based (BBT) beat
3216 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3218 * this follows the snap convention in the gui.
3219 * if sub_num is zero, the musical position will be taken from the supplied frame.
3222 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3226 if (ts->position_lock_style() == MusicTime) {
3228 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3229 Glib::Threads::RWLock::WriterLock lm (lock);
3230 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3232 tempo_copy->set_position_lock_style (AudioTime);
3234 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3235 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3236 const double pulse = pulse_at_beat_locked (future_map, beat);
3238 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3239 solve_map_pulse (_metrics, ts, pulse);
3240 recompute_meters (_metrics);
3248 Glib::Threads::RWLock::WriterLock lm (lock);
3249 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3251 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3253 /* We're moving the object that defines the grid while snapping to it...
3254 * Placing the ts at the beat corresponding to the requested frame may shift the
3255 * grid in such a way that the mouse is left hovering over a completerly different division,
3256 * causing jittering when the mouse next moves (esp. large tempo deltas).
3258 * This alters the snap behaviour slightly in that we snap to beat divisions
3259 * in the future map rather than the existing one.
3261 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3262 const framepos_t snapped_frame = frame_at_minute (minute_at_pulse_locked (future_map, qn / 4.0));
3264 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3265 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3266 ts->set_pulse (qn / 4.0);
3267 recompute_meters (_metrics);
3270 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3271 recompute_meters (_metrics);
3277 Metrics::const_iterator d = future_map.begin();
3278 while (d != future_map.end()) {
3283 MetricPositionChanged (PropertyChange ()); // Emit Signal
3286 /** moves a MeterSection to a specified position.
3287 * @param ms - the section to be moved
3288 * @param frame - the new position in frames for the meter
3290 * as a meter cannot snap to anything but bars,
3291 * the supplied frame is rounded to the nearest bar, possibly
3292 * leaving the meter position unchanged.
3295 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3299 if (ms->position_lock_style() == AudioTime) {
3302 Glib::Threads::RWLock::WriterLock lm (lock);
3303 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3305 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3306 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3307 recompute_tempi (_metrics);
3312 Glib::Threads::RWLock::WriterLock lm (lock);
3313 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3315 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3316 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3318 if (solve_map_bbt (future_map, copy, bbt)) {
3319 solve_map_bbt (_metrics, ms, bbt);
3320 recompute_tempi (_metrics);
3325 Metrics::const_iterator d = future_map.begin();
3326 while (d != future_map.end()) {
3331 MetricPositionChanged (PropertyChange ()); // Emit Signal
3335 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3338 bool can_solve = false;
3340 Glib::Threads::RWLock::WriterLock lm (lock);
3341 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3343 if (tempo_copy->type() == TempoSection::Constant) {
3344 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3345 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3347 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3348 tempo_copy->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3351 if (ts->clamped()) {
3352 TempoSection* prev = 0;
3353 if ((prev = previous_tempo_section_locked (future_map, tempo_copy)) != 0) {
3354 prev->set_end_note_types_per_minute (tempo_copy->note_types_per_minute());
3358 recompute_tempi (future_map);
3360 if (check_solved (future_map)) {
3361 if (ts->type() == TempoSection::Constant) {
3362 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3363 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3365 ts->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3366 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3369 if (ts->clamped()) {
3370 TempoSection* prev = 0;
3371 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3372 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3376 recompute_map (_metrics);
3381 Metrics::const_iterator d = future_map.begin();
3382 while (d != future_map.end()) {
3387 MetricPositionChanged (PropertyChange ()); // Emit Signal
3394 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3397 Ts (future prev_t) Tnext
3400 |----------|----------
3407 Glib::Threads::RWLock::WriterLock lm (lock);
3413 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3419 /* minimum allowed measurement distance in frames */
3420 framepos_t const min_dframe = 2;
3423 if (prev_t->clamped()) {
3424 TempoSection* next_t = next_tempo_section_locked (future_map, prev_t);
3425 TempoSection* prev_to_prev_t = previous_tempo_section_locked (future_map, prev_t);
3426 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3427 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3429 double contribution = 0.0;
3430 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3431 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3433 framepos_t const fr_off = (end_frame - frame);
3434 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3436 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3437 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3438 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3440 new_bpm = prev_t->note_types_per_minute();
3443 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3445 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
3446 / (double) (end_frame - prev_t->frame()));
3448 new_bpm = prev_t->note_types_per_minute();
3451 new_bpm = min (new_bpm, (double) 1000.0);
3453 /* don't clamp and proceed here.
3454 testing has revealed that this can go negative,
3455 which is an entirely different thing to just being too low.
3458 if (new_bpm < 0.5) {
3462 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3463 prev_t->set_note_types_per_minute (new_bpm);
3465 prev_t->set_end_note_types_per_minute (new_bpm);
3466 prev_t->set_note_types_per_minute (new_bpm);
3469 if (prev_t->clamped()) {
3470 TempoSection* prev = 0;
3471 if ((prev = previous_tempo_section_locked (future_map, prev_t)) != 0) {
3472 prev->set_end_note_types_per_minute (prev_t->note_types_per_minute());
3476 recompute_tempi (future_map);
3477 recompute_meters (future_map);
3479 if (check_solved (future_map)) {
3480 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3481 ts->set_note_types_per_minute (new_bpm);
3483 ts->set_end_note_types_per_minute (new_bpm);
3484 ts->set_note_types_per_minute (new_bpm);
3486 if (ts->clamped()) {
3487 TempoSection* prev = 0;
3488 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3489 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3492 recompute_tempi (_metrics);
3493 recompute_meters (_metrics);
3499 Metrics::const_iterator d = future_map.begin();
3500 while (d != future_map.end()) {
3504 MetricPositionChanged (PropertyChange ()); // Emit Signal
3509 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3512 Ts (future prev_t) Tnext
3515 |----------|----------
3522 Glib::Threads::RWLock::WriterLock lm (lock);
3528 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3534 /* minimum allowed measurement distance in frames */
3535 framepos_t const min_dframe = 2;
3538 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3539 new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->frame() - frame)
3540 / (double) (prev_t->frame() - end_frame));
3542 new_bpm = prev_t->end_note_types_per_minute();
3545 new_bpm = min (new_bpm, (double) 1000.0);
3547 if (new_bpm < 0.5) {
3551 prev_t->set_end_note_types_per_minute (new_bpm);
3553 TempoSection* next = 0;
3554 if ((next = next_tempo_section_locked (future_map, prev_t)) != 0) {
3555 if (next->clamped()) {
3556 next->set_note_types_per_minute (prev_t->end_note_types_per_minute());
3560 recompute_tempi (future_map);
3561 recompute_meters (future_map);
3563 if (check_solved (future_map)) {
3564 ts->set_end_note_types_per_minute (new_bpm);
3566 TempoSection* true_next = 0;
3567 if ((true_next = next_tempo_section_locked (_metrics, ts)) != 0) {
3568 if (true_next->clamped()) {
3569 true_next->set_note_types_per_minute (ts->end_note_types_per_minute());
3573 recompute_tempi (_metrics);
3574 recompute_meters (_metrics);
3580 Metrics::const_iterator d = future_map.begin();
3581 while (d != future_map.end()) {
3586 MetricPositionChanged (PropertyChange ()); // Emit Signal
3590 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
3592 TempoSection* next_t = 0;
3593 TempoSection* next_to_next_t = 0;
3595 bool can_solve = false;
3597 /* minimum allowed measurement distance in frames */
3598 framepos_t const min_dframe = 2;
3601 Glib::Threads::RWLock::WriterLock lm (lock);
3606 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3607 TempoSection* prev_to_prev_t = 0;
3608 const frameoffset_t fr_off = end_frame - frame;
3614 if (tempo_copy->pulse() > 0.0) {
3615 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
3618 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3619 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3620 next_t = static_cast<TempoSection*> (*i);
3629 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3630 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3631 next_to_next_t = static_cast<TempoSection*> (*i);
3636 if (!next_to_next_t) {
3640 double prev_contribution = 0.0;
3642 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3643 prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3646 const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
3649 framepos_t old_tc_minute = tempo_copy->minute();
3650 double old_next_minute = next_t->minute();
3651 double old_next_to_next_minute = next_to_next_t->minute();
3654 double new_next_bpm;
3655 double new_copy_end_bpm;
3657 if (frame > tempo_copy->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > tempo_copy->frame() + min_dframe) {
3658 new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
3659 / (double) (end_frame - tempo_copy->frame()));
3661 new_bpm = tempo_copy->note_types_per_minute();
3664 /* don't clamp and proceed here.
3665 testing has revealed that this can go negative,
3666 which is an entirely different thing to just being too low.
3668 if (new_bpm < 0.5) {
3672 new_bpm = min (new_bpm, (double) 1000.0);
3674 tempo_copy->set_note_types_per_minute (new_bpm);
3675 if (tempo_copy->type() == TempoSection::Constant) {
3676 tempo_copy->set_end_note_types_per_minute (new_bpm);
3679 recompute_tempi (future_map);
3681 if (check_solved (future_map)) {
3687 ts->set_note_types_per_minute (new_bpm);
3688 if (ts->type() == TempoSection::Constant) {
3689 ts->set_end_note_types_per_minute (new_bpm);
3692 recompute_map (_metrics);
3697 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3698 if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) {
3700 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3701 / (double) ((old_next_to_next_minute) - old_next_minute));
3704 new_next_bpm = next_t->note_types_per_minute();
3707 next_t->set_note_types_per_minute (new_next_bpm);
3708 recompute_tempi (future_map);
3710 if (check_solved (future_map)) {
3711 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3712 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3713 next_t = static_cast<TempoSection*> (*i);
3721 next_t->set_note_types_per_minute (new_next_bpm);
3722 recompute_map (_metrics);
3726 double next_frame_ratio = 1.0;
3727 double copy_frame_ratio = 1.0;
3729 if (next_to_next_t) {
3730 next_frame_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute - old_next_minute);
3732 copy_frame_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute));
3735 new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
3736 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
3738 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3740 if (next_t->clamped()) {
3741 next_t->set_note_types_per_minute (new_copy_end_bpm);
3743 next_t->set_note_types_per_minute (new_next_bpm);
3746 recompute_tempi (future_map);
3748 if (check_solved (future_map)) {
3749 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3750 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3751 next_t = static_cast<TempoSection*> (*i);
3760 if (next_t->clamped()) {
3761 next_t->set_note_types_per_minute (new_copy_end_bpm);
3763 next_t->set_note_types_per_minute (new_next_bpm);
3766 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3767 recompute_map (_metrics);
3773 Metrics::const_iterator d = future_map.begin();
3774 while (d != future_map.end()) {
3779 MetricPositionChanged (PropertyChange ()); // Emit Signal
3784 /** Returns the frame position of the musical position zero */
3786 TempoMap::music_origin ()
3788 Glib::Threads::RWLock::ReaderLock lm (lock);
3790 return first_tempo().frame();
3793 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3794 * the supplied frame, possibly returning a negative value.
3796 * @param frame The session frame position.
3797 * @param sub_num The subdivision to use when rounding the beat.
3798 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3799 * Positive integers indicate quarter note (non BBT) divisions.
3800 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3801 * @return The beat position of the supplied frame.
3803 * when working to a musical grid, the use of sub_nom indicates that
3804 * the position should be interpreted musically.
3806 * it effectively snaps to meter bars, meter beats or quarter note divisions
3807 * (as per current gui convention) and returns a musical position independent of frame rate.
3809 * If the supplied frame lies before the first meter, the return will be negative,
3810 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3811 * the continuation of the tempo curve (backwards).
3813 * This function is sensitive to tempo and meter.
3816 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3818 Glib::Threads::RWLock::ReaderLock lm (lock);
3820 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3824 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3826 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3829 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3830 * the supplied frame, possibly returning a negative value.
3832 * @param frame The session frame position.
3833 * @param sub_num The subdivision to use when rounding the quarter note.
3834 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3835 * Positive integers indicate quarter note (non BBT) divisions.
3836 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3837 * @return The quarter note position of the supplied frame.
3839 * When working to a musical grid, the use of sub_nom indicates that
3840 * the frame position should be interpreted musically.
3842 * it effectively snaps to meter bars, meter beats or quarter note divisions
3843 * (as per current gui convention) and returns a musical position independent of frame rate.
3845 * If the supplied frame lies before the first meter, the return will be negative,
3846 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3847 * the continuation of the tempo curve (backwards).
3849 * This function is tempo-sensitive.
3852 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3854 Glib::Threads::RWLock::ReaderLock lm (lock);
3856 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3860 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3862 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3865 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3866 } else if (sub_num == 1) {
3867 /* the gui requested exact musical (BBT) beat */
3868 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3869 } else if (sub_num == -1) {
3871 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3875 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3877 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3879 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3889 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3890 * @param pos the frame position in the tempo map.
3891 * @param bbt the distance in BBT time from pos to calculate.
3892 * @param dir the rounding direction..
3893 * @return the duration in frames between pos and bbt
3896 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3898 Glib::Threads::RWLock::ReaderLock lm (lock);
3900 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3902 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3905 pos_bbt.bars += bbt.bars;
3907 pos_bbt.ticks += bbt.ticks;
3908 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3910 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3913 pos_bbt.beats += bbt.beats;
3914 if ((double) pos_bbt.beats > divisions) {
3916 pos_bbt.beats -= divisions;
3918 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3920 return pos_bbt_frame - pos;
3924 if (pos_bbt.bars <= bbt.bars) {
3927 pos_bbt.bars -= bbt.bars;
3930 if (pos_bbt.ticks < bbt.ticks) {
3931 if (pos_bbt.bars > 1) {
3932 if (pos_bbt.beats == 1) {
3934 pos_bbt.beats = divisions;
3938 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3944 pos_bbt.ticks -= bbt.ticks;
3947 if (pos_bbt.beats <= bbt.beats) {
3948 if (pos_bbt.bars > 1) {
3950 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3955 pos_bbt.beats -= bbt.beats;
3958 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3965 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3967 return round_to_type (fr, dir, Bar);
3971 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3973 return round_to_type (fr, dir, Beat);
3977 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3979 Glib::Threads::RWLock::ReaderLock lm (lock);
3980 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);
3981 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3982 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3984 ticks -= beats * BBT_Time::ticks_per_beat;
3987 /* round to next (or same iff dir == RoundUpMaybe) */
3989 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3991 if (mod == 0 && dir == RoundUpMaybe) {
3992 /* right on the subdivision, which is fine, so do nothing */
3994 } else if (mod == 0) {
3995 /* right on the subdivision, so the difference is just the subdivision ticks */
3996 ticks += ticks_one_subdivisions_worth;
3999 /* not on subdivision, compute distance to next subdivision */
4001 ticks += ticks_one_subdivisions_worth - mod;
4004 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
4005 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
4006 // And since the "prev" direction DOES move beats, I assume this code is unintended.
4007 // But I'm keeping it around, until we determine there are no terrible consequences.
4008 // if (ticks >= BBT_Time::ticks_per_beat) {
4009 // ticks -= BBT_Time::ticks_per_beat;
4012 } else if (dir < 0) {
4014 /* round to previous (or same iff dir == RoundDownMaybe) */
4016 uint32_t difference = ticks % ticks_one_subdivisions_worth;
4018 if (difference == 0 && dir == RoundDownAlways) {
4019 /* right on the subdivision, but force-rounding down,
4020 so the difference is just the subdivision ticks */
4021 difference = ticks_one_subdivisions_worth;
4024 if (ticks < difference) {
4025 ticks = BBT_Time::ticks_per_beat - ticks;
4027 ticks -= difference;
4031 /* round to nearest */
4034 /* compute the distance to the previous and next subdivision */
4036 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
4038 /* closer to the next subdivision, so shift forward */
4040 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
4042 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
4044 if (ticks > BBT_Time::ticks_per_beat) {
4046 ticks -= BBT_Time::ticks_per_beat;
4047 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
4050 } else if (rem > 0) {
4052 /* closer to previous subdivision, so shift backward */
4056 /* can't go backwards past zero, so ... */
4057 return MusicFrame (0, 0);
4059 /* step back to previous beat */
4061 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4062 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4064 ticks = lrint (ticks - rem);
4065 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4068 /* on the subdivision, do nothing */
4072 MusicFrame ret (0, 0);
4073 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4074 ret.division = sub_num;
4080 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
4082 Glib::Threads::RWLock::ReaderLock lm (lock);
4083 const double minute = minute_at_frame (frame);
4084 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4085 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
4086 MusicFrame ret (0, 0);
4093 /* find bar previous to 'frame' */
4099 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4103 } else if (dir > 0) {
4104 /* find bar following 'frame' */
4109 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4113 /* true rounding: find nearest bar */
4114 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4117 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4119 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4121 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4122 ret.frame = next_ft;
4127 ret.frame = prev_ft;
4139 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
4142 } else if (dir > 0) {
4143 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
4147 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
4154 return MusicFrame (0, 0);
4158 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4159 framepos_t lower, framepos_t upper, uint32_t bar_mod)
4161 Glib::Threads::RWLock::ReaderLock lm (lock);
4162 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
4164 /* although the map handles negative beats, bbt doesn't. */
4169 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
4173 while (pos >= 0 && pos < upper) {
4174 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
4175 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4176 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4177 const double qn = pulse_at_beat_locked (_metrics, cnt) * 4.0;
4179 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, qn));
4183 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
4188 bbt.bars -= bbt.bars % bar_mod;
4192 while (pos >= 0 && pos < upper) {
4193 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4194 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4195 const double qn = pulse_at_bbt_locked (_metrics, bbt) * 4.0;
4197 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, qn));
4198 bbt.bars += bar_mod;
4204 TempoMap::tempo_section_at_frame (framepos_t frame) const
4206 Glib::Threads::RWLock::ReaderLock lm (lock);
4208 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4212 TempoMap::tempo_section_at_frame (framepos_t frame)
4214 Glib::Threads::RWLock::ReaderLock lm (lock);
4216 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4220 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4222 TempoSection* prev = 0;
4226 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4228 if ((*i)->is_tempo()) {
4229 t = static_cast<TempoSection*> (*i);
4233 if (prev && t->minute() > minute) {
4243 abort(); /*NOTREACHED*/
4249 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4251 TempoSection* prev = 0;
4255 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4257 if ((*i)->is_tempo()) {
4258 t = static_cast<TempoSection*> (*i);
4262 if (prev && t->minute() > minute) {
4272 abort(); /*NOTREACHED*/
4278 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4280 TempoSection* prev_t = 0;
4281 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4285 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4286 if ((*i)->is_tempo()) {
4287 t = static_cast<TempoSection*> (*i);
4293 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4304 TempoMap::previous_tempo_section (TempoSection* ts) const
4306 Glib::Threads::RWLock::ReaderLock lm (lock);
4308 return previous_tempo_section_locked (_metrics, ts);
4313 TempoMap::previous_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4319 TempoSection* prev = 0;
4321 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4323 if ((*i)->is_tempo()) {
4324 TempoSection* t = static_cast<TempoSection*> (*i);
4330 if (prev && t == ts) {
4341 abort(); /*NOTREACHED*/
4348 TempoMap::next_tempo_section (TempoSection* ts) const
4350 Glib::Threads::RWLock::ReaderLock lm (lock);
4352 return next_tempo_section_locked (_metrics, ts);
4356 TempoMap::next_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4362 TempoSection* prev = 0;
4364 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4366 if ((*i)->is_tempo()) {
4367 TempoSection* t = static_cast<TempoSection*> (*i);
4373 if (prev && prev == ts) {
4384 abort(); /*NOTREACHED*/
4389 /* don't use this to calculate length (the tempo is only correct for this frame).
4390 do that stuff based on the beat_at_frame and frame_at_beat api
4393 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4395 Glib::Threads::RWLock::ReaderLock lm (lock);
4397 const TempoSection* ts_at = 0;
4398 const TempoSection* ts_after = 0;
4399 Metrics::const_iterator i;
4402 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4404 if ((*i)->is_tempo()) {
4405 t = static_cast<TempoSection*> (*i);
4409 if (ts_at && (*i)->frame() > frame) {
4419 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4421 /* must be treated as constant tempo */
4422 return ts_at->frames_per_quarter_note (_frame_rate);
4426 TempoMap::meter_section_at_frame (framepos_t frame) const
4428 Glib::Threads::RWLock::ReaderLock lm (lock);
4429 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4433 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4435 Metrics::const_iterator i;
4436 MeterSection* prev = 0;
4440 for (i = metrics.begin(); i != metrics.end(); ++i) {
4442 if (!(*i)->is_tempo()) {
4443 m = static_cast<MeterSection*> (*i);
4445 if (prev && (*i)->minute() > minute) {
4455 abort(); /*NOTREACHED*/
4462 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4464 MeterSection* prev_m = 0;
4466 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4468 if (!(*i)->is_tempo()) {
4469 m = static_cast<MeterSection*> (*i);
4470 if (prev_m && m->beat() > beat) {
4481 TempoMap::meter_section_at_beat (double beat) const
4483 Glib::Threads::RWLock::ReaderLock lm (lock);
4484 return meter_section_at_beat_locked (_metrics, beat);
4488 TempoMap::meter_at_frame (framepos_t frame) const
4490 TempoMetric m (metric_at (frame));
4495 TempoMap::fix_legacy_session ()
4497 MeterSection* prev_m = 0;
4498 TempoSection* prev_t = 0;
4499 bool have_initial_t = false;
4501 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4505 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4507 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4510 m->set_minute (0.0);
4511 m->set_position_lock_style (AudioTime);
4516 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4517 + (m->bbt().beats - 1)
4518 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4520 m->set_beat (start);
4521 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4522 + (m->bbt().beats - 1)
4523 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4524 m->set_pulse (start_beat / prev_m->note_divisor());
4527 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4532 /* Ramp type never existed in the era of this tempo section */
4533 t->set_end_note_types_per_minute (t->note_types_per_minute());
4537 t->set_minute (0.0);
4538 t->set_position_lock_style (AudioTime);
4540 have_initial_t = true;
4545 /* some 4.x sessions have no initial (non-movable) tempo. */
4546 if (!have_initial_t) {
4547 prev_t->set_pulse (0.0);
4548 prev_t->set_minute (0.0);
4549 prev_t->set_position_lock_style (AudioTime);
4550 prev_t->set_initial (true);
4551 prev_t->set_locked_to_meter (true);
4552 have_initial_t = true;
4555 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4556 + (t->legacy_bbt().beats - 1)
4557 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4559 t->set_pulse (beat / prev_m->note_divisor());
4561 /* really shouldn't happen but.. */
4562 t->set_pulse (beat / 4.0);
4570 TempoMap::fix_legacy_end_session ()
4572 TempoSection* prev_t = 0;
4574 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4577 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4584 if (prev_t->type() != TempoSection::Constant) {
4585 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4595 TempoMap::get_state ()
4597 Metrics::const_iterator i;
4598 XMLNode *root = new XMLNode ("TempoMap");
4601 Glib::Threads::RWLock::ReaderLock lm (lock);
4602 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4603 root->add_child_nocopy ((*i)->get_state());
4611 TempoMap::set_state (const XMLNode& node, int /*version*/)
4614 Glib::Threads::RWLock::WriterLock lm (lock);
4617 XMLNodeConstIterator niter;
4618 Metrics old_metrics (_metrics);
4621 nlist = node.children();
4623 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4624 XMLNode* child = *niter;
4626 if (child->name() == TempoSection::xml_state_node_name) {
4629 TempoSection* ts = new TempoSection (*child, _frame_rate);
4630 _metrics.push_back (ts);
4633 catch (failed_constructor& err){
4634 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4635 _metrics = old_metrics;
4636 old_metrics.clear();
4640 } else if (child->name() == MeterSection::xml_state_node_name) {
4643 MeterSection* ms = new MeterSection (*child, _frame_rate);
4644 _metrics.push_back (ms);
4647 catch (failed_constructor& err) {
4648 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4649 _metrics = old_metrics;
4650 old_metrics.clear();
4656 /* check for legacy sessions where bbt was the base musical unit for tempo */
4657 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4659 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4660 if (t->legacy_bbt().bars != 0) {
4661 fix_legacy_session();
4665 if (t->end_note_types_per_minute() < 0.0) {
4666 fix_legacy_end_session();
4674 if (niter == nlist.end()) {
4675 MetricSectionSorter cmp;
4676 _metrics.sort (cmp);
4679 /* check for multiple tempo/meters at the same location, which
4680 ardour2 somehow allowed.
4683 Metrics::iterator prev = _metrics.end();
4684 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4685 if (prev != _metrics.end()) {
4687 MeterSection* prev_m;
4689 TempoSection* prev_t;
4690 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4691 if (prev_m->pulse() == ms->pulse()) {
4692 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4693 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4696 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4697 if (prev_t->pulse() == ts->pulse()) {
4698 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4699 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4707 recompute_map (_metrics);
4709 Metrics::const_iterator d = old_metrics.begin();
4710 while (d != old_metrics.end()) {
4714 old_metrics.clear ();
4717 PropertyChanged (PropertyChange ());
4723 TempoMap::dump (std::ostream& o) const
4725 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4726 const MeterSection* m;
4727 const TempoSection* t;
4728 const TempoSection* prev_t = 0;
4730 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4732 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4733 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4734 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4735 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4736 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4738 o << " current start : " << t->note_types_per_minute()
4739 << " current end : " << t->end_note_types_per_minute()
4740 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4741 o << " previous : " << prev_t->note_types_per_minute()
4742 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4743 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4744 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4745 << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4746 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4749 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4750 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4751 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4752 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4755 o << "------" << std::endl;
4759 TempoMap::n_tempos() const
4761 Glib::Threads::RWLock::ReaderLock lm (lock);
4764 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4765 if ((*i)->is_tempo()) {
4774 TempoMap::n_meters() const
4776 Glib::Threads::RWLock::ReaderLock lm (lock);
4779 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4780 if (!(*i)->is_tempo()) {
4789 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4791 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4792 if ((*i)->frame() >= where && !(*i)->initial ()) {
4796 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4797 gui_set_meter_position (ms, (*i)->frame() + amount);
4800 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4801 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4806 PropertyChanged (PropertyChange ());
4810 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4814 std::list<MetricSection*> metric_kill_list;
4816 TempoSection* last_tempo = NULL;
4817 MeterSection* last_meter = NULL;
4818 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4819 bool meter_after = false; // is there a meter marker likewise?
4821 Glib::Threads::RWLock::WriterLock lm (lock);
4822 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4823 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4824 metric_kill_list.push_back(*i);
4825 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4828 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4832 else if ((*i)->frame() >= where) {
4833 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4834 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4835 if ((*i)->frame() == where) {
4836 // marker was immediately after end of range
4837 tempo_after = dynamic_cast<TempoSection*> (*i);
4838 meter_after = dynamic_cast<MeterSection*> (*i);
4844 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4845 if (last_tempo && !tempo_after) {
4846 metric_kill_list.remove(last_tempo);
4847 last_tempo->set_minute (minute_at_frame (where));
4850 if (last_meter && !meter_after) {
4851 metric_kill_list.remove(last_meter);
4852 last_meter->set_minute (minute_at_frame (where));
4856 //remove all the remaining metrics
4857 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4858 _metrics.remove(*i);
4863 recompute_map (_metrics);
4866 PropertyChanged (PropertyChange ());
4870 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4871 * pos can be -ve, if required.
4874 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4876 Glib::Threads::RWLock::ReaderLock lm (lock);
4877 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4879 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4883 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4885 Glib::Threads::RWLock::ReaderLock lm (lock);
4887 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4888 pos_bbt.ticks += op.ticks;
4889 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4891 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4893 pos_bbt.beats += op.beats;
4894 /* the meter in effect will start on the bar */
4895 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();
4896 while (pos_bbt.beats >= divisions_per_bar + 1) {
4898 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4899 pos_bbt.beats -= divisions_per_bar;
4901 pos_bbt.bars += op.bars;
4903 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4906 /** Count the number of beats that are equivalent to distance when going forward,
4910 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4912 Glib::Threads::RWLock::ReaderLock lm (lock);
4914 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4918 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4924 operator<< (std::ostream& o, const Meter& m) {
4925 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4929 operator<< (std::ostream& o, const Tempo& t) {
4930 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4934 operator<< (std::ostream& o, const MetricSection& section) {
4936 o << "MetricSection @ " << section.frame() << ' ';
4938 const TempoSection* ts;
4939 const MeterSection* ms;
4941 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4942 o << *((const Tempo*) ts);
4943 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4944 o << *((const Meter*) ms);