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)
162 _legacy_bbt = BBT_Time (0, 0, 0);
165 std::string start_bbt;
166 if (node.get_property ("start", start_bbt)) {
167 if (string_to_bbt_time (start_bbt, bbt)) {
168 /* legacy session - start used to be in bbt*/
171 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
175 // Don't worry about return value, exception will be thrown on error
176 MetricSection::set_state (node, Stateful::loading_state_version);
178 if (node.get_property ("beats-per-minute", _note_types_per_minute)) {
179 if (_note_types_per_minute < 0.0) {
180 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
181 throw failed_constructor();
185 if (node.get_property ("note-type", _note_type)) {
186 if (_note_type < 1.0) {
187 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
188 throw failed_constructor();
191 /* older session, make note type be quarter by default */
195 if (!node.get_property ("clamped", _clamped)) {
199 if (node.get_property ("end-beats-per-minute", _end_note_types_per_minute)) {
200 if (_end_note_types_per_minute < 0.0) {
201 info << _("TempoSection XML node has an illegal \"end-beats-per-minute\" value") << endmsg;
202 throw failed_constructor();
206 TempoSection::Type old_type;
207 if (node.get_property ("tempo-type", old_type)) {
208 /* sessions with a tempo-type node contain no end-beats-per-minute.
209 if the legacy node indicates a constant tempo, simply fill this in with the
210 start tempo. otherwise we need the next neighbour to know what it will be.
213 if (old_type == TempoSection::Constant) {
214 _end_note_types_per_minute = _note_types_per_minute;
216 _end_note_types_per_minute = -1.0;
220 if (!node.get_property ("active", _active)) {
221 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
225 if (!node.get_property ("locked-to-meter", _locked_to_meter)) {
227 set_locked_to_meter (true);
229 set_locked_to_meter (false);
233 /* 5.5 marked initial tempo as not locked to meter. this should always be true anyway */
235 set_locked_to_meter (true);
240 TempoSection::get_state() const
242 XMLNode *root = new XMLNode (xml_state_node_name);
244 MetricSection::add_state_to_node (*root);
246 root->set_property ("beats-per-minute", _note_types_per_minute);
247 root->set_property ("note-type", _note_type);
248 root->set_property ("clamped", _clamped);
249 root->set_property ("end-beats-per-minute", _end_note_types_per_minute);
250 root->set_property ("active", _active);
251 root->set_property ("locked-to-meter", _locked_to_meter);
256 /** returns the Tempo at the session-relative minute.
259 TempoSection::tempo_at_minute (const double& m) const
261 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
263 return Tempo (note_types_per_minute(), note_type());
266 return Tempo (_tempo_at_time (m - minute()), _note_type, _end_note_types_per_minute);
269 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
270 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
271 * @param p the pulse used to calculate the returned minute for constant tempi
272 * @return the minute at the supplied tempo
274 * note that the note_type is currently ignored in this function. see below.
278 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
279 * there should be no ramp between the two even if we are ramped.
280 * in other words a ramp should only place a curve on note_types_per_minute.
281 * we should be able to use Tempo note type here, but the above
282 * complicates things a bit.
285 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
287 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
289 return ((p - pulse()) / pulses_per_minute()) + minute();
292 return _time_at_tempo (ntpm) + minute();
295 /** returns the Tempo at the supplied whole-note pulse.
298 TempoSection::tempo_at_pulse (const double& p) const
300 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
303 return Tempo (note_types_per_minute(), note_type());
306 return Tempo (_tempo_at_pulse (p - pulse()), _note_type, _end_note_types_per_minute);
309 /** returns the whole-note pulse where a tempo in note types per minute occurs.
310 * constant tempi require minute m.
311 * @param ntpm the note types per minute value used to calculate the returned pulse
312 * @param m the minute used to calculate the returned pulse if the tempo is constant
313 * @return the whole-note pulse at the supplied tempo
315 * note that note_type is currently ignored in this function. see minute_at_tempo().
317 * for constant tempi, this is anaologous to pulse_at_minute().
320 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
322 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
324 return ((m - minute()) * pulses_per_minute()) + pulse();
327 return _pulse_at_tempo (ntpm) + pulse();
330 /** returns the whole-note pulse at the supplied session-relative minute.
333 TempoSection::pulse_at_minute (const double& m) const
335 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
337 return ((m - minute()) * pulses_per_minute()) + pulse();
340 return _pulse_at_time (m - minute()) + pulse();
343 /** returns the session-relative minute at the supplied whole-note pulse.
346 TempoSection::minute_at_pulse (const double& p) const
348 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
350 return ((p - pulse()) / pulses_per_minute()) + minute();
353 return _time_at_pulse (p - pulse()) + minute();
356 /** returns thw whole-note pulse at session frame position f.
357 * @param f the frame position.
358 * @return the position in whole-note pulses corresponding to f
360 * for use with musical units whose granularity is coarser than frames (e.g. ticks)
363 TempoSection::pulse_at_frame (const framepos_t& f) const
365 const bool constant = type() == Constant || _c == 0.0 || (initial() && f < frame());
367 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
370 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
374 TempoSection::frame_at_pulse (const double& p) const
376 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
378 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
381 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
389 Tt----|-----------------*|
390 Ta----|--------------|* |
396 _______________|___|____
397 time a t (next tempo)
400 Duration in beats at time a is the integral of some Tempo function.
401 In our case, the Tempo function (Tempo at time t) is
404 with function constant
409 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
410 b(t) = T0(e^(ct) - 1) / c
412 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:
413 t(b) = log((c.b / T0) + 1) / c
415 The time t at which Tempo T occurs is a as above:
416 t(T) = log(T / T0) / c
418 The beat at which a Tempo T occurs is:
421 The Tempo at which beat b occurs is:
424 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
425 Our problem is that we usually don't know t.
426 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.
427 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
428 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
430 By substituting our expanded t as a in the c function above, our problem is reduced to:
431 c = T0 (e^(log (Ta / T0)) - 1) / b
433 Of course the word 'beat' has been left loosely defined above.
434 In music, a beat is defined by the musical pulse (which comes from the tempo)
435 and the meter in use at a particular time (how many pulse divisions there are in one bar).
436 It would be more accurate to substitute the work 'pulse' for 'beat' above.
440 We can now store c for future time calculations.
441 If the following tempo section (the one that defines c in conjunction with this one)
442 is changed or moved, c is no longer valid.
444 The public methods are session-relative.
446 Most of this stuff is taken from this paper:
449 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
452 Zurich University of Arts
453 Institute for Computer Music and Sound Technology
455 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
459 /** compute this ramp's function constant from some tempo-pulse point
460 * @param end_npm end tempo (in note types per minute)
461 * @param end_pulse duration (pulses into global start) of some other position.
462 * @return the calculated function constant
465 TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const
467 if (note_types_per_minute() == end_npm || type() == Constant) {
471 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
472 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
475 /** compute the function constant from some tempo-time point.
476 * @param end_npm tempo (note types/min.)
477 * @param end_minute distance (in minutes) from session origin
478 * @return the calculated function constant
481 TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const
483 if (note_types_per_minute() == end_npm || type() == Constant) {
487 return c_func (end_npm, end_minute - minute());
490 /* position function */
492 TempoSection::a_func (double end_npm, double c) const
494 return log (end_npm / note_types_per_minute()) / c;
497 /*function constant*/
499 TempoSection::c_func (double end_npm, double end_time) const
501 return log (end_npm / note_types_per_minute()) / end_time;
504 /* tempo in note types per minute at time in minutes */
506 TempoSection::_tempo_at_time (const double& time) const
508 return exp (_c * time) * note_types_per_minute();
511 /* time in minutes at tempo in note types per minute */
513 TempoSection::_time_at_tempo (const double& npm) const
515 return log (npm / note_types_per_minute()) / _c;
518 /* pulse at tempo in note types per minute */
520 TempoSection::_pulse_at_tempo (const double& npm) const
522 return ((npm - note_types_per_minute()) / _c) / _note_type;
525 /* tempo in note types per minute at pulse */
527 TempoSection::_tempo_at_pulse (const double& pulse) const
529 return (pulse * _note_type * _c) + note_types_per_minute();
532 /* pulse at time in minutes */
534 TempoSection::_pulse_at_time (const double& time) const
536 return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
539 /* time in minutes at pulse */
541 TempoSection::_time_at_pulse (const double& pulse) const
543 return log1p ((_c * pulse * _note_type) / note_types_per_minute()) / _c;
546 /***********************************************************************/
548 const string MeterSection::xml_state_node_name = "Meter";
550 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
551 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
553 pair<double, BBT_Time> start;
557 if (node.get_property ("start", bbt_str)) {
558 if (string_to_bbt_time (bbt_str, start.second)) {
559 /* legacy session - start used to be in bbt*/
560 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
563 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
567 MetricSection::set_state (node, Stateful::loading_state_version);
569 node.get_property ("beat", start.first);
571 if (node.get_property ("bbt", bbt_str)) {
572 if (!string_to_bbt_time (bbt_str, start.second)) {
573 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
574 throw failed_constructor();
577 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
582 /* beats-per-bar is old; divisions-per-bar is new */
584 if (!node.get_property ("divisions-per-bar", _divisions_per_bar)) {
585 if (!node.get_property ("beats-per-bar", _divisions_per_bar)) {
586 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
587 throw failed_constructor();
591 if (_divisions_per_bar < 0.0) {
592 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
593 throw failed_constructor();
596 if (!node.get_property ("note-type", _note_type)) {
597 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
598 throw failed_constructor();
601 if (_note_type < 0.0) {
602 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
603 throw failed_constructor();
608 MeterSection::get_state() const
610 XMLNode *root = new XMLNode (xml_state_node_name);
612 MetricSection::add_state_to_node (*root);
615 bbt_time_to_string (_bbt, bbt_str);
616 root->set_property ("bbt", bbt_str);
617 root->set_property ("beat", beat());
618 root->set_property ("note-type", _note_type);
619 root->set_property ("divisions-per-bar", _divisions_per_bar);
624 /***********************************************************************/
628 Tempo determines the rate of musical pulse determined by its components
629 note types per minute - the rate per minute of the whole note divisor _note_type
630 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
631 Meter divides the musical pulse into measures and beats according to its components
635 TempoSection - translates between time, musical pulse and tempo.
636 has a musical location in whole notes (pulses).
637 has a time location in minutes.
638 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
639 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
641 MeterSection - translates between BBT, meter-based beat and musical pulse.
642 has a musical location in whole notes (pulses)
643 has a musical location in meter-based beats
644 has a musical location in BBT time
645 has a time location expressed in minutes.
647 TempoSection and MeterSection may be locked to either audio or music (position lock style).
648 The lock style determines the location type to be kept as a reference when location is recalculated.
650 The first tempo and meter are special. they must move together, and are locked to audio.
651 Audio locked tempi which lie before the first meter are made inactive.
653 Recomputing the map is the process where the 'missing' location types are calculated.
654 We construct the tempo map by first using the locked location type of each section
655 to determine non-locked location types (pulse or minute position).
656 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
658 Having done this, we can now traverse the Metrics list by pulse or minute
659 to query its relevant meter/tempo.
661 It is important to keep the _metrics in an order that makes sense.
662 Because ramped MusicTime and AudioTime tempos can interact with each other,
663 reordering is frequent. Care must be taken to keep _metrics in a solved state.
664 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
668 Music and audio-locked objects may seem interchangeable on the surface, but when translating
669 between audio samples and beat, remember that a sample is only a quantised approximation
670 of the actual time (in minutes) of a beat.
671 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
672 mean that this frame is the actual location in time of 1|3|0.
674 You cannot use a frame measurement to determine beat distance except under special circumstances
675 (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).
677 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
678 sample space the user is operating at to be translated correctly to the object.
680 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
681 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
682 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
684 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
686 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
687 The result is rounded to audio frames.
688 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
691 frame_at_beat (beat_at_frame (frame)) == frame
693 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
695 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
696 frames_between_quarter_notes () eliminats this effect when determining time duration
697 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
699 The above pointless example could instead do:
700 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
702 The Shaggs - Things I Wonder
703 https://www.youtube.com/watch?v=9wQK6zMJOoQ
706 struct MetricSectionSorter {
707 bool operator() (const MetricSection* a, const MetricSection* b) {
708 return a->pulse() < b->pulse();
712 struct MetricSectionFrameSorter {
713 bool operator() (const MetricSection* a, const MetricSection* b) {
714 return a->frame() < b->frame();
718 TempoMap::TempoMap (framecnt_t fr)
721 BBT_Time start (1, 1, 0);
723 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo, AudioTime, fr);
724 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
726 t->set_initial (true);
727 t->set_locked_to_meter (true);
729 m->set_initial (true);
731 /* note: frame time is correct (zero) for both of these */
733 _metrics.push_back (t);
734 _metrics.push_back (m);
739 TempoMap::operator= (TempoMap const & other)
741 if (&other != this) {
742 Glib::Threads::RWLock::ReaderLock lr (other.lock);
743 Glib::Threads::RWLock::WriterLock lm (lock);
744 _frame_rate = other._frame_rate;
746 Metrics::const_iterator d = _metrics.begin();
747 while (d != _metrics.end()) {
753 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
754 TempoSection const * const ts = dynamic_cast<TempoSection const * const> (*m);
755 MeterSection const * const ms = dynamic_cast<MeterSection const * const> (*m);
758 TempoSection* new_section = new TempoSection (*ts);
759 _metrics.push_back (new_section);
761 MeterSection* new_section = new MeterSection (*ms);
762 _metrics.push_back (new_section);
767 PropertyChanged (PropertyChange());
772 TempoMap::~TempoMap ()
774 Metrics::const_iterator d = _metrics.begin();
775 while (d != _metrics.end()) {
783 TempoMap::frame_at_minute (const double time) const
785 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
789 TempoMap::minute_at_frame (const framepos_t frame) const
791 return (frame / (double) _frame_rate) / 60.0;
795 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
797 bool removed = false;
800 Glib::Threads::RWLock::WriterLock lm (lock);
801 if ((removed = remove_tempo_locked (tempo))) {
802 if (complete_operation) {
803 recompute_map (_metrics);
808 if (removed && complete_operation) {
809 PropertyChanged (PropertyChange ());
814 TempoMap::remove_tempo_locked (const TempoSection& tempo)
818 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
819 if (dynamic_cast<TempoSection*> (*i) != 0) {
820 if (tempo.frame() == (*i)->frame()) {
821 if (!(*i)->initial()) {
834 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
836 bool removed = false;
839 Glib::Threads::RWLock::WriterLock lm (lock);
840 if ((removed = remove_meter_locked (tempo))) {
841 if (complete_operation) {
842 recompute_map (_metrics);
847 if (removed && complete_operation) {
848 PropertyChanged (PropertyChange ());
853 TempoMap::remove_meter_locked (const MeterSection& meter)
856 if (meter.position_lock_style() == AudioTime) {
857 /* remove meter-locked tempo */
858 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
860 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
861 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
870 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
871 if (dynamic_cast<MeterSection*> (*i) != 0) {
872 if (meter.frame() == (*i)->frame()) {
873 if (!(*i)->initial()) {
886 TempoMap::do_insert (MetricSection* section)
888 bool need_add = true;
889 /* we only allow new meters to be inserted on beat 1 of an existing
893 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
895 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
897 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
898 corrected.second.beats = 1;
899 corrected.second.ticks = 0;
900 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
901 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
902 m->bbt(), corrected.second) << endmsg;
903 //m->set_pulse (corrected);
907 /* Look for any existing MetricSection that is of the same type and
908 in the same bar as the new one, and remove it before adding
909 the new one. Note that this means that if we find a matching,
910 existing section, we can break out of the loop since we're
911 guaranteed that there is only one such match.
914 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
916 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
917 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
918 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
919 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
921 if (tempo && insert_tempo) {
924 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
925 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
927 if (tempo->initial()) {
929 /* can't (re)move this section, so overwrite
930 * its data content (but not its properties as
934 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
935 (*i)->set_position_lock_style (AudioTime);
944 } else if (meter && insert_meter) {
948 bool const ipm = insert_meter->position_lock_style() == MusicTime;
950 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
952 if (meter->initial()) {
954 /* can't (re)move this section, so overwrite
955 * its data content (but not its properties as
959 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
960 (*i)->set_position_lock_style (AudioTime);
970 /* non-matching types, so we don't care */
974 /* Add the given MetricSection, if we didn't just reset an existing
979 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
980 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
984 TempoSection* prev_t = 0;
986 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
987 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
988 bool const ipm = insert_meter->position_lock_style() == MusicTime;
991 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
995 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
999 prev_t = dynamic_cast<TempoSection*> (*i);
1002 } else if (insert_tempo) {
1003 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1004 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1007 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1008 const bool lm = insert_tempo->locked_to_meter();
1009 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1010 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1017 _metrics.insert (i, section);
1021 /* user supplies the exact pulse if pls == MusicTime */
1023 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1025 if (tempo.note_types_per_minute() <= 0.0) {
1026 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1030 TempoSection* ts = 0;
1032 Glib::Threads::RWLock::WriterLock lm (lock);
1033 /* here we default to not clamped for a new tempo section. preference? */
1034 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, false, false);
1036 recompute_map (_metrics);
1039 PropertyChanged (PropertyChange ());
1045 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1047 if (tempo.note_types_per_minute() <= 0.0) {
1048 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1052 bool const locked_to_meter = ts.locked_to_meter();
1053 bool const ts_clamped = ts.clamped();
1054 TempoSection* new_ts = 0;
1057 Glib::Threads::RWLock::WriterLock lm (lock);
1058 TempoSection& first (first_tempo());
1059 if (!ts.initial()) {
1060 if (locked_to_meter) {
1062 /* cannot move a meter-locked tempo section */
1063 *static_cast<Tempo*>(&ts) = tempo;
1064 recompute_map (_metrics);
1067 remove_tempo_locked (ts);
1068 new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter, ts_clamped);
1069 /* enforce clampedness of next tempo section */
1070 TempoSection* next_t = next_tempo_section_locked (_metrics, new_ts);
1071 if (next_t && next_t->clamped()) {
1072 next_t->set_note_types_per_minute (new_ts->end_note_types_per_minute());
1077 first.set_pulse (0.0);
1078 first.set_minute (minute_at_frame (frame));
1079 first.set_position_lock_style (AudioTime);
1080 first.set_locked_to_meter (true);
1081 first.set_clamped (ts_clamped);
1083 /* cannot move the first tempo section */
1084 *static_cast<Tempo*>(&first) = tempo;
1087 recompute_map (_metrics);
1090 PropertyChanged (PropertyChange ());
1094 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1095 , PositionLockStyle pls, bool recompute, bool locked_to_meter, bool clamped)
1097 TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _frame_rate);
1098 t->set_locked_to_meter (locked_to_meter);
1099 t->set_clamped (clamped);
1103 TempoSection* prev_tempo = 0;
1104 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1105 TempoSection* const this_t = dynamic_cast<TempoSection*>(*i);
1108 if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1109 prev_tempo->set_end_note_types_per_minute (t->note_types_per_minute());
1113 prev_tempo = this_t;
1118 if (pls == AudioTime) {
1119 solve_map_minute (_metrics, t, t->minute());
1121 solve_map_pulse (_metrics, t, t->pulse());
1123 recompute_meters (_metrics);
1130 TempoMap::add_meter (const Meter& meter, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1132 MeterSection* m = 0;
1134 Glib::Threads::RWLock::WriterLock lm (lock);
1135 m = add_meter_locked (meter, where, frame, pls, true);
1140 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1145 PropertyChanged (PropertyChange ());
1150 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1153 Glib::Threads::RWLock::WriterLock lm (lock);
1155 if (!ms.initial()) {
1156 remove_meter_locked (ms);
1157 add_meter_locked (meter, where, frame, pls, true);
1159 MeterSection& first (first_meter());
1160 TempoSection& first_t (first_tempo());
1161 /* cannot move the first meter section */
1162 *static_cast<Meter*>(&first) = meter;
1163 first.set_position_lock_style (AudioTime);
1164 first.set_pulse (0.0);
1165 first.set_minute (minute_at_frame (frame));
1166 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1167 first.set_beat (beat);
1168 first_t.set_minute (first.minute());
1169 first_t.set_locked_to_meter (true);
1170 first_t.set_pulse (0.0);
1171 first_t.set_position_lock_style (AudioTime);
1172 recompute_map (_metrics);
1176 PropertyChanged (PropertyChange ());
1180 TempoMap::add_meter_locked (const Meter& meter, const BBT_Time& bbt, framepos_t frame, PositionLockStyle pls, bool recompute)
1182 double const minute_at_bbt = minute_at_bbt_locked (_metrics, bbt);
1183 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_bbt - minute_at_frame (1));
1184 double const pulse = ((bbt.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1185 /* the natural time of the BBT position */
1186 double const time_minutes = minute_at_pulse_locked (_metrics, pulse);
1188 if (pls == AudioTime) {
1189 /* add meter-locked tempo at the natural time in the current map (frame may differ). */
1190 Tempo const tempo_at_time = tempo_at_minute_locked (_metrics, time_minutes);
1191 TempoSection* mlt = add_tempo_locked (tempo_at_time, pulse, time_minutes, AudioTime, true, true, false);
1197 /* still using natural time for the position, ignoring lock style. */
1198 MeterSection* new_meter = new MeterSection (pulse, time_minutes, beat_at_bbt_locked (_metrics, bbt), bbt, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1200 bool solved = false;
1202 do_insert (new_meter);
1206 if (pls == AudioTime) {
1207 /* now set the audio locked meter's position to frame */
1208 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1209 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1210 fudge frame so that the meter ends up at its BBT position instead.
1213 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1216 solved = solve_map_bbt (_metrics, new_meter, bbt);
1217 /* required due to resetting the pulse of meter-locked tempi above.
1218 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1219 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1221 recompute_map (_metrics);
1225 if (!solved && recompute) {
1226 /* if this has failed to solve, there is little we can do other than to ensure that
1227 the new map is recalculated.
1229 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1230 recompute_map (_metrics);
1237 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1239 Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1242 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1243 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1248 Glib::Threads::RWLock::WriterLock lm (lock);
1249 *((Tempo*) t) = newtempo;
1250 recompute_map (_metrics);
1252 PropertyChanged (PropertyChange ());
1259 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1261 Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1264 TempoSection* first;
1265 Metrics::iterator i;
1267 /* find the TempoSection immediately preceding "where"
1270 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1272 if ((*i)->frame() > where) {
1278 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1291 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1301 Glib::Threads::RWLock::WriterLock lm (lock);
1302 /* cannot move the first tempo section */
1303 *((Tempo*)prev) = newtempo;
1304 recompute_map (_metrics);
1307 PropertyChanged (PropertyChange ());
1311 TempoMap::first_meter () const
1313 const MeterSection *m = 0;
1315 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1316 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1321 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1322 abort(); /*NOTREACHED*/
1327 TempoMap::first_meter ()
1329 MeterSection *m = 0;
1331 /* CALLER MUST HOLD LOCK */
1333 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1334 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1339 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1340 abort(); /*NOTREACHED*/
1345 TempoMap::first_tempo () const
1347 const TempoSection *t = 0;
1349 /* CALLER MUST HOLD LOCK */
1351 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1352 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1362 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1363 abort(); /*NOTREACHED*/
1368 TempoMap::first_tempo ()
1370 TempoSection *t = 0;
1372 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1373 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1383 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1384 abort(); /*NOTREACHED*/
1388 TempoMap::recompute_tempi (Metrics& metrics)
1390 TempoSection* prev_t = 0;
1392 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1395 if ((*i)->is_tempo()) {
1396 t = static_cast<TempoSection*> (*i);
1409 if (t->position_lock_style() == AudioTime) {
1410 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1411 if (!t->locked_to_meter()) {
1412 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1416 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1417 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1425 prev_t->set_c (0.0);
1428 /* tempos must be positioned correctly.
1429 the current approach is to use a meter's bbt time as its base position unit.
1430 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1431 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1434 TempoMap::recompute_meters (Metrics& metrics)
1436 MeterSection* meter = 0;
1437 MeterSection* prev_m = 0;
1439 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1440 if (!(*mi)->is_tempo()) {
1441 meter = static_cast<MeterSection*> (*mi);
1442 if (meter->position_lock_style() == AudioTime) {
1444 pair<double, BBT_Time> b_bbt;
1445 TempoSection* meter_locked_tempo = 0;
1446 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1448 if ((*ii)->is_tempo()) {
1449 t = static_cast<TempoSection*> (*ii);
1450 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1451 meter_locked_tempo = t;
1458 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1459 if (beats + prev_m->beat() != meter->beat()) {
1460 /* reordering caused a bbt change */
1462 beats = meter->beat() - prev_m->beat();
1463 b_bbt = make_pair (beats + prev_m->beat()
1464 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1465 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1467 } else if (!meter->initial()) {
1468 b_bbt = make_pair (meter->beat(), meter->bbt());
1469 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1472 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1474 if (meter_locked_tempo) {
1475 meter_locked_tempo->set_pulse (pulse);
1477 meter->set_beat (b_bbt);
1478 meter->set_pulse (pulse);
1483 pair<double, BBT_Time> b_bbt;
1485 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1486 if (beats + prev_m->beat() != meter->beat()) {
1487 /* reordering caused a bbt change */
1488 b_bbt = make_pair (beats + prev_m->beat()
1489 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1491 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1493 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1495 /* shouldn't happen - the first is audio-locked */
1496 pulse = pulse_at_beat_locked (metrics, meter->beat());
1497 b_bbt = make_pair (meter->beat(), meter->bbt());
1500 meter->set_beat (b_bbt);
1501 meter->set_pulse (pulse);
1502 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1511 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1513 /* CALLER MUST HOLD WRITE LOCK */
1515 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1518 /* silly call from Session::process() during startup
1523 recompute_tempi (metrics);
1524 recompute_meters (metrics);
1528 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1530 Glib::Threads::RWLock::ReaderLock lm (lock);
1531 TempoMetric m (first_meter(), first_tempo());
1534 *last = ++_metrics.begin();
1537 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1538 at something, because we insert the default tempo and meter during
1539 TempoMap construction.
1541 now see if we can find better candidates.
1544 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1546 if ((*i)->frame() > frame) {
1560 /* XX meters only */
1562 TempoMap::metric_at (BBT_Time bbt) const
1564 Glib::Threads::RWLock::ReaderLock lm (lock);
1565 TempoMetric m (first_meter(), first_tempo());
1567 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1568 at something, because we insert the default tempo and meter during
1569 TempoMap construction.
1571 now see if we can find better candidates.
1574 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1576 if (!(*i)->is_tempo()) {
1577 mw = static_cast<MeterSection*> (*i);
1578 BBT_Time section_start (mw->bbt());
1580 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1591 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1592 * @param frame The session frame position.
1593 * @return The beat duration according to the tempo map at the supplied frame.
1595 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1596 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1598 * This function uses both tempo and meter.
1601 TempoMap::beat_at_frame (const framecnt_t& frame) const
1603 Glib::Threads::RWLock::ReaderLock lm (lock);
1605 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1608 /* This function uses both tempo and meter.*/
1610 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1612 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1613 MeterSection* prev_m = 0;
1614 MeterSection* next_m = 0;
1616 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1617 if (!(*i)->is_tempo()) {
1618 if (prev_m && (*i)->minute() > minute) {
1619 next_m = static_cast<MeterSection*> (*i);
1622 prev_m = static_cast<MeterSection*> (*i);
1626 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1628 /* audio locked meters fake their beat */
1629 if (next_m && next_m->beat() < beat) {
1630 return next_m->beat();
1636 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1637 * @param beat The BBT (meter-based) beat.
1638 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1640 * This function uses both tempo and meter.
1643 TempoMap::frame_at_beat (const double& beat) const
1645 Glib::Threads::RWLock::ReaderLock lm (lock);
1647 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1650 /* meter & tempo section based */
1652 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1654 MeterSection* prev_m = 0;
1655 TempoSection* prev_t = 0;
1659 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1660 if (!(*i)->is_tempo()) {
1661 m = static_cast<MeterSection*> (*i);
1662 if (prev_m && m->beat() > beat) {
1672 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1673 if ((*i)->is_tempo()) {
1674 t = static_cast<TempoSection*> (*i);
1680 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1689 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1692 /** Returns a Tempo corresponding to the supplied frame position.
1693 * @param frame The audio frame.
1694 * @return a Tempo according to the tempo map at the supplied frame.
1698 TempoMap::tempo_at_frame (const framepos_t& frame) const
1700 Glib::Threads::RWLock::ReaderLock lm (lock);
1702 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1706 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1708 TempoSection* prev_t = 0;
1712 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1713 if ((*i)->is_tempo()) {
1714 t = static_cast<TempoSection*> (*i);
1718 if ((prev_t) && t->minute() > minute) {
1719 /* t is the section past frame */
1720 return prev_t->tempo_at_minute (minute);
1726 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1729 /** returns the frame at which the supplied tempo occurs, or
1730 * the frame of the last tempo section (search exhausted)
1731 * only the position of the first occurence will be returned
1735 TempoMap::frame_at_tempo (const Tempo& tempo) const
1737 Glib::Threads::RWLock::ReaderLock lm (lock);
1739 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1743 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1745 TempoSection* prev_t = 0;
1746 const double tempo_bpm = tempo.note_types_per_minute();
1748 Metrics::const_iterator i;
1750 for (i = metrics.begin(); i != metrics.end(); ++i) {
1752 if ((*i)->is_tempo()) {
1753 t = static_cast<TempoSection*> (*i);
1761 if (t->note_types_per_minute() == tempo_bpm) {
1766 const double prev_t_bpm = prev_t->note_types_per_minute();
1767 const double prev_t_end_bpm = prev_t->end_note_types_per_minute();
1768 if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm)
1769 || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm)
1770 || (prev_t_end_bpm == tempo_bpm)) {
1772 return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
1779 return prev_t->minute();
1783 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1785 TempoSection* prev_t = 0;
1789 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1790 if ((*i)->is_tempo()) {
1791 t = static_cast<TempoSection*> (*i);
1795 if ((prev_t) && t->pulse() > pulse) {
1796 /* t is the section past frame */
1797 return prev_t->tempo_at_pulse (pulse);
1803 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1807 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1809 TempoSection* prev_t = 0;
1810 const double tempo_bpm = tempo.note_types_per_minute();
1812 Metrics::const_iterator i;
1814 for (i = metrics.begin(); i != metrics.end(); ++i) {
1816 if ((*i)->is_tempo()) {
1817 t = static_cast<TempoSection*> (*i);
1823 const double t_bpm = t->note_types_per_minute();
1825 if (t_bpm == tempo_bpm) {
1830 const double prev_t_bpm = prev_t->note_types_per_minute();
1832 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1833 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1840 return prev_t->pulse();
1843 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1844 * @param qn the position in quarter note beats.
1845 * @return the Tempo at the supplied quarter-note.
1848 TempoMap::tempo_at_quarter_note (const double& qn) const
1850 Glib::Threads::RWLock::ReaderLock lm (lock);
1852 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1855 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1856 * @param tempo the tempo.
1857 * @return the position in quarter-note beats where the map bpm
1858 * is equal to that of the Tempo. currently ignores note_type.
1861 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1863 Glib::Threads::RWLock::ReaderLock lm (lock);
1865 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;
1868 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1869 * @param metrics the list of metric sections used to calculate the pulse.
1870 * @param beat The BBT (meter-based) beat.
1871 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1873 * a pulse or whole note is the base musical position of a MetricSection.
1874 * it is equivalent to four quarter notes.
1878 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1880 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1882 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1885 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1886 * @param metrics the list of metric sections used to calculate the beat.
1887 * @param pulse the whole-note pulse.
1888 * @return the meter-based beat at the supplied whole-note pulse.
1890 * a pulse or whole note is the base musical position of a MetricSection.
1891 * it is equivalent to four quarter notes.
1894 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1896 MeterSection* prev_m = 0;
1898 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1900 if (!(*i)->is_tempo()) {
1901 m = static_cast<MeterSection*> (*i);
1902 if (prev_m && m->pulse() > pulse) {
1910 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1914 /* tempo section based */
1916 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1918 /* HOLD (at least) THE READER LOCK */
1919 TempoSection* prev_t = 0;
1921 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1923 if ((*i)->is_tempo()) {
1924 t = static_cast<TempoSection*> (*i);
1928 if (prev_t && t->minute() > minute) {
1929 /*the previous ts is the one containing the frame */
1930 const double ret = prev_t->pulse_at_minute (minute);
1931 /* audio locked section in new meter*/
1932 if (t->pulse() < ret) {
1941 /* treated as constant for this ts */
1942 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1944 return pulses_in_section + prev_t->pulse();
1947 /* tempo section based */
1949 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1951 /* HOLD THE READER LOCK */
1953 const TempoSection* prev_t = 0;
1955 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1958 if ((*i)->is_tempo()) {
1959 t = static_cast<TempoSection*> (*i);
1963 if (prev_t && t->pulse() > pulse) {
1964 return prev_t->minute_at_pulse (pulse);
1970 /* must be treated as constant, irrespective of _type */
1971 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1973 return dtime + prev_t->minute();
1976 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1977 * @param bbt The BBT time (meter-based).
1978 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1982 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1984 Glib::Threads::RWLock::ReaderLock lm (lock);
1985 return beat_at_bbt_locked (_metrics, bbt);
1990 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1992 /* CALLER HOLDS READ LOCK */
1994 MeterSection* prev_m = 0;
1996 /* because audio-locked meters have 'fake' integral beats,
1997 there is no pulse offset here.
2001 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2002 if (!(*i)->is_tempo()) {
2003 m = static_cast<MeterSection*> (*i);
2005 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2006 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2014 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2015 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2016 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2021 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2022 * @param beat The BBT (meter-based) beat.
2023 * @return The BBT time (meter-based) at the supplied meter-based beat.
2027 TempoMap::bbt_at_beat (const double& beat)
2029 Glib::Threads::RWLock::ReaderLock lm (lock);
2030 return bbt_at_beat_locked (_metrics, beat);
2034 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2036 /* CALLER HOLDS READ LOCK */
2037 MeterSection* prev_m = 0;
2038 const double beats = max (0.0, b);
2040 MeterSection* m = 0;
2042 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2043 if (!(*i)->is_tempo()) {
2044 m = static_cast<MeterSection*> (*i);
2046 if (m->beat() > beats) {
2047 /* this is the meter after the one our beat is on*/
2057 const double beats_in_ms = beats - prev_m->beat();
2058 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2059 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2060 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2061 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2065 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2066 ret.beats = (uint32_t) floor (remaining_beats);
2067 ret.bars = total_bars;
2069 /* 0 0 0 to 1 1 0 - based mapping*/
2073 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2075 ret.ticks -= BBT_Time::ticks_per_beat;
2078 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2086 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2087 * @param bbt The BBT time (meter-based).
2088 * @return the quarter note beat at the supplied BBT time
2090 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2092 * while the input uses meter, the output does not.
2095 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2097 Glib::Threads::RWLock::ReaderLock lm (lock);
2099 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2103 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2105 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2108 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2111 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2115 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2117 /* CALLER HOLDS READ LOCK */
2119 MeterSection* prev_m = 0;
2121 /* because audio-locked meters have 'fake' integral beats,
2122 there is no pulse offset here.
2126 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2127 if (!(*i)->is_tempo()) {
2128 m = static_cast<MeterSection*> (*i);
2130 if (m->bbt().bars > bbt.bars) {
2138 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2139 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2140 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2145 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2146 * @param qn the quarter-note beat.
2147 * @return The BBT time (meter-based) at the supplied meter-based beat.
2149 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2153 TempoMap::bbt_at_quarter_note (const double& qn)
2155 Glib::Threads::RWLock::ReaderLock lm (lock);
2157 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2160 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2161 * @param metrics The list of metric sections used to determine the result.
2162 * @param pulse The whole-note pulse.
2163 * @return The BBT time at the supplied whole-note pulse.
2165 * a pulse or whole note is the basic musical position of a MetricSection.
2166 * it is equivalent to four quarter notes.
2167 * while the output uses meter, the input does not.
2170 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2172 MeterSection* prev_m = 0;
2174 MeterSection* m = 0;
2176 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2178 if (!(*i)->is_tempo()) {
2179 m = static_cast<MeterSection*> (*i);
2182 double const pulses_to_m = m->pulse() - prev_m->pulse();
2183 if (prev_m->pulse() + pulses_to_m > pulse) {
2184 /* this is the meter after the one our beat is on*/
2195 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2196 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2197 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2198 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2199 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2203 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2204 ret.beats = (uint32_t) floor (remaining_beats);
2205 ret.bars = total_bars;
2207 /* 0 0 0 to 1 1 0 mapping*/
2211 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2213 ret.ticks -= BBT_Time::ticks_per_beat;
2216 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2224 /** Returns the BBT time corresponding to the supplied frame position.
2225 * @param frame the position in audio samples.
2226 * @return the BBT time at the frame position .
2230 TempoMap::bbt_at_frame (framepos_t frame)
2238 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2243 const double minute = minute_at_frame (frame);
2245 Glib::Threads::RWLock::ReaderLock lm (lock);
2247 return bbt_at_minute_locked (_metrics, minute);
2251 TempoMap::bbt_at_frame_rt (framepos_t frame)
2253 const double minute = minute_at_frame (frame);
2255 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2258 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2261 return bbt_at_minute_locked (_metrics, minute);
2265 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2275 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2276 MeterSection* prev_m = 0;
2277 MeterSection* next_m = 0;
2281 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2282 if (!(*i)->is_tempo()) {
2283 m = static_cast<MeterSection*> (*i);
2284 if (prev_m && m->minute() > minute) {
2292 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2294 /* handle frame before first meter */
2295 if (minute < prev_m->minute()) {
2298 /* audio locked meters fake their beat */
2299 if (next_m && next_m->beat() < beat) {
2300 beat = next_m->beat();
2303 beat = max (0.0, beat);
2305 const double beats_in_ms = beat - prev_m->beat();
2306 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2307 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2308 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2309 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2313 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2314 ret.beats = (uint32_t) floor (remaining_beats);
2315 ret.bars = total_bars;
2317 /* 0 0 0 to 1 1 0 - based mapping*/
2321 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2323 ret.ticks -= BBT_Time::ticks_per_beat;
2326 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2334 /** Returns the frame position corresponding to the supplied BBT time.
2335 * @param bbt the position in BBT time.
2336 * @return the frame position at bbt.
2340 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2344 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2349 if (bbt.beats < 1) {
2350 throw std::logic_error ("beats are counted from one");
2355 Glib::Threads::RWLock::ReaderLock lm (lock);
2356 minute = minute_at_bbt_locked (_metrics, bbt);
2359 return frame_at_minute (minute);
2362 /* meter & tempo section based */
2364 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2366 /* HOLD THE READER LOCK */
2368 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2373 * Returns the quarter-note beat position corresponding to the supplied frame.
2375 * @param frame the position in frames.
2376 * @return The quarter-note position of the supplied frame. Ignores meter.
2380 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2382 const double minute = minute_at_frame (frame);
2384 Glib::Threads::RWLock::ReaderLock lm (lock);
2386 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2390 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2392 const double minute = minute_at_frame (frame);
2394 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2397 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2400 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2404 * Returns the frame position corresponding to the supplied quarter-note beat.
2406 * @param quarter_note the quarter-note position.
2407 * @return the frame position of the supplied quarter-note. Ignores meter.
2412 TempoMap::frame_at_quarter_note (const double quarter_note) const
2416 Glib::Threads::RWLock::ReaderLock lm (lock);
2418 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2421 return frame_at_minute (minute);
2424 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2425 * @param beat The BBT (meter-based) beat.
2426 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2428 * a quarter-note may be compared with and assigned to Evoral::Beats.
2432 TempoMap::quarter_note_at_beat (const double beat) const
2434 Glib::Threads::RWLock::ReaderLock lm (lock);
2436 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2439 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2440 * @param quarter_note The position in quarter-note beats.
2441 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2443 * a quarter-note is the musical unit of Evoral::Beats.
2447 TempoMap::beat_at_quarter_note (const double quarter_note) const
2449 Glib::Threads::RWLock::ReaderLock lm (lock);
2451 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2454 /** Returns the duration in frames between two supplied quarter-note beat positions.
2455 * @param start the first position in quarter-note beats.
2456 * @param end the end position in quarter-note beats.
2457 * @return the frame distance ober the quarter-note beats duration.
2459 * use this rather than e.g.
2460 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2461 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2465 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2470 Glib::Threads::RWLock::ReaderLock lm (lock);
2471 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2474 return frame_at_minute (minutes);
2478 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2481 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2485 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2487 Glib::Threads::RWLock::ReaderLock lm (lock);
2489 return quarter_notes_between_frames_locked (_metrics, start, end);
2493 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2495 const TempoSection* prev_t = 0;
2497 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2500 if ((*i)->is_tempo()) {
2501 t = static_cast<TempoSection*> (*i);
2505 if (prev_t && t->frame() > start) {
2512 const double start_qn = prev_t->pulse_at_frame (start);
2514 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2517 if ((*i)->is_tempo()) {
2518 t = static_cast<TempoSection*> (*i);
2522 if (prev_t && t->frame() > end) {
2528 const double end_qn = prev_t->pulse_at_frame (end);
2530 return (end_qn - start_qn) * 4.0;
2534 TempoMap::check_solved (const Metrics& metrics) const
2536 TempoSection* prev_t = 0;
2537 MeterSection* prev_m = 0;
2539 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2542 if ((*i)->is_tempo()) {
2543 t = static_cast<TempoSection*> (*i);
2548 /* check ordering */
2549 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2553 /* precision check ensures tempo and frames align.*/
2554 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2555 if (!t->locked_to_meter()) {
2560 /* gradient limit - who knows what it should be?
2561 things are also ok (if a little chaotic) without this
2563 if (fabs (prev_t->c()) > 1000.0) {
2564 //std::cout << "c : " << prev_t->c() << std::endl;
2571 if (!(*i)->is_tempo()) {
2572 m = static_cast<MeterSection*> (*i);
2573 if (prev_m && m->position_lock_style() == AudioTime) {
2574 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2575 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2576 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2578 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2592 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2594 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2596 if ((*i)->is_tempo()) {
2597 t = static_cast<TempoSection*> (*i);
2598 if (t->locked_to_meter()) {
2599 t->set_active (true);
2600 } else if (t->position_lock_style() == AudioTime) {
2601 if (t->frame() < frame) {
2602 t->set_active (false);
2603 t->set_pulse (-1.0);
2604 } else if (t->frame() > frame) {
2605 t->set_active (true);
2606 } else if (t->frame() == frame) {
2616 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2618 TempoSection* prev_t = 0;
2619 TempoSection* section_prev = 0;
2620 double first_m_minute = 0.0;
2621 const bool sml = section->locked_to_meter();
2623 /* can't move a tempo before the first meter */
2624 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2626 if (!(*i)->is_tempo()) {
2627 m = static_cast<MeterSection*> (*i);
2629 first_m_minute = m->minute();
2634 if (!section->initial() && minute <= first_m_minute) {
2638 section->set_active (true);
2639 section->set_minute (minute);
2641 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2643 if ((*i)->is_tempo()) {
2644 t = static_cast<TempoSection*> (*i);
2656 if (t->frame() == frame_at_minute (minute)) {
2660 const bool tlm = t->position_lock_style() == MusicTime;
2662 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2663 section_prev = prev_t;
2665 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2666 if (!section->locked_to_meter()) {
2667 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2672 if (t->position_lock_style() == MusicTime) {
2673 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2674 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2676 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2677 if (!t->locked_to_meter()) {
2678 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2686 MetricSectionFrameSorter fcmp;
2687 imaginary.sort (fcmp);
2689 recompute_tempi (imaginary);
2691 if (check_solved (imaginary)) {
2699 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2701 TempoSection* prev_t = 0;
2702 TempoSection* section_prev = 0;
2704 section->set_pulse (pulse);
2706 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2708 if ((*i)->is_tempo()) {
2709 t = static_cast<TempoSection*> (*i);
2720 section_prev = prev_t;
2724 if (t->position_lock_style() == MusicTime) {
2725 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2726 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2728 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2729 if (!t->locked_to_meter()) {
2730 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2739 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2740 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2743 MetricSectionSorter cmp;
2744 imaginary.sort (cmp);
2746 recompute_tempi (imaginary);
2748 * XX need a restriction here, but only for this case,
2749 * as audio locked tempos don't interact in the same way.
2751 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2753 * |50 bpm |250 bpm |60 bpm
2754 * drag 250 to the pulse after 60->
2755 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2757 if (check_solved (imaginary)) {
2765 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2767 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2768 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2769 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2773 if (section->initial()) {
2774 /* lock the first tempo to our first meter */
2775 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2780 TempoSection* meter_locked_tempo = 0;
2782 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2784 if ((*ii)->is_tempo()) {
2785 t = static_cast<TempoSection*> (*ii);
2786 if (t->locked_to_meter() && t->frame() == section->frame()) {
2787 meter_locked_tempo = t;
2793 if (!meter_locked_tempo) {
2797 MeterSection* prev_m = 0;
2799 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2800 bool solved = false;
2802 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2804 if (!(*i)->is_tempo()) {
2805 m = static_cast<MeterSection*> (*i);
2807 if (prev_m && !section->initial()) {
2808 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2809 if (beats + prev_m->beat() < section->beat()) {
2810 /* set the section pulse according to its musical position,
2811 * as an earlier time than this has been requested.
2813 const double new_pulse = ((section->beat() - prev_m->beat())
2814 / prev_m->note_divisor()) + prev_m->pulse();
2816 tempo_copy->set_position_lock_style (MusicTime);
2817 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2818 meter_locked_tempo->set_position_lock_style (MusicTime);
2819 section->set_position_lock_style (MusicTime);
2820 section->set_pulse (new_pulse);
2821 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2822 meter_locked_tempo->set_position_lock_style (AudioTime);
2823 section->set_position_lock_style (AudioTime);
2824 section->set_minute (meter_locked_tempo->minute());
2830 Metrics::const_iterator d = future_map.begin();
2831 while (d != future_map.end()) {
2840 /* all is ok. set section's locked tempo if allowed.
2841 possibly disallow if there is an adjacent audio-locked tempo.
2842 XX this check could possibly go. its never actually happened here.
2844 MeterSection* meter_copy = const_cast<MeterSection*>
2845 (&meter_section_at_minute_locked (future_map, section->minute()));
2847 meter_copy->set_minute (minute);
2849 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2850 section->set_minute (minute);
2851 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2852 / prev_m->note_divisor()) + prev_m->pulse());
2853 solve_map_minute (imaginary, meter_locked_tempo, minute);
2858 Metrics::const_iterator d = future_map.begin();
2859 while (d != future_map.end()) {
2869 /* initial (first meter atm) */
2871 tempo_copy->set_minute (minute);
2872 tempo_copy->set_pulse (0.0);
2874 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2875 section->set_minute (minute);
2876 meter_locked_tempo->set_minute (minute);
2877 meter_locked_tempo->set_pulse (0.0);
2878 solve_map_minute (imaginary, meter_locked_tempo, minute);
2883 Metrics::const_iterator d = future_map.begin();
2884 while (d != future_map.end()) {
2893 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2894 section->set_beat (b_bbt);
2895 section->set_pulse (0.0);
2905 MetricSectionFrameSorter fcmp;
2906 imaginary.sort (fcmp);
2908 recompute_meters (imaginary);
2914 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2916 /* disallow setting section to an existing meter's bbt */
2917 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2919 if (!(*i)->is_tempo()) {
2920 m = static_cast<MeterSection*> (*i);
2921 if (m != section && m->bbt().bars == when.bars) {
2927 MeterSection* prev_m = 0;
2928 MeterSection* section_prev = 0;
2930 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2932 if (!(*i)->is_tempo()) {
2933 m = static_cast<MeterSection*> (*i);
2939 pair<double, BBT_Time> b_bbt;
2940 double new_pulse = 0.0;
2942 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2943 section_prev = prev_m;
2945 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2946 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2947 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2949 section->set_beat (b_bbt);
2950 section->set_pulse (pulse);
2951 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2955 if (m->position_lock_style() == AudioTime) {
2956 TempoSection* meter_locked_tempo = 0;
2958 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2960 if ((*ii)->is_tempo()) {
2961 t = static_cast<TempoSection*> (*ii);
2962 if (t->locked_to_meter() && t->frame() == m->frame()) {
2963 meter_locked_tempo = t;
2969 if (!meter_locked_tempo) {
2974 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2976 if (beats + prev_m->beat() != m->beat()) {
2977 /* tempo/ meter change caused a change in beat (bar). */
2979 /* the user has requested that the previous section of music overlaps this one.
2980 we have no choice but to change the bar number here, as being locked to audio means
2981 we must stay where we are on the timeline.
2983 beats = m->beat() - prev_m->beat();
2984 b_bbt = make_pair (beats + prev_m->beat()
2985 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2986 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2988 } else if (!m->initial()) {
2989 b_bbt = make_pair (m->beat(), m->bbt());
2990 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2993 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2996 meter_locked_tempo->set_pulse (new_pulse);
2997 m->set_beat (b_bbt);
2998 m->set_pulse (new_pulse);
3002 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3003 if (beats + prev_m->beat() != m->beat()) {
3004 /* tempo/ meter change caused a change in beat (bar). */
3005 b_bbt = make_pair (beats + prev_m->beat()
3006 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3008 b_bbt = make_pair (beats + prev_m->beat()
3011 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3012 m->set_beat (b_bbt);
3013 m->set_pulse (new_pulse);
3014 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3021 if (!section_prev) {
3023 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3024 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3025 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3027 section->set_beat (b_bbt);
3028 section->set_pulse (pulse);
3029 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3032 MetricSectionSorter cmp;
3033 imaginary.sort (cmp);
3035 recompute_meters (imaginary);
3040 /** places a copy of _metrics into copy and returns a pointer
3041 * to section's equivalent in copy.
3044 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section) const
3046 TempoSection* ret = 0;
3048 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3049 if ((*i)->is_tempo()) {
3050 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3052 ret = new TempoSection (*t);
3053 copy.push_back (ret);
3057 TempoSection* cp = new TempoSection (*t);
3058 copy.push_back (cp);
3060 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3061 MeterSection* cp = new MeterSection (*m);
3062 copy.push_back (cp);
3070 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section) const
3072 MeterSection* ret = 0;
3074 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3075 if ((*i)->is_tempo()) {
3076 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3077 TempoSection* cp = new TempoSection (*t);
3078 copy.push_back (cp);
3080 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3082 ret = new MeterSection (*m);
3083 copy.push_back (ret);
3086 MeterSection* cp = new MeterSection (*m);
3087 copy.push_back (cp);
3094 /** answers the question "is this a valid beat position for this tempo section?".
3095 * it returns true if the tempo section can be moved to the requested bbt position,
3096 * leaving the tempo map in a solved state.
3097 * @param ts the tempo section to be moved
3098 * @param bbt the requested new position for the tempo section
3099 * @return true if the tempo section can be moved to the position, otherwise false.
3102 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3105 TempoSection* tempo_copy = 0;
3108 Glib::Threads::RWLock::ReaderLock lm (lock);
3109 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3115 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3117 Metrics::const_iterator d = copy.begin();
3118 while (d != copy.end()) {
3127 * 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,
3128 * taking any possible reordering as a consequence of this into account.
3129 * @param section - the section to be altered
3130 * @param bbt - the BBT time where the altered tempo will fall
3131 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3133 pair<double, framepos_t>
3134 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3137 pair<double, framepos_t> ret = make_pair (0.0, 0);
3139 Glib::Threads::RWLock::ReaderLock lm (lock);
3141 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3143 const double beat = beat_at_bbt_locked (future_map, bbt);
3145 if (section->position_lock_style() == AudioTime) {
3146 tempo_copy->set_position_lock_style (MusicTime);
3149 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3150 ret.first = tempo_copy->pulse();
3151 ret.second = tempo_copy->frame();
3153 ret.first = section->pulse();
3154 ret.second = section->frame();
3157 Metrics::const_iterator d = future_map.begin();
3158 while (d != future_map.end()) {
3165 /** moves a TempoSection to a specified position.
3166 * @param ts - the section to be moved
3167 * @param frame - the new position in frames for the tempo
3168 * @param sub_num - the snap division to use if using musical time.
3170 * if sub_num is non-zero, the frame position is used to calculate an exact
3173 * -1 | snap to bars (meter-based)
3174 * 0 | no snap - use audio frame for musical position
3175 * 1 | snap to meter-based (BBT) beat
3176 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3178 * this follows the snap convention in the gui.
3179 * if sub_num is zero, the musical position will be taken from the supplied frame.
3182 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3186 if (ts->position_lock_style() == MusicTime) {
3188 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3189 Glib::Threads::RWLock::WriterLock lm (lock);
3190 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3192 tempo_copy->set_position_lock_style (AudioTime);
3194 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3195 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3196 const double pulse = pulse_at_beat_locked (future_map, beat);
3198 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3199 solve_map_pulse (_metrics, ts, pulse);
3200 recompute_meters (_metrics);
3208 Glib::Threads::RWLock::WriterLock lm (lock);
3209 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3213 /* We're moving the object that defines the grid while snapping to it...
3214 * Placing the ts at the beat corresponding to the requested frame may shift the
3215 * grid in such a way that the mouse is left hovering over a completerly different division,
3216 * causing jittering when the mouse next moves (esp. large tempo deltas).
3217 * We fudge around this by doing this in the musical domain and then swapping back for the recompute.
3219 const double qn = exact_qn_at_frame_locked (_metrics, frame, sub_num);
3220 tempo_copy->set_position_lock_style (MusicTime);
3221 if (solve_map_pulse (future_map, tempo_copy, qn / 4.0)) {
3222 ts->set_position_lock_style (MusicTime);
3223 solve_map_pulse (_metrics, ts, qn / 4.0);
3224 ts->set_position_lock_style (AudioTime);
3225 recompute_meters (_metrics);
3228 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3229 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3230 recompute_meters (_metrics);
3236 Metrics::const_iterator d = future_map.begin();
3237 while (d != future_map.end()) {
3242 MetricPositionChanged (PropertyChange ()); // Emit Signal
3245 /** moves a MeterSection to a specified position.
3246 * @param ms - the section to be moved
3247 * @param frame - the new position in frames for the meter
3249 * as a meter cannot snap to anything but bars,
3250 * the supplied frame is rounded to the nearest bar, possibly
3251 * leaving the meter position unchanged.
3254 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3258 if (ms->position_lock_style() == AudioTime) {
3261 Glib::Threads::RWLock::WriterLock lm (lock);
3262 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3264 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3265 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3266 recompute_tempi (_metrics);
3271 Glib::Threads::RWLock::WriterLock lm (lock);
3272 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3274 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3275 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3277 if (solve_map_bbt (future_map, copy, bbt)) {
3278 solve_map_bbt (_metrics, ms, bbt);
3279 recompute_tempi (_metrics);
3284 Metrics::const_iterator d = future_map.begin();
3285 while (d != future_map.end()) {
3290 MetricPositionChanged (PropertyChange ()); // Emit Signal
3294 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3297 bool can_solve = false;
3299 Glib::Threads::RWLock::WriterLock lm (lock);
3300 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3302 if (tempo_copy->type() == TempoSection::Constant) {
3303 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3304 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3306 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3307 tempo_copy->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3310 if (ts->clamped()) {
3311 TempoSection* prev = 0;
3312 if ((prev = previous_tempo_section_locked (future_map, tempo_copy)) != 0) {
3313 prev->set_end_note_types_per_minute (tempo_copy->note_types_per_minute());
3317 recompute_tempi (future_map);
3319 if (check_solved (future_map)) {
3320 if (ts->type() == TempoSection::Constant) {
3321 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3322 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3324 ts->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3325 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3328 if (ts->clamped()) {
3329 TempoSection* prev = 0;
3330 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3331 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3335 recompute_map (_metrics);
3340 Metrics::const_iterator d = future_map.begin();
3341 while (d != future_map.end()) {
3346 MetricPositionChanged (PropertyChange ()); // Emit Signal
3353 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame, const double start_qnote, const double end_qnote)
3356 Ts (future prev_t) Tnext
3359 |----------|----------
3366 Glib::Threads::RWLock::WriterLock lm (lock);
3372 TempoSection* ts_copy = copy_metrics_and_point (_metrics, future_map, ts);
3378 /* minimum allowed measurement distance in frames */
3379 framepos_t const min_dframe = 2;
3382 if (ts_copy->clamped()) {
3383 TempoSection* next_t = next_tempo_section_locked (future_map, ts_copy);
3384 TempoSection* prev_to_ts_copy = previous_tempo_section_locked (future_map, ts_copy);
3385 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3386 constant to constant is straightforward, as the tempo prev to ts_copy has constant slope.
3387 */ double contribution = 0.0;
3388 if (next_t && prev_to_ts_copy && prev_to_ts_copy->type() == TempoSection::Ramp) {
3389 contribution = (ts_copy->pulse() - prev_to_ts_copy->pulse()) / (double) (next_t->pulse() - prev_to_ts_copy->pulse());
3391 framepos_t const fr_off = end_frame - frame;
3392 frameoffset_t const ts_copy_frame_contribution = fr_off - (contribution * (double) fr_off);
3394 if (frame > prev_to_ts_copy->frame() + min_dframe && (frame + ts_copy_frame_contribution) > prev_to_ts_copy->frame() + min_dframe) {
3395 new_bpm = ts_copy->note_types_per_minute() * ((start_qnote - (prev_to_ts_copy->pulse() * 4.0))
3396 / (end_qnote - (prev_to_ts_copy->pulse() * 4.0)));
3398 new_bpm = ts_copy->note_types_per_minute();
3401 if (frame > ts_copy->frame() + min_dframe && end_frame > ts_copy->frame() + min_dframe) {
3403 new_bpm = ts_copy->note_types_per_minute() * ((frame - ts_copy->frame())
3404 / (double) (end_frame - ts_copy->frame()));
3406 new_bpm = ts_copy->note_types_per_minute();
3409 new_bpm = min (new_bpm, (double) 1000.0);
3411 /* don't clamp and proceed here.
3412 testing has revealed that this can go negative,
3413 which is an entirely different thing to just being too low.
3416 if (new_bpm < 0.5) {
3420 ts_copy->set_note_types_per_minute (new_bpm);
3422 if (ts_copy->clamped()) {
3423 TempoSection* prev = 0;
3424 if ((prev = previous_tempo_section_locked (future_map, ts_copy)) != 0) {
3425 prev->set_end_note_types_per_minute (ts_copy->note_types_per_minute());
3429 recompute_tempi (future_map);
3430 recompute_meters (future_map);
3432 if (check_solved (future_map)) {
3433 ts->set_note_types_per_minute (new_bpm);
3435 if (ts->clamped()) {
3436 TempoSection* prev = 0;
3437 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3438 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3442 recompute_tempi (_metrics);
3443 recompute_meters (_metrics);
3449 Metrics::const_iterator d = future_map.begin();
3450 while (d != future_map.end()) {
3454 MetricPositionChanged (PropertyChange ()); // Emit Signal
3459 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3462 Ts (future prev_t) Tnext
3465 |----------|----------
3472 Glib::Threads::RWLock::WriterLock lm (lock);
3478 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3484 /* minimum allowed measurement distance in frames */
3485 framepos_t const min_dframe = 2;
3488 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3489 new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->frame() - frame)
3490 / (double) (prev_t->frame() - end_frame));
3492 new_bpm = prev_t->end_note_types_per_minute();
3495 new_bpm = min (new_bpm, (double) 1000.0);
3497 if (new_bpm < 0.5) {
3501 prev_t->set_end_note_types_per_minute (new_bpm);
3503 TempoSection* next = 0;
3504 if ((next = next_tempo_section_locked (future_map, prev_t)) != 0) {
3505 if (next->clamped()) {
3506 next->set_note_types_per_minute (prev_t->end_note_types_per_minute());
3510 recompute_tempi (future_map);
3511 recompute_meters (future_map);
3513 if (check_solved (future_map)) {
3514 ts->set_end_note_types_per_minute (new_bpm);
3516 TempoSection* true_next = 0;
3517 if ((true_next = next_tempo_section_locked (_metrics, ts)) != 0) {
3518 if (true_next->clamped()) {
3519 true_next->set_note_types_per_minute (ts->end_note_types_per_minute());
3523 recompute_tempi (_metrics);
3524 recompute_meters (_metrics);
3530 Metrics::const_iterator d = future_map.begin();
3531 while (d != future_map.end()) {
3536 MetricPositionChanged (PropertyChange ()); // Emit Signal
3540 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
3542 TempoSection* next_t = 0;
3543 TempoSection* next_to_next_t = 0;
3545 bool can_solve = false;
3547 /* minimum allowed measurement distance in frames */
3548 framepos_t const min_dframe = 2;
3551 Glib::Threads::RWLock::WriterLock lm (lock);
3556 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3557 TempoSection* prev_to_prev_t = 0;
3558 const frameoffset_t fr_off = end_frame - frame;
3564 if (tempo_copy->pulse() > 0.0) {
3565 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
3568 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3569 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3570 next_t = static_cast<TempoSection*> (*i);
3579 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3580 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3581 next_to_next_t = static_cast<TempoSection*> (*i);
3586 if (!next_to_next_t) {
3590 double prev_contribution = 0.0;
3592 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3593 prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3596 const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
3599 framepos_t old_tc_minute = tempo_copy->minute();
3600 double old_next_minute = next_t->minute();
3601 double old_next_to_next_minute = next_to_next_t->minute();
3604 double new_next_bpm;
3605 double new_copy_end_bpm;
3607 if (frame > tempo_copy->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > tempo_copy->frame() + min_dframe) {
3608 new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
3609 / (double) (end_frame - tempo_copy->frame()));
3611 new_bpm = tempo_copy->note_types_per_minute();
3614 /* don't clamp and proceed here.
3615 testing has revealed that this can go negative,
3616 which is an entirely different thing to just being too low.
3618 if (new_bpm < 0.5) {
3622 new_bpm = min (new_bpm, (double) 1000.0);
3624 tempo_copy->set_note_types_per_minute (new_bpm);
3625 if (tempo_copy->type() == TempoSection::Constant) {
3626 tempo_copy->set_end_note_types_per_minute (new_bpm);
3629 recompute_tempi (future_map);
3631 if (check_solved (future_map)) {
3637 ts->set_note_types_per_minute (new_bpm);
3638 if (ts->type() == TempoSection::Constant) {
3639 ts->set_end_note_types_per_minute (new_bpm);
3642 recompute_map (_metrics);
3647 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3648 if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) {
3650 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3651 / (double) ((old_next_to_next_minute) - old_next_minute));
3654 new_next_bpm = next_t->note_types_per_minute();
3657 next_t->set_note_types_per_minute (new_next_bpm);
3658 recompute_tempi (future_map);
3660 if (check_solved (future_map)) {
3661 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3662 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3663 next_t = static_cast<TempoSection*> (*i);
3671 next_t->set_note_types_per_minute (new_next_bpm);
3672 recompute_map (_metrics);
3676 double next_frame_ratio = 1.0;
3677 double copy_frame_ratio = 1.0;
3679 if (next_to_next_t) {
3680 next_frame_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute - old_next_minute);
3682 copy_frame_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute));
3685 new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
3686 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
3688 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3690 if (next_t->clamped()) {
3691 next_t->set_note_types_per_minute (new_copy_end_bpm);
3693 next_t->set_note_types_per_minute (new_next_bpm);
3696 recompute_tempi (future_map);
3698 if (check_solved (future_map)) {
3699 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3700 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3701 next_t = static_cast<TempoSection*> (*i);
3710 if (next_t->clamped()) {
3711 next_t->set_note_types_per_minute (new_copy_end_bpm);
3713 next_t->set_note_types_per_minute (new_next_bpm);
3716 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3717 recompute_map (_metrics);
3723 Metrics::const_iterator d = future_map.begin();
3724 while (d != future_map.end()) {
3729 MetricPositionChanged (PropertyChange ()); // Emit Signal
3734 /** Returns the frame position of the musical position zero */
3736 TempoMap::music_origin ()
3738 Glib::Threads::RWLock::ReaderLock lm (lock);
3740 return first_tempo().frame();
3743 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3744 * the supplied frame, possibly returning a negative value.
3746 * @param frame The session frame position.
3747 * @param sub_num The subdivision to use when rounding the beat.
3748 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3749 * Positive integers indicate quarter note (non BBT) divisions.
3750 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3751 * @return The beat position of the supplied frame.
3753 * when working to a musical grid, the use of sub_nom indicates that
3754 * the position should be interpreted musically.
3756 * it effectively snaps to meter bars, meter beats or quarter note divisions
3757 * (as per current gui convention) and returns a musical position independent of frame rate.
3759 * If the supplied frame lies before the first meter, the return will be negative,
3760 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3761 * the continuation of the tempo curve (backwards).
3763 * This function is sensitive to tempo and meter.
3766 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3768 Glib::Threads::RWLock::ReaderLock lm (lock);
3770 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3774 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3776 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3779 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3780 * the supplied frame, possibly returning a negative value.
3782 * @param frame The session frame position.
3783 * @param sub_num The subdivision to use when rounding the quarter note.
3784 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3785 * Positive integers indicate quarter note (non BBT) divisions.
3786 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3787 * @return The quarter note position of the supplied frame.
3789 * When working to a musical grid, the use of sub_nom indicates that
3790 * the frame position should be interpreted musically.
3792 * it effectively snaps to meter bars, meter beats or quarter note divisions
3793 * (as per current gui convention) and returns a musical position independent of frame rate.
3795 * If the supplied frame lies before the first meter, the return will be negative,
3796 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3797 * the continuation of the tempo curve (backwards).
3799 * This function is tempo-sensitive.
3802 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3804 Glib::Threads::RWLock::ReaderLock lm (lock);
3806 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3810 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3812 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3815 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3816 } else if (sub_num == 1) {
3817 /* the gui requested exact musical (BBT) beat */
3818 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3819 } else if (sub_num == -1) {
3821 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3825 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3827 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3829 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3839 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3840 * @param pos the frame position in the tempo map.
3841 * @param bbt the distance in BBT time from pos to calculate.
3842 * @param dir the rounding direction..
3843 * @return the duration in frames between pos and bbt
3846 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3848 Glib::Threads::RWLock::ReaderLock lm (lock);
3850 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3852 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3855 pos_bbt.bars += bbt.bars;
3857 pos_bbt.ticks += bbt.ticks;
3858 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3860 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3863 pos_bbt.beats += bbt.beats;
3864 if ((double) pos_bbt.beats > divisions) {
3866 pos_bbt.beats -= divisions;
3868 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3870 return pos_bbt_frame - pos;
3874 if (pos_bbt.bars <= bbt.bars) {
3877 pos_bbt.bars -= bbt.bars;
3880 if (pos_bbt.ticks < bbt.ticks) {
3881 if (pos_bbt.bars > 1) {
3882 if (pos_bbt.beats == 1) {
3884 pos_bbt.beats = divisions;
3888 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3894 pos_bbt.ticks -= bbt.ticks;
3897 if (pos_bbt.beats <= bbt.beats) {
3898 if (pos_bbt.bars > 1) {
3900 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3905 pos_bbt.beats -= bbt.beats;
3908 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3915 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3917 return round_to_type (fr, dir, Bar);
3921 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3923 return round_to_type (fr, dir, Beat);
3927 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3929 Glib::Threads::RWLock::ReaderLock lm (lock);
3930 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);
3931 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3932 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3934 ticks -= beats * BBT_Time::ticks_per_beat;
3937 /* round to next (or same iff dir == RoundUpMaybe) */
3939 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3941 if (mod == 0 && dir == RoundUpMaybe) {
3942 /* right on the subdivision, which is fine, so do nothing */
3944 } else if (mod == 0) {
3945 /* right on the subdivision, so the difference is just the subdivision ticks */
3946 ticks += ticks_one_subdivisions_worth;
3949 /* not on subdivision, compute distance to next subdivision */
3951 ticks += ticks_one_subdivisions_worth - mod;
3954 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3955 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3956 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3957 // But I'm keeping it around, until we determine there are no terrible consequences.
3958 // if (ticks >= BBT_Time::ticks_per_beat) {
3959 // ticks -= BBT_Time::ticks_per_beat;
3962 } else if (dir < 0) {
3964 /* round to previous (or same iff dir == RoundDownMaybe) */
3966 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3968 if (difference == 0 && dir == RoundDownAlways) {
3969 /* right on the subdivision, but force-rounding down,
3970 so the difference is just the subdivision ticks */
3971 difference = ticks_one_subdivisions_worth;
3974 if (ticks < difference) {
3975 ticks = BBT_Time::ticks_per_beat - ticks;
3977 ticks -= difference;
3981 /* round to nearest */
3984 /* compute the distance to the previous and next subdivision */
3986 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3988 /* closer to the next subdivision, so shift forward */
3990 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3992 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3994 if (ticks > BBT_Time::ticks_per_beat) {
3996 ticks -= BBT_Time::ticks_per_beat;
3997 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
4000 } else if (rem > 0) {
4002 /* closer to previous subdivision, so shift backward */
4006 /* can't go backwards past zero, so ... */
4007 return MusicFrame (0, 0);
4009 /* step back to previous beat */
4011 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4012 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4014 ticks = lrint (ticks - rem);
4015 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4018 /* on the subdivision, do nothing */
4022 MusicFrame ret (0, 0);
4023 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4024 ret.division = sub_num;
4030 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
4032 Glib::Threads::RWLock::ReaderLock lm (lock);
4033 const double minute = minute_at_frame (frame);
4034 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4035 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
4036 MusicFrame ret (0, 0);
4043 /* find bar previous to 'frame' */
4049 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4053 } else if (dir > 0) {
4054 /* find bar following 'frame' */
4059 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4063 /* true rounding: find nearest bar */
4064 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4067 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4069 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4071 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4072 ret.frame = next_ft;
4077 ret.frame = prev_ft;
4089 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
4092 } else if (dir > 0) {
4093 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
4097 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
4104 return MusicFrame (0, 0);
4108 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4109 framepos_t lower, framepos_t upper, uint32_t bar_mod)
4111 Glib::Threads::RWLock::ReaderLock lm (lock);
4112 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
4114 /* although the map handles negative beats, bbt doesn't. */
4119 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
4123 while (pos >= 0 && pos < upper) {
4124 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
4125 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4126 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4127 const double qn = pulse_at_beat_locked (_metrics, cnt) * 4.0;
4129 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, qn));
4133 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
4138 bbt.bars -= bbt.bars % bar_mod;
4142 while (pos >= 0 && pos < upper) {
4143 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4144 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4145 const double qn = pulse_at_bbt_locked (_metrics, bbt) * 4.0;
4147 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, qn));
4148 bbt.bars += bar_mod;
4154 TempoMap::tempo_section_at_frame (framepos_t frame) const
4156 Glib::Threads::RWLock::ReaderLock lm (lock);
4158 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4162 TempoMap::tempo_section_at_frame (framepos_t frame)
4164 Glib::Threads::RWLock::ReaderLock lm (lock);
4166 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4170 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4172 TempoSection* prev = 0;
4176 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4178 if ((*i)->is_tempo()) {
4179 t = static_cast<TempoSection*> (*i);
4183 if (prev && t->minute() > minute) {
4193 abort(); /*NOTREACHED*/
4199 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4201 TempoSection* prev = 0;
4205 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4207 if ((*i)->is_tempo()) {
4208 t = static_cast<TempoSection*> (*i);
4212 if (prev && t->minute() > minute) {
4222 abort(); /*NOTREACHED*/
4228 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4230 TempoSection* prev_t = 0;
4231 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4235 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4236 if ((*i)->is_tempo()) {
4237 t = static_cast<TempoSection*> (*i);
4243 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4254 TempoMap::previous_tempo_section (TempoSection* ts) const
4256 Glib::Threads::RWLock::ReaderLock lm (lock);
4258 return previous_tempo_section_locked (_metrics, ts);
4263 TempoMap::previous_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4269 TempoSection* prev = 0;
4271 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4273 if ((*i)->is_tempo()) {
4274 TempoSection* t = static_cast<TempoSection*> (*i);
4280 if (prev && t == ts) {
4291 abort(); /*NOTREACHED*/
4298 TempoMap::next_tempo_section (TempoSection* ts) const
4300 Glib::Threads::RWLock::ReaderLock lm (lock);
4302 return next_tempo_section_locked (_metrics, ts);
4306 TempoMap::next_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4312 TempoSection* prev = 0;
4314 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4316 if ((*i)->is_tempo()) {
4317 TempoSection* t = static_cast<TempoSection*> (*i);
4323 if (prev && prev == ts) {
4334 abort(); /*NOTREACHED*/
4339 /* don't use this to calculate length (the tempo is only correct for this frame).
4340 do that stuff based on the beat_at_frame and frame_at_beat api
4343 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4345 Glib::Threads::RWLock::ReaderLock lm (lock);
4347 const TempoSection* ts_at = 0;
4348 const TempoSection* ts_after = 0;
4349 Metrics::const_iterator i;
4352 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4354 if ((*i)->is_tempo()) {
4355 t = static_cast<TempoSection*> (*i);
4359 if (ts_at && (*i)->frame() > frame) {
4369 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4371 /* must be treated as constant tempo */
4372 return ts_at->frames_per_quarter_note (_frame_rate);
4376 TempoMap::meter_section_at_frame (framepos_t frame) const
4378 Glib::Threads::RWLock::ReaderLock lm (lock);
4379 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4383 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4385 Metrics::const_iterator i;
4386 MeterSection* prev = 0;
4390 for (i = metrics.begin(); i != metrics.end(); ++i) {
4392 if (!(*i)->is_tempo()) {
4393 m = static_cast<MeterSection*> (*i);
4395 if (prev && (*i)->minute() > minute) {
4405 abort(); /*NOTREACHED*/
4412 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4414 MeterSection* prev_m = 0;
4416 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4418 if (!(*i)->is_tempo()) {
4419 m = static_cast<MeterSection*> (*i);
4420 if (prev_m && m->beat() > beat) {
4431 TempoMap::meter_section_at_beat (double beat) const
4433 Glib::Threads::RWLock::ReaderLock lm (lock);
4434 return meter_section_at_beat_locked (_metrics, beat);
4438 TempoMap::meter_at_frame (framepos_t frame) const
4440 TempoMetric m (metric_at (frame));
4445 TempoMap::fix_legacy_session ()
4447 MeterSection* prev_m = 0;
4448 TempoSection* prev_t = 0;
4449 bool have_initial_t = false;
4451 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4455 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4457 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4460 m->set_minute (0.0);
4461 m->set_position_lock_style (AudioTime);
4466 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4467 + (m->bbt().beats - 1)
4468 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4470 m->set_beat (start);
4471 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4472 + (m->bbt().beats - 1)
4473 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4474 m->set_pulse (start_beat / prev_m->note_divisor());
4477 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4482 /* Ramp type never existed in the era of this tempo section */
4483 t->set_end_note_types_per_minute (t->note_types_per_minute());
4487 t->set_minute (0.0);
4488 t->set_position_lock_style (AudioTime);
4490 have_initial_t = true;
4495 /* some 4.x sessions have no initial (non-movable) tempo. */
4496 if (!have_initial_t) {
4497 prev_t->set_pulse (0.0);
4498 prev_t->set_minute (0.0);
4499 prev_t->set_position_lock_style (AudioTime);
4500 prev_t->set_initial (true);
4501 prev_t->set_locked_to_meter (true);
4502 have_initial_t = true;
4505 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4506 + (t->legacy_bbt().beats - 1)
4507 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4509 t->set_pulse (beat / prev_m->note_divisor());
4511 /* really shouldn't happen but.. */
4512 t->set_pulse (beat / 4.0);
4520 TempoMap::fix_legacy_end_session ()
4522 TempoSection* prev_t = 0;
4524 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4527 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4534 if (prev_t->type() != TempoSection::Constant) {
4535 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4545 TempoMap::get_state ()
4547 Metrics::const_iterator i;
4548 XMLNode *root = new XMLNode ("TempoMap");
4551 Glib::Threads::RWLock::ReaderLock lm (lock);
4552 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4553 root->add_child_nocopy ((*i)->get_state());
4561 TempoMap::set_state (const XMLNode& node, int /*version*/)
4564 Glib::Threads::RWLock::WriterLock lm (lock);
4567 XMLNodeConstIterator niter;
4568 Metrics old_metrics (_metrics);
4571 nlist = node.children();
4573 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4574 XMLNode* child = *niter;
4576 if (child->name() == TempoSection::xml_state_node_name) {
4579 TempoSection* ts = new TempoSection (*child, _frame_rate);
4580 _metrics.push_back (ts);
4583 catch (failed_constructor& err){
4584 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4585 _metrics = old_metrics;
4586 old_metrics.clear();
4590 } else if (child->name() == MeterSection::xml_state_node_name) {
4593 MeterSection* ms = new MeterSection (*child, _frame_rate);
4594 _metrics.push_back (ms);
4597 catch (failed_constructor& err) {
4598 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4599 _metrics = old_metrics;
4600 old_metrics.clear();
4606 /* check for legacy sessions where bbt was the base musical unit for tempo */
4607 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4609 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4610 if (t->legacy_bbt().bars != 0) {
4611 fix_legacy_session();
4615 if (t->end_note_types_per_minute() < 0.0) {
4616 fix_legacy_end_session();
4624 if (niter == nlist.end()) {
4625 MetricSectionSorter cmp;
4626 _metrics.sort (cmp);
4629 /* check for multiple tempo/meters at the same location, which
4630 ardour2 somehow allowed.
4633 Metrics::iterator prev = _metrics.end();
4634 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4635 if (prev != _metrics.end()) {
4637 MeterSection* prev_m;
4639 TempoSection* prev_t;
4640 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4641 if (prev_m->beat() == ms->beat()) {
4642 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
4643 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
4646 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4647 if (prev_t->pulse() == ts->pulse()) {
4648 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4649 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4657 recompute_map (_metrics);
4659 Metrics::const_iterator d = old_metrics.begin();
4660 while (d != old_metrics.end()) {
4664 old_metrics.clear ();
4667 PropertyChanged (PropertyChange ());
4673 TempoMap::dump (std::ostream& o) const
4675 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4676 const MeterSection* m;
4677 const TempoSection* t;
4678 const TempoSection* prev_t = 0;
4680 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4682 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4683 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4684 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4685 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4686 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4688 o << " current start : " << t->note_types_per_minute()
4689 << " current end : " << t->end_note_types_per_minute()
4690 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4691 o << " previous : " << prev_t->note_types_per_minute()
4692 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4693 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4694 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4695 << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4696 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4699 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4700 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4701 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4702 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4705 o << "------" << std::endl;
4709 TempoMap::n_tempos() const
4711 Glib::Threads::RWLock::ReaderLock lm (lock);
4714 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4715 if ((*i)->is_tempo()) {
4724 TempoMap::n_meters() const
4726 Glib::Threads::RWLock::ReaderLock lm (lock);
4729 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4730 if (!(*i)->is_tempo()) {
4739 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4741 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4742 if ((*i)->frame() >= where && !(*i)->initial ()) {
4746 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4747 gui_set_meter_position (ms, (*i)->frame() + amount);
4750 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4751 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4756 PropertyChanged (PropertyChange ());
4760 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4764 std::list<MetricSection*> metric_kill_list;
4766 TempoSection* last_tempo = NULL;
4767 MeterSection* last_meter = NULL;
4768 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4769 bool meter_after = false; // is there a meter marker likewise?
4771 Glib::Threads::RWLock::WriterLock lm (lock);
4772 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4773 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4774 metric_kill_list.push_back(*i);
4775 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4778 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4782 else if ((*i)->frame() >= where) {
4783 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4784 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4785 if ((*i)->frame() == where) {
4786 // marker was immediately after end of range
4787 tempo_after = dynamic_cast<TempoSection*> (*i);
4788 meter_after = dynamic_cast<MeterSection*> (*i);
4794 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4795 if (last_tempo && !tempo_after) {
4796 metric_kill_list.remove(last_tempo);
4797 last_tempo->set_minute (minute_at_frame (where));
4800 if (last_meter && !meter_after) {
4801 metric_kill_list.remove(last_meter);
4802 last_meter->set_minute (minute_at_frame (where));
4806 //remove all the remaining metrics
4807 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4808 _metrics.remove(*i);
4813 recompute_map (_metrics);
4816 PropertyChanged (PropertyChange ());
4820 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4821 * pos can be -ve, if required.
4824 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4826 Glib::Threads::RWLock::ReaderLock lm (lock);
4827 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4829 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4833 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4835 Glib::Threads::RWLock::ReaderLock lm (lock);
4837 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4838 pos_bbt.ticks += op.ticks;
4839 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4841 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4843 pos_bbt.beats += op.beats;
4844 /* the meter in effect will start on the bar */
4845 double divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4846 while (pos_bbt.beats >= divisions_per_bar + 1) {
4848 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4849 pos_bbt.beats -= divisions_per_bar;
4851 pos_bbt.bars += op.bars;
4853 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4856 /** Count the number of beats that are equivalent to distance when going forward,
4860 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4862 Glib::Threads::RWLock::ReaderLock lm (lock);
4864 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4868 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4874 operator<< (std::ostream& o, const Meter& m) {
4875 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4879 operator<< (std::ostream& o, const Tempo& t) {
4880 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4884 operator<< (std::ostream& o, const MetricSection& section) {
4886 o << "MetricSection @ " << section.frame() << ' ';
4888 const TempoSection* ts;
4889 const MeterSection* ms;
4891 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4892 o << *((const Tempo*) ts);
4893 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4894 o << *((const Meter*) ms);