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 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, false);
1035 recompute_map (_metrics);
1038 PropertyChanged (PropertyChange ());
1044 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1046 if (tempo.note_types_per_minute() <= 0.0) {
1047 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1051 bool const locked_to_meter = ts.locked_to_meter();
1052 bool const ts_clamped = ts.clamped();
1053 TempoSection* new_ts = 0;
1056 Glib::Threads::RWLock::WriterLock lm (lock);
1057 TempoSection& first (first_tempo());
1058 if (!ts.initial()) {
1059 if (locked_to_meter) {
1061 /* cannot move a meter-locked tempo section */
1062 *static_cast<Tempo*>(&ts) = tempo;
1063 recompute_map (_metrics);
1066 remove_tempo_locked (ts);
1067 new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter);
1068 new_ts->set_clamped (ts_clamped);
1070 if (new_ts && new_ts->type() == TempoSection::Constant) {
1071 new_ts->set_end_note_types_per_minute (new_ts->note_types_per_minute());
1076 first.set_pulse (0.0);
1077 first.set_minute (minute_at_frame (frame));
1078 first.set_position_lock_style (AudioTime);
1079 first.set_locked_to_meter (true);
1080 first.set_clamped (ts_clamped);
1082 /* cannot move the first tempo section */
1083 *static_cast<Tempo*>(&first) = tempo;
1086 recompute_map (_metrics);
1089 PropertyChanged (PropertyChange ());
1093 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1094 , PositionLockStyle pls, bool recompute, bool locked_to_meter)
1096 TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _frame_rate);
1097 t->set_locked_to_meter (locked_to_meter);
1101 TempoSection* prev_tempo = 0;
1102 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1103 TempoSection* const this_t = dynamic_cast<TempoSection*>(*i);
1105 bool const ipm = t->position_lock_style() == MusicTime;
1106 bool const lm = t->locked_to_meter();
1108 if ((ipm && this_t->pulse() == t->pulse()) || (!ipm && this_t->frame() == t->frame())
1109 || (lm && this_t->pulse() == t->pulse())) {
1110 if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1111 prev_tempo->set_end_note_types_per_minute (t->note_types_per_minute());
1115 prev_tempo = this_t;
1120 if (pls == AudioTime) {
1121 solve_map_minute (_metrics, t, t->minute());
1123 solve_map_pulse (_metrics, t, t->pulse());
1125 recompute_meters (_metrics);
1132 TempoMap::add_meter (const Meter& meter, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1134 MeterSection* m = 0;
1136 Glib::Threads::RWLock::WriterLock lm (lock);
1137 m = add_meter_locked (meter, where, frame, pls, true);
1142 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1147 PropertyChanged (PropertyChange ());
1152 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1155 Glib::Threads::RWLock::WriterLock lm (lock);
1157 if (!ms.initial()) {
1158 remove_meter_locked (ms);
1159 add_meter_locked (meter, where, frame, pls, true);
1161 MeterSection& first (first_meter());
1162 TempoSection& first_t (first_tempo());
1163 /* cannot move the first meter section */
1164 *static_cast<Meter*>(&first) = meter;
1165 first.set_position_lock_style (AudioTime);
1166 first.set_pulse (0.0);
1167 first.set_minute (minute_at_frame (frame));
1168 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1169 first.set_beat (beat);
1170 first_t.set_minute (first.minute());
1171 first_t.set_locked_to_meter (true);
1172 first_t.set_pulse (0.0);
1173 first_t.set_position_lock_style (AudioTime);
1174 recompute_map (_metrics);
1178 PropertyChanged (PropertyChange ());
1182 TempoMap::add_meter_locked (const Meter& meter, const BBT_Time& bbt, framepos_t frame, PositionLockStyle pls, bool recompute)
1184 double const minute_at_bbt = minute_at_bbt_locked (_metrics, bbt);
1185 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_bbt - minute_at_frame (1));
1186 double const pulse = ((bbt.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1187 /* the natural time of the BBT position */
1188 double const time_minutes = minute_at_pulse_locked (_metrics, pulse);
1190 if (pls == AudioTime) {
1191 /* add meter-locked tempo at the natural time in the current map (frame may differ). */
1192 Tempo const tempo_at_time = tempo_at_minute_locked (_metrics, time_minutes);
1193 TempoSection* mlt = add_tempo_locked (tempo_at_time, pulse, time_minutes, AudioTime, true, true);
1199 /* still using natural time for the position, ignoring lock style. */
1200 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);
1202 bool solved = false;
1204 do_insert (new_meter);
1208 if (pls == AudioTime) {
1209 /* now set the audio locked meter's position to frame */
1210 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1211 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1212 fudge frame so that the meter ends up at its BBT position instead.
1215 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1218 solved = solve_map_bbt (_metrics, new_meter, bbt);
1219 /* required due to resetting the pulse of meter-locked tempi above.
1220 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1221 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1223 recompute_map (_metrics);
1227 if (!solved && recompute) {
1228 /* if this has failed to solve, there is little we can do other than to ensure that
1229 the new map is recalculated.
1231 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1232 recompute_map (_metrics);
1239 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1241 Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1244 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1245 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1250 Glib::Threads::RWLock::WriterLock lm (lock);
1251 *((Tempo*) t) = newtempo;
1252 recompute_map (_metrics);
1254 PropertyChanged (PropertyChange ());
1261 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1263 Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1266 TempoSection* first;
1267 Metrics::iterator i;
1269 /* find the TempoSection immediately preceding "where"
1272 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1274 if ((*i)->frame() > where) {
1280 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1293 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1303 Glib::Threads::RWLock::WriterLock lm (lock);
1304 /* cannot move the first tempo section */
1305 *((Tempo*)prev) = newtempo;
1306 recompute_map (_metrics);
1309 PropertyChanged (PropertyChange ());
1313 TempoMap::first_meter () const
1315 const MeterSection *m = 0;
1317 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1318 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1323 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1324 abort(); /*NOTREACHED*/
1329 TempoMap::first_meter ()
1331 MeterSection *m = 0;
1333 /* CALLER MUST HOLD LOCK */
1335 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1336 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1341 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1342 abort(); /*NOTREACHED*/
1347 TempoMap::first_tempo () const
1349 const TempoSection *t = 0;
1351 /* CALLER MUST HOLD LOCK */
1353 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1354 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1364 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1365 abort(); /*NOTREACHED*/
1370 TempoMap::first_tempo ()
1372 TempoSection *t = 0;
1374 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1375 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1385 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1386 abort(); /*NOTREACHED*/
1390 TempoMap::recompute_tempi (Metrics& metrics)
1392 TempoSection* prev_t = 0;
1394 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1397 if ((*i)->is_tempo()) {
1398 t = static_cast<TempoSection*> (*i);
1410 if (t->position_lock_style() == AudioTime) {
1411 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1412 if (!t->locked_to_meter()) {
1413 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1417 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1418 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1426 prev_t->set_c (0.0);
1429 /* tempos must be positioned correctly.
1430 the current approach is to use a meter's bbt time as its base position unit.
1431 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1432 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1435 TempoMap::recompute_meters (Metrics& metrics)
1437 MeterSection* meter = 0;
1438 MeterSection* prev_m = 0;
1440 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1441 if (!(*mi)->is_tempo()) {
1442 meter = static_cast<MeterSection*> (*mi);
1443 if (meter->position_lock_style() == AudioTime) {
1445 pair<double, BBT_Time> b_bbt;
1446 TempoSection* meter_locked_tempo = 0;
1447 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1449 if ((*ii)->is_tempo()) {
1450 t = static_cast<TempoSection*> (*ii);
1451 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1452 meter_locked_tempo = t;
1459 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1460 if (beats + prev_m->beat() != meter->beat()) {
1461 /* reordering caused a bbt change */
1463 beats = meter->beat() - prev_m->beat();
1464 b_bbt = make_pair (beats + prev_m->beat()
1465 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1466 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1468 } else if (!meter->initial()) {
1469 b_bbt = make_pair (meter->beat(), meter->bbt());
1470 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1473 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1475 if (meter_locked_tempo) {
1476 meter_locked_tempo->set_pulse (pulse);
1478 meter->set_beat (b_bbt);
1479 meter->set_pulse (pulse);
1484 pair<double, BBT_Time> b_bbt;
1486 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1487 if (beats + prev_m->beat() != meter->beat()) {
1488 /* reordering caused a bbt change */
1489 b_bbt = make_pair (beats + prev_m->beat()
1490 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1492 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1494 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1496 /* shouldn't happen - the first is audio-locked */
1497 pulse = pulse_at_beat_locked (metrics, meter->beat());
1498 b_bbt = make_pair (meter->beat(), meter->bbt());
1501 meter->set_beat (b_bbt);
1502 meter->set_pulse (pulse);
1503 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1512 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1514 /* CALLER MUST HOLD WRITE LOCK */
1516 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1519 /* silly call from Session::process() during startup
1524 recompute_tempi (metrics);
1525 recompute_meters (metrics);
1529 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1531 Glib::Threads::RWLock::ReaderLock lm (lock);
1532 TempoMetric m (first_meter(), first_tempo());
1535 *last = ++_metrics.begin();
1538 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1539 at something, because we insert the default tempo and meter during
1540 TempoMap construction.
1542 now see if we can find better candidates.
1545 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1547 if ((*i)->frame() > frame) {
1561 /* XX meters only */
1563 TempoMap::metric_at (BBT_Time bbt) const
1565 Glib::Threads::RWLock::ReaderLock lm (lock);
1566 TempoMetric m (first_meter(), first_tempo());
1568 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1569 at something, because we insert the default tempo and meter during
1570 TempoMap construction.
1572 now see if we can find better candidates.
1575 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1577 if (!(*i)->is_tempo()) {
1578 mw = static_cast<MeterSection*> (*i);
1579 BBT_Time section_start (mw->bbt());
1581 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1592 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1593 * @param frame The session frame position.
1594 * @return The beat duration according to the tempo map at the supplied frame.
1596 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1597 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1599 * This function uses both tempo and meter.
1602 TempoMap::beat_at_frame (const framecnt_t& frame) const
1604 Glib::Threads::RWLock::ReaderLock lm (lock);
1606 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1609 /* This function uses both tempo and meter.*/
1611 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1613 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1614 MeterSection* prev_m = 0;
1615 MeterSection* next_m = 0;
1617 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1618 if (!(*i)->is_tempo()) {
1619 if (prev_m && (*i)->minute() > minute) {
1620 next_m = static_cast<MeterSection*> (*i);
1623 prev_m = static_cast<MeterSection*> (*i);
1627 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1629 /* audio locked meters fake their beat */
1630 if (next_m && next_m->beat() < beat) {
1631 return next_m->beat();
1637 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1638 * @param beat The BBT (meter-based) beat.
1639 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1641 * This function uses both tempo and meter.
1644 TempoMap::frame_at_beat (const double& beat) const
1646 Glib::Threads::RWLock::ReaderLock lm (lock);
1648 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1651 /* meter & tempo section based */
1653 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1655 MeterSection* prev_m = 0;
1656 TempoSection* prev_t = 0;
1660 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1661 if (!(*i)->is_tempo()) {
1662 m = static_cast<MeterSection*> (*i);
1663 if (prev_m && m->beat() > beat) {
1673 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1674 if ((*i)->is_tempo()) {
1675 t = static_cast<TempoSection*> (*i);
1681 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1690 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1693 /** Returns a Tempo corresponding to the supplied frame position.
1694 * @param frame The audio frame.
1695 * @return a Tempo according to the tempo map at the supplied frame.
1699 TempoMap::tempo_at_frame (const framepos_t& frame) const
1701 Glib::Threads::RWLock::ReaderLock lm (lock);
1703 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1707 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1709 TempoSection* prev_t = 0;
1713 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1714 if ((*i)->is_tempo()) {
1715 t = static_cast<TempoSection*> (*i);
1719 if ((prev_t) && t->minute() > minute) {
1720 /* t is the section past frame */
1721 return prev_t->tempo_at_minute (minute);
1727 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1730 /** returns the frame at which the supplied tempo occurs, or
1731 * the frame of the last tempo section (search exhausted)
1732 * only the position of the first occurence will be returned
1736 TempoMap::frame_at_tempo (const Tempo& tempo) const
1738 Glib::Threads::RWLock::ReaderLock lm (lock);
1740 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1744 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1746 TempoSection* prev_t = 0;
1747 const double tempo_bpm = tempo.note_types_per_minute();
1749 Metrics::const_iterator i;
1751 for (i = metrics.begin(); i != metrics.end(); ++i) {
1753 if ((*i)->is_tempo()) {
1754 t = static_cast<TempoSection*> (*i);
1762 if (t->note_types_per_minute() == tempo_bpm) {
1767 const double prev_t_bpm = prev_t->note_types_per_minute();
1768 const double prev_t_end_bpm = prev_t->end_note_types_per_minute();
1769 if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm)
1770 || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm)
1771 || (prev_t_end_bpm == tempo_bpm)) {
1773 return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
1780 return prev_t->minute();
1784 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1786 TempoSection* prev_t = 0;
1790 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1791 if ((*i)->is_tempo()) {
1792 t = static_cast<TempoSection*> (*i);
1796 if ((prev_t) && t->pulse() > pulse) {
1797 /* t is the section past frame */
1798 return prev_t->tempo_at_pulse (pulse);
1804 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1808 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1810 TempoSection* prev_t = 0;
1811 const double tempo_bpm = tempo.note_types_per_minute();
1813 Metrics::const_iterator i;
1815 for (i = metrics.begin(); i != metrics.end(); ++i) {
1817 if ((*i)->is_tempo()) {
1818 t = static_cast<TempoSection*> (*i);
1824 const double t_bpm = t->note_types_per_minute();
1826 if (t_bpm == tempo_bpm) {
1831 const double prev_t_bpm = prev_t->note_types_per_minute();
1833 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1834 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1841 return prev_t->pulse();
1844 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1845 * @param qn the position in quarter note beats.
1846 * @return the Tempo at the supplied quarter-note.
1849 TempoMap::tempo_at_quarter_note (const double& qn) const
1851 Glib::Threads::RWLock::ReaderLock lm (lock);
1853 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1856 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1857 * @param tempo the tempo.
1858 * @return the position in quarter-note beats where the map bpm
1859 * is equal to that of the Tempo. currently ignores note_type.
1862 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1864 Glib::Threads::RWLock::ReaderLock lm (lock);
1866 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;
1869 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1870 * @param metrics the list of metric sections used to calculate the pulse.
1871 * @param beat The BBT (meter-based) beat.
1872 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1874 * a pulse or whole note is the base musical position of a MetricSection.
1875 * it is equivalent to four quarter notes.
1879 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1881 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1883 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1886 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1887 * @param metrics the list of metric sections used to calculate the beat.
1888 * @param pulse the whole-note pulse.
1889 * @return the meter-based beat at the supplied whole-note pulse.
1891 * a pulse or whole note is the base musical position of a MetricSection.
1892 * it is equivalent to four quarter notes.
1895 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1897 MeterSection* prev_m = 0;
1899 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1901 if (!(*i)->is_tempo()) {
1902 m = static_cast<MeterSection*> (*i);
1903 if (prev_m && m->pulse() > pulse) {
1911 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1915 /* tempo section based */
1917 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1919 /* HOLD (at least) THE READER LOCK */
1920 TempoSection* prev_t = 0;
1922 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1924 if ((*i)->is_tempo()) {
1925 t = static_cast<TempoSection*> (*i);
1929 if (prev_t && t->minute() > minute) {
1930 /*the previous ts is the one containing the frame */
1931 const double ret = prev_t->pulse_at_minute (minute);
1932 /* audio locked section in new meter*/
1933 if (t->pulse() < ret) {
1942 /* treated as constant for this ts */
1943 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1945 return pulses_in_section + prev_t->pulse();
1948 /* tempo section based */
1950 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1952 /* HOLD THE READER LOCK */
1954 const TempoSection* prev_t = 0;
1956 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1959 if ((*i)->is_tempo()) {
1960 t = static_cast<TempoSection*> (*i);
1964 if (prev_t && t->pulse() > pulse) {
1965 return prev_t->minute_at_pulse (pulse);
1971 /* must be treated as constant, irrespective of _type */
1972 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1974 return dtime + prev_t->minute();
1977 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1978 * @param bbt The BBT time (meter-based).
1979 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1983 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1985 Glib::Threads::RWLock::ReaderLock lm (lock);
1986 return beat_at_bbt_locked (_metrics, bbt);
1991 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1993 /* CALLER HOLDS READ LOCK */
1995 MeterSection* prev_m = 0;
1997 /* because audio-locked meters have 'fake' integral beats,
1998 there is no pulse offset here.
2002 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2003 if (!(*i)->is_tempo()) {
2004 m = static_cast<MeterSection*> (*i);
2006 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2007 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2015 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2016 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2017 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2022 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2023 * @param beat The BBT (meter-based) beat.
2024 * @return The BBT time (meter-based) at the supplied meter-based beat.
2028 TempoMap::bbt_at_beat (const double& beat)
2030 Glib::Threads::RWLock::ReaderLock lm (lock);
2031 return bbt_at_beat_locked (_metrics, beat);
2035 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2037 /* CALLER HOLDS READ LOCK */
2038 MeterSection* prev_m = 0;
2039 const double beats = max (0.0, b);
2041 MeterSection* m = 0;
2043 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2044 if (!(*i)->is_tempo()) {
2045 m = static_cast<MeterSection*> (*i);
2047 if (m->beat() > beats) {
2048 /* this is the meter after the one our beat is on*/
2058 const double beats_in_ms = beats - prev_m->beat();
2059 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2060 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2061 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2062 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2066 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2067 ret.beats = (uint32_t) floor (remaining_beats);
2068 ret.bars = total_bars;
2070 /* 0 0 0 to 1 1 0 - based mapping*/
2074 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2076 ret.ticks -= BBT_Time::ticks_per_beat;
2079 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2087 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2088 * @param bbt The BBT time (meter-based).
2089 * @return the quarter note beat at the supplied BBT time
2091 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2093 * while the input uses meter, the output does not.
2096 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2098 Glib::Threads::RWLock::ReaderLock lm (lock);
2100 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2104 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2106 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2109 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2112 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2116 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2118 /* CALLER HOLDS READ LOCK */
2120 MeterSection* prev_m = 0;
2122 /* because audio-locked meters have 'fake' integral beats,
2123 there is no pulse offset here.
2127 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2128 if (!(*i)->is_tempo()) {
2129 m = static_cast<MeterSection*> (*i);
2131 if (m->bbt().bars > bbt.bars) {
2139 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2140 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2141 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2146 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2147 * @param qn the quarter-note beat.
2148 * @return The BBT time (meter-based) at the supplied meter-based beat.
2150 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2154 TempoMap::bbt_at_quarter_note (const double& qn)
2156 Glib::Threads::RWLock::ReaderLock lm (lock);
2158 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2161 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2162 * @param metrics The list of metric sections used to determine the result.
2163 * @param pulse The whole-note pulse.
2164 * @return The BBT time at the supplied whole-note pulse.
2166 * a pulse or whole note is the basic musical position of a MetricSection.
2167 * it is equivalent to four quarter notes.
2168 * while the output uses meter, the input does not.
2171 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2173 MeterSection* prev_m = 0;
2175 MeterSection* m = 0;
2177 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2179 if (!(*i)->is_tempo()) {
2180 m = static_cast<MeterSection*> (*i);
2183 double const pulses_to_m = m->pulse() - prev_m->pulse();
2184 if (prev_m->pulse() + pulses_to_m > pulse) {
2185 /* this is the meter after the one our beat is on*/
2196 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2197 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2198 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2199 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2200 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2204 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2205 ret.beats = (uint32_t) floor (remaining_beats);
2206 ret.bars = total_bars;
2208 /* 0 0 0 to 1 1 0 mapping*/
2212 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2214 ret.ticks -= BBT_Time::ticks_per_beat;
2217 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2225 /** Returns the BBT time corresponding to the supplied frame position.
2226 * @param frame the position in audio samples.
2227 * @return the BBT time at the frame position .
2231 TempoMap::bbt_at_frame (framepos_t frame)
2239 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2244 const double minute = minute_at_frame (frame);
2246 Glib::Threads::RWLock::ReaderLock lm (lock);
2248 return bbt_at_minute_locked (_metrics, minute);
2252 TempoMap::bbt_at_frame_rt (framepos_t frame)
2254 const double minute = minute_at_frame (frame);
2256 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2259 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2262 return bbt_at_minute_locked (_metrics, minute);
2266 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2276 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2277 MeterSection* prev_m = 0;
2278 MeterSection* next_m = 0;
2282 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2283 if (!(*i)->is_tempo()) {
2284 m = static_cast<MeterSection*> (*i);
2285 if (prev_m && m->minute() > minute) {
2293 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2295 /* handle frame before first meter */
2296 if (minute < prev_m->minute()) {
2299 /* audio locked meters fake their beat */
2300 if (next_m && next_m->beat() < beat) {
2301 beat = next_m->beat();
2304 beat = max (0.0, beat);
2306 const double beats_in_ms = beat - prev_m->beat();
2307 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2308 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2309 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2310 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2314 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2315 ret.beats = (uint32_t) floor (remaining_beats);
2316 ret.bars = total_bars;
2318 /* 0 0 0 to 1 1 0 - based mapping*/
2322 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2324 ret.ticks -= BBT_Time::ticks_per_beat;
2327 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2335 /** Returns the frame position corresponding to the supplied BBT time.
2336 * @param bbt the position in BBT time.
2337 * @return the frame position at bbt.
2341 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2345 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2350 if (bbt.beats < 1) {
2351 throw std::logic_error ("beats are counted from one");
2356 Glib::Threads::RWLock::ReaderLock lm (lock);
2357 minute = minute_at_bbt_locked (_metrics, bbt);
2360 return frame_at_minute (minute);
2363 /* meter & tempo section based */
2365 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2367 /* HOLD THE READER LOCK */
2369 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2374 * Returns the quarter-note beat position corresponding to the supplied frame.
2376 * @param frame the position in frames.
2377 * @return The quarter-note position of the supplied frame. Ignores meter.
2381 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2383 const double minute = minute_at_frame (frame);
2385 Glib::Threads::RWLock::ReaderLock lm (lock);
2387 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2391 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2393 const double minute = minute_at_frame (frame);
2395 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2398 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2401 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2405 * Returns the frame position corresponding to the supplied quarter-note beat.
2407 * @param quarter_note the quarter-note position.
2408 * @return the frame position of the supplied quarter-note. Ignores meter.
2413 TempoMap::frame_at_quarter_note (const double quarter_note) const
2417 Glib::Threads::RWLock::ReaderLock lm (lock);
2419 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2422 return frame_at_minute (minute);
2425 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2426 * @param beat The BBT (meter-based) beat.
2427 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2429 * a quarter-note may be compared with and assigned to Evoral::Beats.
2433 TempoMap::quarter_note_at_beat (const double beat) const
2435 Glib::Threads::RWLock::ReaderLock lm (lock);
2437 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2440 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2441 * @param quarter_note The position in quarter-note beats.
2442 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2444 * a quarter-note is the musical unit of Evoral::Beats.
2448 TempoMap::beat_at_quarter_note (const double quarter_note) const
2450 Glib::Threads::RWLock::ReaderLock lm (lock);
2452 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2455 /** Returns the duration in frames between two supplied quarter-note beat positions.
2456 * @param start the first position in quarter-note beats.
2457 * @param end the end position in quarter-note beats.
2458 * @return the frame distance ober the quarter-note beats duration.
2460 * use this rather than e.g.
2461 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2462 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2466 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2471 Glib::Threads::RWLock::ReaderLock lm (lock);
2472 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2475 return frame_at_minute (minutes);
2479 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2482 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2486 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2488 Glib::Threads::RWLock::ReaderLock lm (lock);
2490 return quarter_notes_between_frames_locked (_metrics, start, end);
2494 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2496 const TempoSection* prev_t = 0;
2498 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2501 if ((*i)->is_tempo()) {
2502 t = static_cast<TempoSection*> (*i);
2506 if (prev_t && t->frame() > start) {
2513 const double start_qn = prev_t->pulse_at_frame (start);
2515 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2518 if ((*i)->is_tempo()) {
2519 t = static_cast<TempoSection*> (*i);
2523 if (prev_t && t->frame() > end) {
2529 const double end_qn = prev_t->pulse_at_frame (end);
2531 return (end_qn - start_qn) * 4.0;
2535 TempoMap::check_solved (const Metrics& metrics) const
2537 TempoSection* prev_t = 0;
2538 MeterSection* prev_m = 0;
2540 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2543 if ((*i)->is_tempo()) {
2544 t = static_cast<TempoSection*> (*i);
2549 /* check ordering */
2550 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2554 /* precision check ensures tempo and frames align.*/
2555 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2556 if (!t->locked_to_meter()) {
2561 /* gradient limit - who knows what it should be?
2562 things are also ok (if a little chaotic) without this
2564 if (fabs (prev_t->c()) > 1000.0) {
2565 //std::cout << "c : " << prev_t->c() << std::endl;
2572 if (!(*i)->is_tempo()) {
2573 m = static_cast<MeterSection*> (*i);
2574 if (prev_m && m->position_lock_style() == AudioTime) {
2575 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2576 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2577 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2579 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2593 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2595 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2597 if ((*i)->is_tempo()) {
2598 t = static_cast<TempoSection*> (*i);
2599 if (t->locked_to_meter()) {
2600 t->set_active (true);
2601 } else if (t->position_lock_style() == AudioTime) {
2602 if (t->frame() < frame) {
2603 t->set_active (false);
2604 t->set_pulse (-1.0);
2605 } else if (t->frame() > frame) {
2606 t->set_active (true);
2607 } else if (t->frame() == frame) {
2617 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2619 TempoSection* prev_t = 0;
2620 TempoSection* section_prev = 0;
2621 double first_m_minute = 0.0;
2622 const bool sml = section->locked_to_meter();
2624 /* can't move a tempo before the first meter */
2625 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2627 if (!(*i)->is_tempo()) {
2628 m = static_cast<MeterSection*> (*i);
2630 first_m_minute = m->minute();
2635 if (!section->initial() && minute <= first_m_minute) {
2639 section->set_active (true);
2640 section->set_minute (minute);
2642 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2644 if ((*i)->is_tempo()) {
2645 t = static_cast<TempoSection*> (*i);
2657 if (t->frame() == frame_at_minute (minute)) {
2661 const bool tlm = t->position_lock_style() == MusicTime;
2663 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2664 section_prev = prev_t;
2666 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2667 if (!section->locked_to_meter()) {
2668 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2673 if (t->position_lock_style() == MusicTime) {
2674 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2675 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2677 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2678 if (!t->locked_to_meter()) {
2679 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2687 MetricSectionFrameSorter fcmp;
2688 imaginary.sort (fcmp);
2690 recompute_tempi (imaginary);
2692 if (check_solved (imaginary)) {
2700 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2702 TempoSection* prev_t = 0;
2703 TempoSection* section_prev = 0;
2705 section->set_pulse (pulse);
2707 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2709 if ((*i)->is_tempo()) {
2710 t = static_cast<TempoSection*> (*i);
2721 section_prev = prev_t;
2725 if (t->position_lock_style() == MusicTime) {
2726 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2727 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2729 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2730 if (!t->locked_to_meter()) {
2731 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2740 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2741 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2744 MetricSectionSorter cmp;
2745 imaginary.sort (cmp);
2747 recompute_tempi (imaginary);
2749 * XX need a restriction here, but only for this case,
2750 * as audio locked tempos don't interact in the same way.
2752 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2754 * |50 bpm |250 bpm |60 bpm
2755 * drag 250 to the pulse after 60->
2756 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2758 if (check_solved (imaginary)) {
2766 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2768 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2769 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2770 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2774 if (section->initial()) {
2775 /* lock the first tempo to our first meter */
2776 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2781 TempoSection* meter_locked_tempo = 0;
2783 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2785 if ((*ii)->is_tempo()) {
2786 t = static_cast<TempoSection*> (*ii);
2787 if (t->locked_to_meter() && t->frame() == section->frame()) {
2788 meter_locked_tempo = t;
2794 if (!meter_locked_tempo) {
2798 MeterSection* prev_m = 0;
2800 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2801 bool solved = false;
2803 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2805 if (!(*i)->is_tempo()) {
2806 m = static_cast<MeterSection*> (*i);
2808 if (prev_m && !section->initial()) {
2809 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2810 if (beats + prev_m->beat() < section->beat()) {
2811 /* set the section pulse according to its musical position,
2812 * as an earlier time than this has been requested.
2814 const double new_pulse = ((section->beat() - prev_m->beat())
2815 / prev_m->note_divisor()) + prev_m->pulse();
2817 tempo_copy->set_position_lock_style (MusicTime);
2818 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2819 meter_locked_tempo->set_position_lock_style (MusicTime);
2820 section->set_position_lock_style (MusicTime);
2821 section->set_pulse (new_pulse);
2822 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2823 meter_locked_tempo->set_position_lock_style (AudioTime);
2824 section->set_position_lock_style (AudioTime);
2825 section->set_minute (meter_locked_tempo->minute());
2831 Metrics::const_iterator d = future_map.begin();
2832 while (d != future_map.end()) {
2841 /* all is ok. set section's locked tempo if allowed.
2842 possibly disallow if there is an adjacent audio-locked tempo.
2843 XX this check could possibly go. its never actually happened here.
2845 MeterSection* meter_copy = const_cast<MeterSection*>
2846 (&meter_section_at_minute_locked (future_map, section->minute()));
2848 meter_copy->set_minute (minute);
2850 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2851 section->set_minute (minute);
2852 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2853 / prev_m->note_divisor()) + prev_m->pulse());
2854 solve_map_minute (imaginary, meter_locked_tempo, minute);
2859 Metrics::const_iterator d = future_map.begin();
2860 while (d != future_map.end()) {
2870 /* initial (first meter atm) */
2872 tempo_copy->set_minute (minute);
2873 tempo_copy->set_pulse (0.0);
2875 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2876 section->set_minute (minute);
2877 meter_locked_tempo->set_minute (minute);
2878 meter_locked_tempo->set_pulse (0.0);
2879 solve_map_minute (imaginary, meter_locked_tempo, minute);
2884 Metrics::const_iterator d = future_map.begin();
2885 while (d != future_map.end()) {
2894 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2895 section->set_beat (b_bbt);
2896 section->set_pulse (0.0);
2906 MetricSectionFrameSorter fcmp;
2907 imaginary.sort (fcmp);
2909 recompute_meters (imaginary);
2915 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2917 /* disallow setting section to an existing meter's bbt */
2918 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2920 if (!(*i)->is_tempo()) {
2921 m = static_cast<MeterSection*> (*i);
2922 if (m != section && m->bbt().bars == when.bars) {
2928 MeterSection* prev_m = 0;
2929 MeterSection* section_prev = 0;
2931 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2933 if (!(*i)->is_tempo()) {
2934 m = static_cast<MeterSection*> (*i);
2940 pair<double, BBT_Time> b_bbt;
2941 double new_pulse = 0.0;
2943 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2944 section_prev = prev_m;
2946 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2947 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2948 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2950 section->set_beat (b_bbt);
2951 section->set_pulse (pulse);
2952 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2956 if (m->position_lock_style() == AudioTime) {
2957 TempoSection* meter_locked_tempo = 0;
2959 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2961 if ((*ii)->is_tempo()) {
2962 t = static_cast<TempoSection*> (*ii);
2963 if (t->locked_to_meter() && t->frame() == m->frame()) {
2964 meter_locked_tempo = t;
2970 if (!meter_locked_tempo) {
2975 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2977 if (beats + prev_m->beat() != m->beat()) {
2978 /* tempo/ meter change caused a change in beat (bar). */
2980 /* the user has requested that the previous section of music overlaps this one.
2981 we have no choice but to change the bar number here, as being locked to audio means
2982 we must stay where we are on the timeline.
2984 beats = m->beat() - prev_m->beat();
2985 b_bbt = make_pair (beats + prev_m->beat()
2986 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2987 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2989 } else if (!m->initial()) {
2990 b_bbt = make_pair (m->beat(), m->bbt());
2991 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2994 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2997 meter_locked_tempo->set_pulse (new_pulse);
2998 m->set_beat (b_bbt);
2999 m->set_pulse (new_pulse);
3003 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3004 if (beats + prev_m->beat() != m->beat()) {
3005 /* tempo/ meter change caused a change in beat (bar). */
3006 b_bbt = make_pair (beats + prev_m->beat()
3007 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3009 b_bbt = make_pair (beats + prev_m->beat()
3012 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3013 m->set_beat (b_bbt);
3014 m->set_pulse (new_pulse);
3015 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3022 if (!section_prev) {
3024 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3025 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3026 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3028 section->set_beat (b_bbt);
3029 section->set_pulse (pulse);
3030 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3033 MetricSectionSorter cmp;
3034 imaginary.sort (cmp);
3036 recompute_meters (imaginary);
3041 /** places a copy of _metrics into copy and returns a pointer
3042 * to section's equivalent in copy.
3045 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section) const
3047 TempoSection* ret = 0;
3049 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3050 if ((*i)->is_tempo()) {
3051 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3053 ret = new TempoSection (*t);
3054 copy.push_back (ret);
3058 TempoSection* cp = new TempoSection (*t);
3059 copy.push_back (cp);
3061 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3062 MeterSection* cp = new MeterSection (*m);
3063 copy.push_back (cp);
3071 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section) const
3073 MeterSection* ret = 0;
3075 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3076 if ((*i)->is_tempo()) {
3077 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3078 TempoSection* cp = new TempoSection (*t);
3079 copy.push_back (cp);
3081 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3083 ret = new MeterSection (*m);
3084 copy.push_back (ret);
3087 MeterSection* cp = new MeterSection (*m);
3088 copy.push_back (cp);
3095 /** answers the question "is this a valid beat position for this tempo section?".
3096 * it returns true if the tempo section can be moved to the requested bbt position,
3097 * leaving the tempo map in a solved state.
3098 * @param ts the tempo section to be moved
3099 * @param bbt the requested new position for the tempo section
3100 * @return true if the tempo section can be moved to the position, otherwise false.
3103 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3106 TempoSection* tempo_copy = 0;
3109 Glib::Threads::RWLock::ReaderLock lm (lock);
3110 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3116 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3118 Metrics::const_iterator d = copy.begin();
3119 while (d != copy.end()) {
3128 * 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,
3129 * taking any possible reordering as a consequence of this into account.
3130 * @param section - the section to be altered
3131 * @param bbt - the BBT time where the altered tempo will fall
3132 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3134 pair<double, framepos_t>
3135 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3138 pair<double, framepos_t> ret = make_pair (0.0, 0);
3140 Glib::Threads::RWLock::ReaderLock lm (lock);
3142 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3144 const double beat = beat_at_bbt_locked (future_map, bbt);
3146 if (section->position_lock_style() == AudioTime) {
3147 tempo_copy->set_position_lock_style (MusicTime);
3150 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3151 ret.first = tempo_copy->pulse();
3152 ret.second = tempo_copy->frame();
3154 ret.first = section->pulse();
3155 ret.second = section->frame();
3158 Metrics::const_iterator d = future_map.begin();
3159 while (d != future_map.end()) {
3166 /** moves a TempoSection to a specified position.
3167 * @param ts - the section to be moved
3168 * @param frame - the new position in frames for the tempo
3169 * @param sub_num - the snap division to use if using musical time.
3171 * if sub_num is non-zero, the frame position is used to calculate an exact
3174 * -1 | snap to bars (meter-based)
3175 * 0 | no snap - use audio frame for musical position
3176 * 1 | snap to meter-based (BBT) beat
3177 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3179 * this follows the snap convention in the gui.
3180 * if sub_num is zero, the musical position will be taken from the supplied frame.
3183 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3187 if (ts->position_lock_style() == MusicTime) {
3189 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3190 Glib::Threads::RWLock::WriterLock lm (lock);
3191 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3193 tempo_copy->set_position_lock_style (AudioTime);
3195 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3196 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3197 const double pulse = pulse_at_beat_locked (future_map, beat);
3199 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3200 solve_map_pulse (_metrics, ts, pulse);
3201 recompute_meters (_metrics);
3209 Glib::Threads::RWLock::WriterLock lm (lock);
3210 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3214 /* We're moving the object that defines the grid while snapping to it...
3215 * Placing the ts at the beat corresponding to the requested frame may shift the
3216 * grid in such a way that the mouse is left hovering over a completerly different division,
3217 * causing jittering when the mouse next moves (esp. large tempo deltas).
3218 * We fudge around this by doing this in the musical domain and then swapping back for the recompute.
3220 const double qn = exact_qn_at_frame_locked (_metrics, frame, sub_num);
3221 tempo_copy->set_position_lock_style (MusicTime);
3222 if (solve_map_pulse (future_map, tempo_copy, qn / 4.0)) {
3223 ts->set_position_lock_style (MusicTime);
3224 solve_map_pulse (_metrics, ts, qn / 4.0);
3225 ts->set_position_lock_style (AudioTime);
3226 recompute_meters (_metrics);
3229 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3230 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3231 recompute_meters (_metrics);
3237 Metrics::const_iterator d = future_map.begin();
3238 while (d != future_map.end()) {
3243 MetricPositionChanged (PropertyChange ()); // Emit Signal
3246 /** moves a MeterSection to a specified position.
3247 * @param ms - the section to be moved
3248 * @param frame - the new position in frames for the meter
3250 * as a meter cannot snap to anything but bars,
3251 * the supplied frame is rounded to the nearest bar, possibly
3252 * leaving the meter position unchanged.
3255 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3259 if (ms->position_lock_style() == AudioTime) {
3262 Glib::Threads::RWLock::WriterLock lm (lock);
3263 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3265 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3266 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3267 recompute_tempi (_metrics);
3272 Glib::Threads::RWLock::WriterLock lm (lock);
3273 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3275 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3276 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3278 if (solve_map_bbt (future_map, copy, bbt)) {
3279 solve_map_bbt (_metrics, ms, bbt);
3280 recompute_tempi (_metrics);
3285 Metrics::const_iterator d = future_map.begin();
3286 while (d != future_map.end()) {
3291 MetricPositionChanged (PropertyChange ()); // Emit Signal
3295 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3298 bool can_solve = false;
3300 Glib::Threads::RWLock::WriterLock lm (lock);
3301 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3303 if (tempo_copy->type() == TempoSection::Constant) {
3304 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3305 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3307 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3308 tempo_copy->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3311 if (ts->clamped()) {
3312 TempoSection* prev = 0;
3313 if ((prev = previous_tempo_section_locked (future_map, tempo_copy)) != 0) {
3314 prev->set_end_note_types_per_minute (tempo_copy->note_types_per_minute());
3318 recompute_tempi (future_map);
3320 if (check_solved (future_map)) {
3321 if (ts->type() == TempoSection::Constant) {
3322 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3323 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3325 ts->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3326 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3329 if (ts->clamped()) {
3330 TempoSection* prev = 0;
3331 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3332 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3336 recompute_map (_metrics);
3341 Metrics::const_iterator d = future_map.begin();
3342 while (d != future_map.end()) {
3347 MetricPositionChanged (PropertyChange ()); // Emit Signal
3354 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame, const double start_qnote, const double end_qnote)
3357 Ts (future prev_t) Tnext
3360 |----------|----------
3367 Glib::Threads::RWLock::WriterLock lm (lock);
3373 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3379 /* minimum allowed measurement distance in frames */
3380 framepos_t const min_dframe = 2;
3383 if (prev_t->clamped()) {
3384 TempoSection* next_t = next_tempo_section_locked (future_map, prev_t);
3385 TempoSection* prev_to_prev_t = previous_tempo_section_locked (future_map, prev_t);
3386 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3387 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3389 double contribution = 0.0;
3390 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3391 contribution = (prev_t->pulse() - prev_to_prev_t->pulse()) / (double) (next_t->pulse() - prev_to_prev_t->pulse());
3393 framepos_t const fr_off = end_frame - frame;
3394 frameoffset_t const prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3396 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3397 new_bpm = prev_t->note_types_per_minute() * ((start_qnote - (prev_to_prev_t->pulse() * 4.0))
3398 / (end_qnote - (prev_to_prev_t->pulse() * 4.0)));
3400 new_bpm = prev_t->note_types_per_minute();
3403 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3405 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
3406 / (double) (end_frame - prev_t->frame()));
3408 new_bpm = prev_t->note_types_per_minute();
3411 new_bpm = min (new_bpm, (double) 1000.0);
3413 /* don't clamp and proceed here.
3414 testing has revealed that this can go negative,
3415 which is an entirely different thing to just being too low.
3418 if (new_bpm < 0.5) {
3422 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3423 prev_t->set_note_types_per_minute (new_bpm);
3425 prev_t->set_end_note_types_per_minute (new_bpm);
3426 prev_t->set_note_types_per_minute (new_bpm);
3429 if (prev_t->clamped()) {
3430 TempoSection* prev = 0;
3431 if ((prev = previous_tempo_section_locked (future_map, prev_t)) != 0) {
3432 prev->set_end_note_types_per_minute (prev_t->note_types_per_minute());
3436 recompute_tempi (future_map);
3437 recompute_meters (future_map);
3439 if (check_solved (future_map)) {
3440 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3441 ts->set_note_types_per_minute (new_bpm);
3443 ts->set_end_note_types_per_minute (new_bpm);
3444 ts->set_note_types_per_minute (new_bpm);
3446 if (ts->clamped()) {
3447 TempoSection* prev = 0;
3448 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3449 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3452 recompute_tempi (_metrics);
3453 recompute_meters (_metrics);
3459 Metrics::const_iterator d = future_map.begin();
3460 while (d != future_map.end()) {
3464 MetricPositionChanged (PropertyChange ()); // Emit Signal
3469 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3472 Ts (future prev_t) Tnext
3475 |----------|----------
3482 Glib::Threads::RWLock::WriterLock lm (lock);
3488 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3494 /* minimum allowed measurement distance in frames */
3495 framepos_t const min_dframe = 2;
3498 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3499 new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->frame() - frame)
3500 / (double) (prev_t->frame() - end_frame));
3502 new_bpm = prev_t->end_note_types_per_minute();
3505 new_bpm = min (new_bpm, (double) 1000.0);
3507 if (new_bpm < 0.5) {
3511 prev_t->set_end_note_types_per_minute (new_bpm);
3513 TempoSection* next = 0;
3514 if ((next = next_tempo_section_locked (future_map, prev_t)) != 0) {
3515 if (next->clamped()) {
3516 next->set_note_types_per_minute (prev_t->end_note_types_per_minute());
3520 recompute_tempi (future_map);
3521 recompute_meters (future_map);
3523 if (check_solved (future_map)) {
3524 ts->set_end_note_types_per_minute (new_bpm);
3526 TempoSection* true_next = 0;
3527 if ((true_next = next_tempo_section_locked (_metrics, ts)) != 0) {
3528 if (true_next->clamped()) {
3529 true_next->set_note_types_per_minute (ts->end_note_types_per_minute());
3533 recompute_tempi (_metrics);
3534 recompute_meters (_metrics);
3540 Metrics::const_iterator d = future_map.begin();
3541 while (d != future_map.end()) {
3546 MetricPositionChanged (PropertyChange ()); // Emit Signal
3550 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
3552 TempoSection* next_t = 0;
3553 TempoSection* next_to_next_t = 0;
3555 bool can_solve = false;
3557 /* minimum allowed measurement distance in frames */
3558 framepos_t const min_dframe = 2;
3561 Glib::Threads::RWLock::WriterLock lm (lock);
3566 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3567 TempoSection* prev_to_prev_t = 0;
3568 const frameoffset_t fr_off = end_frame - frame;
3574 if (tempo_copy->pulse() > 0.0) {
3575 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
3578 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3579 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3580 next_t = static_cast<TempoSection*> (*i);
3589 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3590 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3591 next_to_next_t = static_cast<TempoSection*> (*i);
3596 if (!next_to_next_t) {
3600 double prev_contribution = 0.0;
3602 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3603 prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3606 const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
3609 framepos_t old_tc_minute = tempo_copy->minute();
3610 double old_next_minute = next_t->minute();
3611 double old_next_to_next_minute = next_to_next_t->minute();
3614 double new_next_bpm;
3615 double new_copy_end_bpm;
3617 if (frame > tempo_copy->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > tempo_copy->frame() + min_dframe) {
3618 new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
3619 / (double) (end_frame - tempo_copy->frame()));
3621 new_bpm = tempo_copy->note_types_per_minute();
3624 /* don't clamp and proceed here.
3625 testing has revealed that this can go negative,
3626 which is an entirely different thing to just being too low.
3628 if (new_bpm < 0.5) {
3632 new_bpm = min (new_bpm, (double) 1000.0);
3634 tempo_copy->set_note_types_per_minute (new_bpm);
3635 if (tempo_copy->type() == TempoSection::Constant) {
3636 tempo_copy->set_end_note_types_per_minute (new_bpm);
3639 recompute_tempi (future_map);
3641 if (check_solved (future_map)) {
3647 ts->set_note_types_per_minute (new_bpm);
3648 if (ts->type() == TempoSection::Constant) {
3649 ts->set_end_note_types_per_minute (new_bpm);
3652 recompute_map (_metrics);
3657 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3658 if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) {
3660 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3661 / (double) ((old_next_to_next_minute) - old_next_minute));
3664 new_next_bpm = next_t->note_types_per_minute();
3667 next_t->set_note_types_per_minute (new_next_bpm);
3668 recompute_tempi (future_map);
3670 if (check_solved (future_map)) {
3671 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3672 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3673 next_t = static_cast<TempoSection*> (*i);
3681 next_t->set_note_types_per_minute (new_next_bpm);
3682 recompute_map (_metrics);
3686 double next_frame_ratio = 1.0;
3687 double copy_frame_ratio = 1.0;
3689 if (next_to_next_t) {
3690 next_frame_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute - old_next_minute);
3692 copy_frame_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute));
3695 new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
3696 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
3698 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3700 if (next_t->clamped()) {
3701 next_t->set_note_types_per_minute (new_copy_end_bpm);
3703 next_t->set_note_types_per_minute (new_next_bpm);
3706 recompute_tempi (future_map);
3708 if (check_solved (future_map)) {
3709 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3710 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3711 next_t = static_cast<TempoSection*> (*i);
3720 if (next_t->clamped()) {
3721 next_t->set_note_types_per_minute (new_copy_end_bpm);
3723 next_t->set_note_types_per_minute (new_next_bpm);
3726 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3727 recompute_map (_metrics);
3733 Metrics::const_iterator d = future_map.begin();
3734 while (d != future_map.end()) {
3739 MetricPositionChanged (PropertyChange ()); // Emit Signal
3744 /** Returns the frame position of the musical position zero */
3746 TempoMap::music_origin ()
3748 Glib::Threads::RWLock::ReaderLock lm (lock);
3750 return first_tempo().frame();
3753 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3754 * the supplied frame, possibly returning a negative value.
3756 * @param frame The session frame position.
3757 * @param sub_num The subdivision to use when rounding the beat.
3758 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3759 * Positive integers indicate quarter note (non BBT) divisions.
3760 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3761 * @return The beat position of the supplied frame.
3763 * when working to a musical grid, the use of sub_nom indicates that
3764 * the position should be interpreted musically.
3766 * it effectively snaps to meter bars, meter beats or quarter note divisions
3767 * (as per current gui convention) and returns a musical position independent of frame rate.
3769 * If the supplied frame lies before the first meter, the return will be negative,
3770 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3771 * the continuation of the tempo curve (backwards).
3773 * This function is sensitive to tempo and meter.
3776 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3778 Glib::Threads::RWLock::ReaderLock lm (lock);
3780 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3784 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3786 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3789 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3790 * the supplied frame, possibly returning a negative value.
3792 * @param frame The session frame position.
3793 * @param sub_num The subdivision to use when rounding the quarter note.
3794 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3795 * Positive integers indicate quarter note (non BBT) divisions.
3796 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3797 * @return The quarter note position of the supplied frame.
3799 * When working to a musical grid, the use of sub_nom indicates that
3800 * the frame position should be interpreted musically.
3802 * it effectively snaps to meter bars, meter beats or quarter note divisions
3803 * (as per current gui convention) and returns a musical position independent of frame rate.
3805 * If the supplied frame lies before the first meter, the return will be negative,
3806 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3807 * the continuation of the tempo curve (backwards).
3809 * This function is tempo-sensitive.
3812 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3814 Glib::Threads::RWLock::ReaderLock lm (lock);
3816 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3820 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3822 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3825 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3826 } else if (sub_num == 1) {
3827 /* the gui requested exact musical (BBT) beat */
3828 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3829 } else if (sub_num == -1) {
3831 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3835 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3837 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3839 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3849 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3850 * @param pos the frame position in the tempo map.
3851 * @param bbt the distance in BBT time from pos to calculate.
3852 * @param dir the rounding direction..
3853 * @return the duration in frames between pos and bbt
3856 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3858 Glib::Threads::RWLock::ReaderLock lm (lock);
3860 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3862 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3865 pos_bbt.bars += bbt.bars;
3867 pos_bbt.ticks += bbt.ticks;
3868 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3870 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3873 pos_bbt.beats += bbt.beats;
3874 if ((double) pos_bbt.beats > divisions) {
3876 pos_bbt.beats -= divisions;
3878 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3880 return pos_bbt_frame - pos;
3884 if (pos_bbt.bars <= bbt.bars) {
3887 pos_bbt.bars -= bbt.bars;
3890 if (pos_bbt.ticks < bbt.ticks) {
3891 if (pos_bbt.bars > 1) {
3892 if (pos_bbt.beats == 1) {
3894 pos_bbt.beats = divisions;
3898 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3904 pos_bbt.ticks -= bbt.ticks;
3907 if (pos_bbt.beats <= bbt.beats) {
3908 if (pos_bbt.bars > 1) {
3910 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3915 pos_bbt.beats -= bbt.beats;
3918 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3925 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3927 return round_to_type (fr, dir, Bar);
3931 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3933 return round_to_type (fr, dir, Beat);
3937 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3939 Glib::Threads::RWLock::ReaderLock lm (lock);
3940 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);
3941 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3942 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3944 ticks -= beats * BBT_Time::ticks_per_beat;
3947 /* round to next (or same iff dir == RoundUpMaybe) */
3949 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3951 if (mod == 0 && dir == RoundUpMaybe) {
3952 /* right on the subdivision, which is fine, so do nothing */
3954 } else if (mod == 0) {
3955 /* right on the subdivision, so the difference is just the subdivision ticks */
3956 ticks += ticks_one_subdivisions_worth;
3959 /* not on subdivision, compute distance to next subdivision */
3961 ticks += ticks_one_subdivisions_worth - mod;
3964 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3965 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3966 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3967 // But I'm keeping it around, until we determine there are no terrible consequences.
3968 // if (ticks >= BBT_Time::ticks_per_beat) {
3969 // ticks -= BBT_Time::ticks_per_beat;
3972 } else if (dir < 0) {
3974 /* round to previous (or same iff dir == RoundDownMaybe) */
3976 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3978 if (difference == 0 && dir == RoundDownAlways) {
3979 /* right on the subdivision, but force-rounding down,
3980 so the difference is just the subdivision ticks */
3981 difference = ticks_one_subdivisions_worth;
3984 if (ticks < difference) {
3985 ticks = BBT_Time::ticks_per_beat - ticks;
3987 ticks -= difference;
3991 /* round to nearest */
3994 /* compute the distance to the previous and next subdivision */
3996 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3998 /* closer to the next subdivision, so shift forward */
4000 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
4002 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
4004 if (ticks > BBT_Time::ticks_per_beat) {
4006 ticks -= BBT_Time::ticks_per_beat;
4007 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
4010 } else if (rem > 0) {
4012 /* closer to previous subdivision, so shift backward */
4016 /* can't go backwards past zero, so ... */
4017 return MusicFrame (0, 0);
4019 /* step back to previous beat */
4021 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4022 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4024 ticks = lrint (ticks - rem);
4025 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4028 /* on the subdivision, do nothing */
4032 MusicFrame ret (0, 0);
4033 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4034 ret.division = sub_num;
4040 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
4042 Glib::Threads::RWLock::ReaderLock lm (lock);
4043 const double minute = minute_at_frame (frame);
4044 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4045 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
4046 MusicFrame ret (0, 0);
4053 /* find bar previous to 'frame' */
4059 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4063 } else if (dir > 0) {
4064 /* find bar following 'frame' */
4069 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4073 /* true rounding: find nearest bar */
4074 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4077 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4079 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4081 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4082 ret.frame = next_ft;
4087 ret.frame = prev_ft;
4099 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
4102 } else if (dir > 0) {
4103 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
4107 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
4114 return MusicFrame (0, 0);
4118 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4119 framepos_t lower, framepos_t upper, uint32_t bar_mod)
4121 Glib::Threads::RWLock::ReaderLock lm (lock);
4122 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
4124 /* although the map handles negative beats, bbt doesn't. */
4129 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
4133 while (pos >= 0 && pos < upper) {
4134 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
4135 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4136 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4137 const double qn = pulse_at_beat_locked (_metrics, cnt) * 4.0;
4139 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, qn));
4143 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
4148 bbt.bars -= bbt.bars % bar_mod;
4152 while (pos >= 0 && pos < upper) {
4153 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4154 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4155 const double qn = pulse_at_bbt_locked (_metrics, bbt) * 4.0;
4157 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, qn));
4158 bbt.bars += bar_mod;
4164 TempoMap::tempo_section_at_frame (framepos_t frame) const
4166 Glib::Threads::RWLock::ReaderLock lm (lock);
4168 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4172 TempoMap::tempo_section_at_frame (framepos_t frame)
4174 Glib::Threads::RWLock::ReaderLock lm (lock);
4176 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4180 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4182 TempoSection* prev = 0;
4186 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4188 if ((*i)->is_tempo()) {
4189 t = static_cast<TempoSection*> (*i);
4193 if (prev && t->minute() > minute) {
4203 abort(); /*NOTREACHED*/
4209 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4211 TempoSection* prev = 0;
4215 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4217 if ((*i)->is_tempo()) {
4218 t = static_cast<TempoSection*> (*i);
4222 if (prev && t->minute() > minute) {
4232 abort(); /*NOTREACHED*/
4238 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4240 TempoSection* prev_t = 0;
4241 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4245 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4246 if ((*i)->is_tempo()) {
4247 t = static_cast<TempoSection*> (*i);
4253 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4264 TempoMap::previous_tempo_section (TempoSection* ts) const
4266 Glib::Threads::RWLock::ReaderLock lm (lock);
4268 return previous_tempo_section_locked (_metrics, ts);
4273 TempoMap::previous_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4279 TempoSection* prev = 0;
4281 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4283 if ((*i)->is_tempo()) {
4284 TempoSection* t = static_cast<TempoSection*> (*i);
4290 if (prev && t == ts) {
4301 abort(); /*NOTREACHED*/
4308 TempoMap::next_tempo_section (TempoSection* ts) const
4310 Glib::Threads::RWLock::ReaderLock lm (lock);
4312 return next_tempo_section_locked (_metrics, ts);
4316 TempoMap::next_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4322 TempoSection* prev = 0;
4324 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4326 if ((*i)->is_tempo()) {
4327 TempoSection* t = static_cast<TempoSection*> (*i);
4333 if (prev && prev == ts) {
4344 abort(); /*NOTREACHED*/
4349 /* don't use this to calculate length (the tempo is only correct for this frame).
4350 do that stuff based on the beat_at_frame and frame_at_beat api
4353 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4355 Glib::Threads::RWLock::ReaderLock lm (lock);
4357 const TempoSection* ts_at = 0;
4358 const TempoSection* ts_after = 0;
4359 Metrics::const_iterator i;
4362 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4364 if ((*i)->is_tempo()) {
4365 t = static_cast<TempoSection*> (*i);
4369 if (ts_at && (*i)->frame() > frame) {
4379 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4381 /* must be treated as constant tempo */
4382 return ts_at->frames_per_quarter_note (_frame_rate);
4386 TempoMap::meter_section_at_frame (framepos_t frame) const
4388 Glib::Threads::RWLock::ReaderLock lm (lock);
4389 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4393 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4395 Metrics::const_iterator i;
4396 MeterSection* prev = 0;
4400 for (i = metrics.begin(); i != metrics.end(); ++i) {
4402 if (!(*i)->is_tempo()) {
4403 m = static_cast<MeterSection*> (*i);
4405 if (prev && (*i)->minute() > minute) {
4415 abort(); /*NOTREACHED*/
4422 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4424 MeterSection* prev_m = 0;
4426 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4428 if (!(*i)->is_tempo()) {
4429 m = static_cast<MeterSection*> (*i);
4430 if (prev_m && m->beat() > beat) {
4441 TempoMap::meter_section_at_beat (double beat) const
4443 Glib::Threads::RWLock::ReaderLock lm (lock);
4444 return meter_section_at_beat_locked (_metrics, beat);
4448 TempoMap::meter_at_frame (framepos_t frame) const
4450 TempoMetric m (metric_at (frame));
4455 TempoMap::fix_legacy_session ()
4457 MeterSection* prev_m = 0;
4458 TempoSection* prev_t = 0;
4459 bool have_initial_t = false;
4461 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4465 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4467 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4470 m->set_minute (0.0);
4471 m->set_position_lock_style (AudioTime);
4476 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4477 + (m->bbt().beats - 1)
4478 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4480 m->set_beat (start);
4481 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4482 + (m->bbt().beats - 1)
4483 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4484 m->set_pulse (start_beat / prev_m->note_divisor());
4487 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4492 /* Ramp type never existed in the era of this tempo section */
4493 t->set_end_note_types_per_minute (t->note_types_per_minute());
4497 t->set_minute (0.0);
4498 t->set_position_lock_style (AudioTime);
4500 have_initial_t = true;
4505 /* some 4.x sessions have no initial (non-movable) tempo. */
4506 if (!have_initial_t) {
4507 prev_t->set_pulse (0.0);
4508 prev_t->set_minute (0.0);
4509 prev_t->set_position_lock_style (AudioTime);
4510 prev_t->set_initial (true);
4511 prev_t->set_locked_to_meter (true);
4512 have_initial_t = true;
4515 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4516 + (t->legacy_bbt().beats - 1)
4517 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4519 t->set_pulse (beat / prev_m->note_divisor());
4521 /* really shouldn't happen but.. */
4522 t->set_pulse (beat / 4.0);
4530 TempoMap::fix_legacy_end_session ()
4532 TempoSection* prev_t = 0;
4534 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4537 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4544 if (prev_t->type() != TempoSection::Constant) {
4545 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4555 TempoMap::get_state ()
4557 Metrics::const_iterator i;
4558 XMLNode *root = new XMLNode ("TempoMap");
4561 Glib::Threads::RWLock::ReaderLock lm (lock);
4562 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4563 root->add_child_nocopy ((*i)->get_state());
4571 TempoMap::set_state (const XMLNode& node, int /*version*/)
4574 Glib::Threads::RWLock::WriterLock lm (lock);
4577 XMLNodeConstIterator niter;
4578 Metrics old_metrics (_metrics);
4581 nlist = node.children();
4583 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4584 XMLNode* child = *niter;
4586 if (child->name() == TempoSection::xml_state_node_name) {
4589 TempoSection* ts = new TempoSection (*child, _frame_rate);
4590 _metrics.push_back (ts);
4593 catch (failed_constructor& err){
4594 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4595 _metrics = old_metrics;
4596 old_metrics.clear();
4600 } else if (child->name() == MeterSection::xml_state_node_name) {
4603 MeterSection* ms = new MeterSection (*child, _frame_rate);
4604 _metrics.push_back (ms);
4607 catch (failed_constructor& err) {
4608 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4609 _metrics = old_metrics;
4610 old_metrics.clear();
4616 /* check for legacy sessions where bbt was the base musical unit for tempo */
4617 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4619 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4620 if (t->legacy_bbt().bars != 0) {
4621 fix_legacy_session();
4625 if (t->end_note_types_per_minute() < 0.0) {
4626 fix_legacy_end_session();
4634 if (niter == nlist.end()) {
4635 MetricSectionSorter cmp;
4636 _metrics.sort (cmp);
4639 /* check for multiple tempo/meters at the same location, which
4640 ardour2 somehow allowed.
4643 Metrics::iterator prev = _metrics.end();
4644 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4645 if (prev != _metrics.end()) {
4647 MeterSection* prev_m;
4649 TempoSection* prev_t;
4650 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4651 if (prev_m->beat() == ms->beat()) {
4652 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
4653 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
4656 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4657 if (prev_t->pulse() == ts->pulse()) {
4658 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4659 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4667 recompute_map (_metrics);
4669 Metrics::const_iterator d = old_metrics.begin();
4670 while (d != old_metrics.end()) {
4674 old_metrics.clear ();
4677 PropertyChanged (PropertyChange ());
4683 TempoMap::dump (std::ostream& o) const
4685 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4686 const MeterSection* m;
4687 const TempoSection* t;
4688 const TempoSection* prev_t = 0;
4690 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4692 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4693 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4694 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4695 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4696 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4698 o << " current start : " << t->note_types_per_minute()
4699 << " current end : " << t->end_note_types_per_minute()
4700 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4701 o << " previous : " << prev_t->note_types_per_minute()
4702 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4703 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4704 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4705 << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4706 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4709 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4710 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4711 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4712 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4715 o << "------" << std::endl;
4719 TempoMap::n_tempos() const
4721 Glib::Threads::RWLock::ReaderLock lm (lock);
4724 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4725 if ((*i)->is_tempo()) {
4734 TempoMap::n_meters() const
4736 Glib::Threads::RWLock::ReaderLock lm (lock);
4739 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4740 if (!(*i)->is_tempo()) {
4749 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4751 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4752 if ((*i)->frame() >= where && !(*i)->initial ()) {
4756 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4757 gui_set_meter_position (ms, (*i)->frame() + amount);
4760 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4761 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4766 PropertyChanged (PropertyChange ());
4770 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4774 std::list<MetricSection*> metric_kill_list;
4776 TempoSection* last_tempo = NULL;
4777 MeterSection* last_meter = NULL;
4778 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4779 bool meter_after = false; // is there a meter marker likewise?
4781 Glib::Threads::RWLock::WriterLock lm (lock);
4782 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4783 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4784 metric_kill_list.push_back(*i);
4785 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4788 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4792 else if ((*i)->frame() >= where) {
4793 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4794 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4795 if ((*i)->frame() == where) {
4796 // marker was immediately after end of range
4797 tempo_after = dynamic_cast<TempoSection*> (*i);
4798 meter_after = dynamic_cast<MeterSection*> (*i);
4804 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4805 if (last_tempo && !tempo_after) {
4806 metric_kill_list.remove(last_tempo);
4807 last_tempo->set_minute (minute_at_frame (where));
4810 if (last_meter && !meter_after) {
4811 metric_kill_list.remove(last_meter);
4812 last_meter->set_minute (minute_at_frame (where));
4816 //remove all the remaining metrics
4817 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4818 _metrics.remove(*i);
4823 recompute_map (_metrics);
4826 PropertyChanged (PropertyChange ());
4830 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4831 * pos can be -ve, if required.
4834 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4836 Glib::Threads::RWLock::ReaderLock lm (lock);
4837 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4839 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4843 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4845 Glib::Threads::RWLock::ReaderLock lm (lock);
4847 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4848 pos_bbt.ticks += op.ticks;
4849 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4851 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4853 pos_bbt.beats += op.beats;
4854 /* the meter in effect will start on the bar */
4855 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();
4856 while (pos_bbt.beats >= divisions_per_bar + 1) {
4858 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4859 pos_bbt.beats -= divisions_per_bar;
4861 pos_bbt.bars += op.bars;
4863 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4866 /** Count the number of beats that are equivalent to distance when going forward,
4870 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4872 Glib::Threads::RWLock::ReaderLock lm (lock);
4874 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4878 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4884 operator<< (std::ostream& o, const Meter& m) {
4885 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4889 operator<< (std::ostream& o, const Tempo& t) {
4890 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4894 operator<< (std::ostream& o, const MetricSection& section) {
4896 o << "MetricSection @ " << section.frame() << ' ';
4898 const TempoSection* ts;
4899 const MeterSection* ms;
4901 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4902 o << *((const Tempo*) ts);
4903 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4904 o << *((const Meter*) ms);