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;
1031 TempoSection* prev_tempo = 0;
1033 Glib::Threads::RWLock::WriterLock lm (lock);
1034 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true);
1035 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1037 if ((*i)->is_tempo()) {
1038 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1040 bool const ipm = ts->position_lock_style() == MusicTime;
1041 bool const lm = ts->locked_to_meter();
1042 if ((ipm && this_t->pulse() == ts->pulse()) || (!ipm && this_t->frame() == ts->frame())
1043 || (lm && this_t->pulse() == ts->pulse())) {
1044 if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1045 prev_tempo->set_end_note_types_per_minute (ts->note_types_per_minute());
1049 prev_tempo = this_t;
1052 recompute_map (_metrics);
1055 PropertyChanged (PropertyChange ());
1061 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1063 if (tempo.note_types_per_minute() <= 0.0) {
1064 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1068 bool const locked_to_meter = ts.locked_to_meter();
1069 bool const ts_clamped = ts.clamped();
1070 TempoSection* new_ts = 0;
1073 Glib::Threads::RWLock::WriterLock lm (lock);
1074 TempoSection& first (first_tempo());
1075 if (!ts.initial()) {
1076 if (locked_to_meter) {
1078 /* cannot move a meter-locked tempo section */
1079 *static_cast<Tempo*>(&ts) = tempo;
1080 recompute_map (_metrics);
1083 remove_tempo_locked (ts);
1084 new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter);
1085 new_ts->set_clamped (ts_clamped);
1087 if (new_ts && new_ts->type() == TempoSection::Constant) {
1088 new_ts->set_end_note_types_per_minute (new_ts->note_types_per_minute());
1090 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1092 if ((*i)->is_tempo()) {
1093 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1095 bool const ipm = new_ts->position_lock_style() == MusicTime;
1096 bool const lm = new_ts->locked_to_meter();
1097 if ((ipm && this_t->pulse() > new_ts->pulse()) || (!ipm && this_t->frame() > new_ts->frame())
1098 || (lm && this_t->pulse() > new_ts->pulse())) {
1099 new_ts->set_end_note_types_per_minute (tempo.end_note_types_per_minute());
1109 first.set_pulse (0.0);
1110 first.set_minute (minute_at_frame (frame));
1111 first.set_position_lock_style (AudioTime);
1112 first.set_locked_to_meter (true);
1113 first.set_clamped (ts_clamped);
1115 /* cannot move the first tempo section */
1116 *static_cast<Tempo*>(&first) = tempo;
1119 recompute_map (_metrics);
1122 PropertyChanged (PropertyChange ());
1126 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1127 , PositionLockStyle pls, bool recompute, bool locked_to_meter)
1129 TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _frame_rate);
1130 t->set_locked_to_meter (locked_to_meter);
1135 if (pls == AudioTime) {
1136 solve_map_minute (_metrics, t, t->minute());
1138 solve_map_pulse (_metrics, t, t->pulse());
1140 recompute_meters (_metrics);
1147 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1149 MeterSection* m = 0;
1151 Glib::Threads::RWLock::WriterLock lm (lock);
1152 m = add_meter_locked (meter, beat, where, frame, pls, true);
1157 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1162 PropertyChanged (PropertyChange ());
1167 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1170 Glib::Threads::RWLock::WriterLock lm (lock);
1171 const double beat = beat_at_bbt_locked (_metrics, where);
1173 if (!ms.initial()) {
1174 remove_meter_locked (ms);
1175 add_meter_locked (meter, beat, where, frame, pls, true);
1177 MeterSection& first (first_meter());
1178 TempoSection& first_t (first_tempo());
1179 /* cannot move the first meter section */
1180 *static_cast<Meter*>(&first) = meter;
1181 first.set_position_lock_style (AudioTime);
1182 first.set_pulse (0.0);
1183 first.set_minute (minute_at_frame (frame));
1184 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1185 first.set_beat (beat);
1186 first_t.set_minute (first.minute());
1187 first_t.set_locked_to_meter (true);
1188 first_t.set_pulse (0.0);
1189 first_t.set_position_lock_style (AudioTime);
1190 recompute_map (_metrics);
1194 PropertyChanged (PropertyChange ());
1198 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1200 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1201 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1202 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1203 TempoSection* mlt = 0;
1205 if (pls == AudioTime) {
1206 /* add meter-locked tempo */
1207 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), AudioTime, true, true);
1215 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1217 bool solved = false;
1219 do_insert (new_meter);
1223 if (pls == AudioTime) {
1224 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1225 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1226 fudge frame so that the meter ends up at its BBT position instead.
1229 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1232 solved = solve_map_bbt (_metrics, new_meter, where);
1233 /* required due to resetting the pulse of meter-locked tempi above.
1234 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1235 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1237 recompute_map (_metrics);
1241 if (!solved && recompute) {
1242 /* if this has failed to solve, there is little we can do other than to ensure that
1243 the new map is recalculated.
1245 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1246 recompute_map (_metrics);
1253 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1255 Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1258 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1259 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1264 Glib::Threads::RWLock::WriterLock lm (lock);
1265 *((Tempo*) t) = newtempo;
1266 recompute_map (_metrics);
1268 PropertyChanged (PropertyChange ());
1275 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1277 Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1280 TempoSection* first;
1281 Metrics::iterator i;
1283 /* find the TempoSection immediately preceding "where"
1286 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1288 if ((*i)->frame() > where) {
1294 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1307 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1317 Glib::Threads::RWLock::WriterLock lm (lock);
1318 /* cannot move the first tempo section */
1319 *((Tempo*)prev) = newtempo;
1320 recompute_map (_metrics);
1323 PropertyChanged (PropertyChange ());
1327 TempoMap::first_meter () const
1329 const MeterSection *m = 0;
1331 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1332 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1337 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1338 abort(); /*NOTREACHED*/
1343 TempoMap::first_meter ()
1345 MeterSection *m = 0;
1347 /* CALLER MUST HOLD LOCK */
1349 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1350 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1355 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1356 abort(); /*NOTREACHED*/
1361 TempoMap::first_tempo () const
1363 const TempoSection *t = 0;
1365 /* CALLER MUST HOLD LOCK */
1367 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1368 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1378 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1379 abort(); /*NOTREACHED*/
1384 TempoMap::first_tempo ()
1386 TempoSection *t = 0;
1388 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1389 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1399 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1400 abort(); /*NOTREACHED*/
1404 TempoMap::recompute_tempi (Metrics& metrics)
1406 TempoSection* prev_t = 0;
1408 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1411 if ((*i)->is_tempo()) {
1412 t = static_cast<TempoSection*> (*i);
1424 if (t->position_lock_style() == AudioTime) {
1425 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1426 if (!t->locked_to_meter()) {
1427 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1431 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1432 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1440 prev_t->set_c (0.0);
1443 /* tempos must be positioned correctly.
1444 the current approach is to use a meter's bbt time as its base position unit.
1445 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1446 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1449 TempoMap::recompute_meters (Metrics& metrics)
1451 MeterSection* meter = 0;
1452 MeterSection* prev_m = 0;
1454 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1455 if (!(*mi)->is_tempo()) {
1456 meter = static_cast<MeterSection*> (*mi);
1457 if (meter->position_lock_style() == AudioTime) {
1459 pair<double, BBT_Time> b_bbt;
1460 TempoSection* meter_locked_tempo = 0;
1461 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1463 if ((*ii)->is_tempo()) {
1464 t = static_cast<TempoSection*> (*ii);
1465 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1466 meter_locked_tempo = t;
1473 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1474 if (beats + prev_m->beat() != meter->beat()) {
1475 /* reordering caused a bbt change */
1477 beats = meter->beat() - prev_m->beat();
1478 b_bbt = make_pair (beats + prev_m->beat()
1479 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1480 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1482 } else if (!meter->initial()) {
1483 b_bbt = make_pair (meter->beat(), meter->bbt());
1484 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1487 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1489 if (meter_locked_tempo) {
1490 meter_locked_tempo->set_pulse (pulse);
1492 meter->set_beat (b_bbt);
1493 meter->set_pulse (pulse);
1498 pair<double, BBT_Time> b_bbt;
1500 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1501 if (beats + prev_m->beat() != meter->beat()) {
1502 /* reordering caused a bbt change */
1503 b_bbt = make_pair (beats + prev_m->beat()
1504 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1506 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1508 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1510 /* shouldn't happen - the first is audio-locked */
1511 pulse = pulse_at_beat_locked (metrics, meter->beat());
1512 b_bbt = make_pair (meter->beat(), meter->bbt());
1515 meter->set_beat (b_bbt);
1516 meter->set_pulse (pulse);
1517 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1526 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1528 /* CALLER MUST HOLD WRITE LOCK */
1530 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1533 /* silly call from Session::process() during startup
1538 recompute_tempi (metrics);
1539 recompute_meters (metrics);
1543 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1545 Glib::Threads::RWLock::ReaderLock lm (lock);
1546 TempoMetric m (first_meter(), first_tempo());
1549 *last = ++_metrics.begin();
1552 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1553 at something, because we insert the default tempo and meter during
1554 TempoMap construction.
1556 now see if we can find better candidates.
1559 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1561 if ((*i)->frame() > frame) {
1575 /* XX meters only */
1577 TempoMap::metric_at (BBT_Time bbt) const
1579 Glib::Threads::RWLock::ReaderLock lm (lock);
1580 TempoMetric m (first_meter(), first_tempo());
1582 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1583 at something, because we insert the default tempo and meter during
1584 TempoMap construction.
1586 now see if we can find better candidates.
1589 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1591 if (!(*i)->is_tempo()) {
1592 mw = static_cast<MeterSection*> (*i);
1593 BBT_Time section_start (mw->bbt());
1595 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1606 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1607 * @param frame The session frame position.
1608 * @return The beat duration according to the tempo map at the supplied frame.
1610 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1611 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1613 * This function uses both tempo and meter.
1616 TempoMap::beat_at_frame (const framecnt_t& frame) const
1618 Glib::Threads::RWLock::ReaderLock lm (lock);
1620 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1623 /* This function uses both tempo and meter.*/
1625 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1627 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1628 MeterSection* prev_m = 0;
1629 MeterSection* next_m = 0;
1631 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1632 if (!(*i)->is_tempo()) {
1633 if (prev_m && (*i)->minute() > minute) {
1634 next_m = static_cast<MeterSection*> (*i);
1637 prev_m = static_cast<MeterSection*> (*i);
1641 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1643 /* audio locked meters fake their beat */
1644 if (next_m && next_m->beat() < beat) {
1645 return next_m->beat();
1651 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1652 * @param beat The BBT (meter-based) beat.
1653 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1655 * This function uses both tempo and meter.
1658 TempoMap::frame_at_beat (const double& beat) const
1660 Glib::Threads::RWLock::ReaderLock lm (lock);
1662 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1665 /* meter & tempo section based */
1667 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1669 MeterSection* prev_m = 0;
1670 TempoSection* prev_t = 0;
1674 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1675 if (!(*i)->is_tempo()) {
1676 m = static_cast<MeterSection*> (*i);
1677 if (prev_m && m->beat() > beat) {
1687 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1688 if ((*i)->is_tempo()) {
1689 t = static_cast<TempoSection*> (*i);
1695 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1704 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1707 /** Returns a Tempo corresponding to the supplied frame position.
1708 * @param frame The audio frame.
1709 * @return a Tempo according to the tempo map at the supplied frame.
1713 TempoMap::tempo_at_frame (const framepos_t& frame) const
1715 Glib::Threads::RWLock::ReaderLock lm (lock);
1717 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1721 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1723 TempoSection* prev_t = 0;
1727 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1728 if ((*i)->is_tempo()) {
1729 t = static_cast<TempoSection*> (*i);
1733 if ((prev_t) && t->minute() > minute) {
1734 /* t is the section past frame */
1735 return prev_t->tempo_at_minute (minute);
1741 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1744 /** returns the frame at which the supplied tempo occurs, or
1745 * the frame of the last tempo section (search exhausted)
1746 * only the position of the first occurence will be returned
1750 TempoMap::frame_at_tempo (const Tempo& tempo) const
1752 Glib::Threads::RWLock::ReaderLock lm (lock);
1754 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1758 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1760 TempoSection* prev_t = 0;
1761 const double tempo_bpm = tempo.note_types_per_minute();
1763 Metrics::const_iterator i;
1765 for (i = metrics.begin(); i != metrics.end(); ++i) {
1767 if ((*i)->is_tempo()) {
1768 t = static_cast<TempoSection*> (*i);
1776 if (t->note_types_per_minute() == tempo_bpm) {
1781 const double prev_t_bpm = prev_t->note_types_per_minute();
1782 const double prev_t_end_bpm = prev_t->end_note_types_per_minute();
1783 if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm)
1784 || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm)
1785 || (prev_t_end_bpm == tempo_bpm)) {
1787 return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
1794 return prev_t->minute();
1798 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1800 TempoSection* prev_t = 0;
1804 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1805 if ((*i)->is_tempo()) {
1806 t = static_cast<TempoSection*> (*i);
1810 if ((prev_t) && t->pulse() > pulse) {
1811 /* t is the section past frame */
1812 return prev_t->tempo_at_pulse (pulse);
1818 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1822 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1824 TempoSection* prev_t = 0;
1825 const double tempo_bpm = tempo.note_types_per_minute();
1827 Metrics::const_iterator i;
1829 for (i = metrics.begin(); i != metrics.end(); ++i) {
1831 if ((*i)->is_tempo()) {
1832 t = static_cast<TempoSection*> (*i);
1838 const double t_bpm = t->note_types_per_minute();
1840 if (t_bpm == tempo_bpm) {
1845 const double prev_t_bpm = prev_t->note_types_per_minute();
1847 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1848 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1855 return prev_t->pulse();
1858 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1859 * @param qn the position in quarter note beats.
1860 * @return the Tempo at the supplied quarter-note.
1863 TempoMap::tempo_at_quarter_note (const double& qn) const
1865 Glib::Threads::RWLock::ReaderLock lm (lock);
1867 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1870 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1871 * @param tempo the tempo.
1872 * @return the position in quarter-note beats where the map bpm
1873 * is equal to that of the Tempo. currently ignores note_type.
1876 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1878 Glib::Threads::RWLock::ReaderLock lm (lock);
1880 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;
1883 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1884 * @param metrics the list of metric sections used to calculate the pulse.
1885 * @param beat The BBT (meter-based) beat.
1886 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1888 * a pulse or whole note is the base musical position of a MetricSection.
1889 * it is equivalent to four quarter notes.
1893 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1895 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1897 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1900 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1901 * @param metrics the list of metric sections used to calculate the beat.
1902 * @param pulse the whole-note pulse.
1903 * @return the meter-based beat at the supplied whole-note pulse.
1905 * a pulse or whole note is the base musical position of a MetricSection.
1906 * it is equivalent to four quarter notes.
1909 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1911 MeterSection* prev_m = 0;
1913 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1915 if (!(*i)->is_tempo()) {
1916 m = static_cast<MeterSection*> (*i);
1917 if (prev_m && m->pulse() > pulse) {
1925 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1929 /* tempo section based */
1931 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1933 /* HOLD (at least) THE READER LOCK */
1934 TempoSection* prev_t = 0;
1936 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1938 if ((*i)->is_tempo()) {
1939 t = static_cast<TempoSection*> (*i);
1943 if (prev_t && t->minute() > minute) {
1944 /*the previous ts is the one containing the frame */
1945 const double ret = prev_t->pulse_at_minute (minute);
1946 /* audio locked section in new meter*/
1947 if (t->pulse() < ret) {
1956 /* treated as constant for this ts */
1957 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1959 return pulses_in_section + prev_t->pulse();
1962 /* tempo section based */
1964 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1966 /* HOLD THE READER LOCK */
1968 const TempoSection* prev_t = 0;
1970 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1973 if ((*i)->is_tempo()) {
1974 t = static_cast<TempoSection*> (*i);
1978 if (prev_t && t->pulse() > pulse) {
1979 return prev_t->minute_at_pulse (pulse);
1985 /* must be treated as constant, irrespective of _type */
1986 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1988 return dtime + prev_t->minute();
1991 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1992 * @param bbt The BBT time (meter-based).
1993 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1997 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1999 Glib::Threads::RWLock::ReaderLock lm (lock);
2000 return beat_at_bbt_locked (_metrics, bbt);
2005 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2007 /* CALLER HOLDS READ LOCK */
2009 MeterSection* prev_m = 0;
2011 /* because audio-locked meters have 'fake' integral beats,
2012 there is no pulse offset here.
2016 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2017 if (!(*i)->is_tempo()) {
2018 m = static_cast<MeterSection*> (*i);
2020 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2021 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2029 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2030 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2031 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2036 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2037 * @param beat The BBT (meter-based) beat.
2038 * @return The BBT time (meter-based) at the supplied meter-based beat.
2042 TempoMap::bbt_at_beat (const double& beat)
2044 Glib::Threads::RWLock::ReaderLock lm (lock);
2045 return bbt_at_beat_locked (_metrics, beat);
2049 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2051 /* CALLER HOLDS READ LOCK */
2052 MeterSection* prev_m = 0;
2053 const double beats = max (0.0, b);
2055 MeterSection* m = 0;
2057 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2058 if (!(*i)->is_tempo()) {
2059 m = static_cast<MeterSection*> (*i);
2061 if (m->beat() > beats) {
2062 /* this is the meter after the one our beat is on*/
2072 const double beats_in_ms = beats - prev_m->beat();
2073 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2074 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2075 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2076 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2080 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2081 ret.beats = (uint32_t) floor (remaining_beats);
2082 ret.bars = total_bars;
2084 /* 0 0 0 to 1 1 0 - based mapping*/
2088 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2090 ret.ticks -= BBT_Time::ticks_per_beat;
2093 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2101 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2102 * @param bbt The BBT time (meter-based).
2103 * @return the quarter note beat at the supplied BBT time
2105 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2107 * while the input uses meter, the output does not.
2110 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2112 Glib::Threads::RWLock::ReaderLock lm (lock);
2114 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2118 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2120 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2123 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2126 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2130 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2132 /* CALLER HOLDS READ LOCK */
2134 MeterSection* prev_m = 0;
2136 /* because audio-locked meters have 'fake' integral beats,
2137 there is no pulse offset here.
2141 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2142 if (!(*i)->is_tempo()) {
2143 m = static_cast<MeterSection*> (*i);
2145 if (m->bbt().bars > bbt.bars) {
2153 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2154 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2155 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2160 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2161 * @param qn the quarter-note beat.
2162 * @return The BBT time (meter-based) at the supplied meter-based beat.
2164 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2168 TempoMap::bbt_at_quarter_note (const double& qn)
2170 Glib::Threads::RWLock::ReaderLock lm (lock);
2172 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2175 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2176 * @param metrics The list of metric sections used to determine the result.
2177 * @param pulse The whole-note pulse.
2178 * @return The BBT time at the supplied whole-note pulse.
2180 * a pulse or whole note is the basic musical position of a MetricSection.
2181 * it is equivalent to four quarter notes.
2182 * while the output uses meter, the input does not.
2185 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2187 MeterSection* prev_m = 0;
2189 MeterSection* m = 0;
2191 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2193 if (!(*i)->is_tempo()) {
2194 m = static_cast<MeterSection*> (*i);
2197 double const pulses_to_m = m->pulse() - prev_m->pulse();
2198 if (prev_m->pulse() + pulses_to_m > pulse) {
2199 /* this is the meter after the one our beat is on*/
2210 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2211 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2212 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2213 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2214 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2218 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2219 ret.beats = (uint32_t) floor (remaining_beats);
2220 ret.bars = total_bars;
2222 /* 0 0 0 to 1 1 0 mapping*/
2226 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2228 ret.ticks -= BBT_Time::ticks_per_beat;
2231 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2239 /** Returns the BBT time corresponding to the supplied frame position.
2240 * @param frame the position in audio samples.
2241 * @return the BBT time at the frame position .
2245 TempoMap::bbt_at_frame (framepos_t frame)
2253 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2258 const double minute = minute_at_frame (frame);
2260 Glib::Threads::RWLock::ReaderLock lm (lock);
2262 return bbt_at_minute_locked (_metrics, minute);
2266 TempoMap::bbt_at_frame_rt (framepos_t frame)
2268 const double minute = minute_at_frame (frame);
2270 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2273 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2276 return bbt_at_minute_locked (_metrics, minute);
2280 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2290 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2291 MeterSection* prev_m = 0;
2292 MeterSection* next_m = 0;
2296 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2297 if (!(*i)->is_tempo()) {
2298 m = static_cast<MeterSection*> (*i);
2299 if (prev_m && m->minute() > minute) {
2307 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2309 /* handle frame before first meter */
2310 if (minute < prev_m->minute()) {
2313 /* audio locked meters fake their beat */
2314 if (next_m && next_m->beat() < beat) {
2315 beat = next_m->beat();
2318 beat = max (0.0, beat);
2320 const double beats_in_ms = beat - prev_m->beat();
2321 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2322 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2323 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2324 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2328 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2329 ret.beats = (uint32_t) floor (remaining_beats);
2330 ret.bars = total_bars;
2332 /* 0 0 0 to 1 1 0 - based mapping*/
2336 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2338 ret.ticks -= BBT_Time::ticks_per_beat;
2341 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2349 /** Returns the frame position corresponding to the supplied BBT time.
2350 * @param bbt the position in BBT time.
2351 * @return the frame position at bbt.
2355 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2359 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2364 if (bbt.beats < 1) {
2365 throw std::logic_error ("beats are counted from one");
2370 Glib::Threads::RWLock::ReaderLock lm (lock);
2371 minute = minute_at_bbt_locked (_metrics, bbt);
2374 return frame_at_minute (minute);
2377 /* meter & tempo section based */
2379 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2381 /* HOLD THE READER LOCK */
2383 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2388 * Returns the quarter-note beat position corresponding to the supplied frame.
2390 * @param frame the position in frames.
2391 * @return The quarter-note position of the supplied frame. Ignores meter.
2395 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2397 const double minute = minute_at_frame (frame);
2399 Glib::Threads::RWLock::ReaderLock lm (lock);
2401 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2405 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2407 const double minute = minute_at_frame (frame);
2409 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2412 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2415 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2419 * Returns the frame position corresponding to the supplied quarter-note beat.
2421 * @param quarter_note the quarter-note position.
2422 * @return the frame position of the supplied quarter-note. Ignores meter.
2427 TempoMap::frame_at_quarter_note (const double quarter_note) const
2431 Glib::Threads::RWLock::ReaderLock lm (lock);
2433 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2436 return frame_at_minute (minute);
2439 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2440 * @param beat The BBT (meter-based) beat.
2441 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2443 * a quarter-note may be compared with and assigned to Evoral::Beats.
2447 TempoMap::quarter_note_at_beat (const double beat) const
2449 Glib::Threads::RWLock::ReaderLock lm (lock);
2451 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2454 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2455 * @param quarter_note The position in quarter-note beats.
2456 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2458 * a quarter-note is the musical unit of Evoral::Beats.
2462 TempoMap::beat_at_quarter_note (const double quarter_note) const
2464 Glib::Threads::RWLock::ReaderLock lm (lock);
2466 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2469 /** Returns the duration in frames between two supplied quarter-note beat positions.
2470 * @param start the first position in quarter-note beats.
2471 * @param end the end position in quarter-note beats.
2472 * @return the frame distance ober the quarter-note beats duration.
2474 * use this rather than e.g.
2475 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2476 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2480 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2485 Glib::Threads::RWLock::ReaderLock lm (lock);
2486 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2489 return frame_at_minute (minutes);
2493 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2496 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2500 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2502 Glib::Threads::RWLock::ReaderLock lm (lock);
2504 return quarter_notes_between_frames_locked (_metrics, start, end);
2508 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2510 const TempoSection* prev_t = 0;
2512 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2515 if ((*i)->is_tempo()) {
2516 t = static_cast<TempoSection*> (*i);
2520 if (prev_t && t->frame() > start) {
2527 const double start_qn = prev_t->pulse_at_frame (start);
2529 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2532 if ((*i)->is_tempo()) {
2533 t = static_cast<TempoSection*> (*i);
2537 if (prev_t && t->frame() > end) {
2543 const double end_qn = prev_t->pulse_at_frame (end);
2545 return (end_qn - start_qn) * 4.0;
2549 TempoMap::check_solved (const Metrics& metrics) const
2551 TempoSection* prev_t = 0;
2552 MeterSection* prev_m = 0;
2554 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2557 if ((*i)->is_tempo()) {
2558 t = static_cast<TempoSection*> (*i);
2563 /* check ordering */
2564 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2568 /* precision check ensures tempo and frames align.*/
2569 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2570 if (!t->locked_to_meter()) {
2575 /* gradient limit - who knows what it should be?
2576 things are also ok (if a little chaotic) without this
2578 if (fabs (prev_t->c()) > 1000.0) {
2579 //std::cout << "c : " << prev_t->c() << std::endl;
2586 if (!(*i)->is_tempo()) {
2587 m = static_cast<MeterSection*> (*i);
2588 if (prev_m && m->position_lock_style() == AudioTime) {
2589 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2590 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2591 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2593 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2607 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2609 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2611 if ((*i)->is_tempo()) {
2612 t = static_cast<TempoSection*> (*i);
2613 if (t->locked_to_meter()) {
2614 t->set_active (true);
2615 } else if (t->position_lock_style() == AudioTime) {
2616 if (t->frame() < frame) {
2617 t->set_active (false);
2618 t->set_pulse (-1.0);
2619 } else if (t->frame() > frame) {
2620 t->set_active (true);
2621 } else if (t->frame() == frame) {
2631 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2633 TempoSection* prev_t = 0;
2634 TempoSection* section_prev = 0;
2635 double first_m_minute = 0.0;
2636 const bool sml = section->locked_to_meter();
2638 /* can't move a tempo before the first meter */
2639 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2641 if (!(*i)->is_tempo()) {
2642 m = static_cast<MeterSection*> (*i);
2644 first_m_minute = m->minute();
2649 if (!section->initial() && minute <= first_m_minute) {
2653 section->set_active (true);
2654 section->set_minute (minute);
2656 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2658 if ((*i)->is_tempo()) {
2659 t = static_cast<TempoSection*> (*i);
2671 if (t->frame() == frame_at_minute (minute)) {
2675 const bool tlm = t->position_lock_style() == MusicTime;
2677 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2678 section_prev = prev_t;
2680 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2681 if (!section->locked_to_meter()) {
2682 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2687 if (t->position_lock_style() == MusicTime) {
2688 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2689 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2691 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2692 if (!t->locked_to_meter()) {
2693 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2702 recompute_tempi (imaginary);
2704 if (check_solved (imaginary)) {
2707 dunp (imaginary, std::cout);
2711 MetricSectionFrameSorter fcmp;
2712 imaginary.sort (fcmp);
2714 recompute_tempi (imaginary);
2716 if (check_solved (imaginary)) {
2724 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2726 TempoSection* prev_t = 0;
2727 TempoSection* section_prev = 0;
2729 section->set_pulse (pulse);
2731 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2733 if ((*i)->is_tempo()) {
2734 t = static_cast<TempoSection*> (*i);
2745 section_prev = prev_t;
2749 if (t->position_lock_style() == MusicTime) {
2750 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2751 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2753 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2754 if (!t->locked_to_meter()) {
2755 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2764 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2765 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2769 recompute_tempi (imaginary);
2771 if (check_solved (imaginary)) {
2774 dunp (imaginary, std::cout);
2778 MetricSectionSorter cmp;
2779 imaginary.sort (cmp);
2781 recompute_tempi (imaginary);
2783 * XX need a restriction here, but only for this case,
2784 * as audio locked tempos don't interact in the same way.
2786 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2788 * |50 bpm |250 bpm |60 bpm
2789 * drag 250 to the pulse after 60->
2790 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2792 if (check_solved (imaginary)) {
2800 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2802 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2803 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2804 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2808 if (section->initial()) {
2809 /* lock the first tempo to our first meter */
2810 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2815 TempoSection* meter_locked_tempo = 0;
2817 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2819 if ((*ii)->is_tempo()) {
2820 t = static_cast<TempoSection*> (*ii);
2821 if (t->locked_to_meter() && t->frame() == section->frame()) {
2822 meter_locked_tempo = t;
2828 if (!meter_locked_tempo) {
2832 MeterSection* prev_m = 0;
2834 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2835 bool solved = false;
2837 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2839 if (!(*i)->is_tempo()) {
2840 m = static_cast<MeterSection*> (*i);
2842 if (prev_m && !section->initial()) {
2843 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2844 if (beats + prev_m->beat() < section->beat()) {
2845 /* set the section pulse according to its musical position,
2846 * as an earlier time than this has been requested.
2848 const double new_pulse = ((section->beat() - prev_m->beat())
2849 / prev_m->note_divisor()) + prev_m->pulse();
2851 tempo_copy->set_position_lock_style (MusicTime);
2852 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2853 meter_locked_tempo->set_position_lock_style (MusicTime);
2854 section->set_position_lock_style (MusicTime);
2855 section->set_pulse (new_pulse);
2856 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2857 meter_locked_tempo->set_position_lock_style (AudioTime);
2858 section->set_position_lock_style (AudioTime);
2859 section->set_minute (meter_locked_tempo->minute());
2865 Metrics::const_iterator d = future_map.begin();
2866 while (d != future_map.end()) {
2875 /* all is ok. set section's locked tempo if allowed.
2876 possibly disallow if there is an adjacent audio-locked tempo.
2877 XX this check could possibly go. its never actually happened here.
2879 MeterSection* meter_copy = const_cast<MeterSection*>
2880 (&meter_section_at_minute_locked (future_map, section->minute()));
2882 meter_copy->set_minute (minute);
2884 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2885 section->set_minute (minute);
2886 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2887 / prev_m->note_divisor()) + prev_m->pulse());
2888 solve_map_minute (imaginary, meter_locked_tempo, minute);
2893 Metrics::const_iterator d = future_map.begin();
2894 while (d != future_map.end()) {
2904 /* initial (first meter atm) */
2906 tempo_copy->set_minute (minute);
2907 tempo_copy->set_pulse (0.0);
2909 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2910 section->set_minute (minute);
2911 meter_locked_tempo->set_minute (minute);
2912 meter_locked_tempo->set_pulse (0.0);
2913 solve_map_minute (imaginary, meter_locked_tempo, minute);
2918 Metrics::const_iterator d = future_map.begin();
2919 while (d != future_map.end()) {
2928 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2929 section->set_beat (b_bbt);
2930 section->set_pulse (0.0);
2940 MetricSectionFrameSorter fcmp;
2941 imaginary.sort (fcmp);
2943 recompute_meters (imaginary);
2949 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2951 /* disallow setting section to an existing meter's bbt */
2952 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2954 if (!(*i)->is_tempo()) {
2955 m = static_cast<MeterSection*> (*i);
2956 if (m != section && m->bbt().bars == when.bars) {
2962 MeterSection* prev_m = 0;
2963 MeterSection* section_prev = 0;
2965 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2967 if (!(*i)->is_tempo()) {
2968 m = static_cast<MeterSection*> (*i);
2974 pair<double, BBT_Time> b_bbt;
2975 double new_pulse = 0.0;
2977 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2978 section_prev = prev_m;
2980 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2981 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2982 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2984 section->set_beat (b_bbt);
2985 section->set_pulse (pulse);
2986 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2990 if (m->position_lock_style() == AudioTime) {
2991 TempoSection* meter_locked_tempo = 0;
2993 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2995 if ((*ii)->is_tempo()) {
2996 t = static_cast<TempoSection*> (*ii);
2997 if (t->locked_to_meter() && t->frame() == m->frame()) {
2998 meter_locked_tempo = t;
3004 if (!meter_locked_tempo) {
3009 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3011 if (beats + prev_m->beat() != m->beat()) {
3012 /* tempo/ meter change caused a change in beat (bar). */
3014 /* the user has requested that the previous section of music overlaps this one.
3015 we have no choice but to change the bar number here, as being locked to audio means
3016 we must stay where we are on the timeline.
3018 beats = m->beat() - prev_m->beat();
3019 b_bbt = make_pair (beats + prev_m->beat()
3020 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3021 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3023 } else if (!m->initial()) {
3024 b_bbt = make_pair (m->beat(), m->bbt());
3025 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3028 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3031 meter_locked_tempo->set_pulse (new_pulse);
3032 m->set_beat (b_bbt);
3033 m->set_pulse (new_pulse);
3037 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3038 if (beats + prev_m->beat() != m->beat()) {
3039 /* tempo/ meter change caused a change in beat (bar). */
3040 b_bbt = make_pair (beats + prev_m->beat()
3041 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3043 b_bbt = make_pair (beats + prev_m->beat()
3046 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3047 m->set_beat (b_bbt);
3048 m->set_pulse (new_pulse);
3049 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3056 if (!section_prev) {
3058 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3059 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3060 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3062 section->set_beat (b_bbt);
3063 section->set_pulse (pulse);
3064 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3067 MetricSectionSorter cmp;
3068 imaginary.sort (cmp);
3070 recompute_meters (imaginary);
3075 /** places a copy of _metrics into copy and returns a pointer
3076 * to section's equivalent in copy.
3079 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section) const
3081 TempoSection* ret = 0;
3083 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3084 if ((*i)->is_tempo()) {
3085 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3087 ret = new TempoSection (*t);
3088 copy.push_back (ret);
3092 TempoSection* cp = new TempoSection (*t);
3093 copy.push_back (cp);
3095 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3096 MeterSection* cp = new MeterSection (*m);
3097 copy.push_back (cp);
3105 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section) const
3107 MeterSection* ret = 0;
3109 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3110 if ((*i)->is_tempo()) {
3111 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3112 TempoSection* cp = new TempoSection (*t);
3113 copy.push_back (cp);
3115 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3117 ret = new MeterSection (*m);
3118 copy.push_back (ret);
3121 MeterSection* cp = new MeterSection (*m);
3122 copy.push_back (cp);
3129 /** answers the question "is this a valid beat position for this tempo section?".
3130 * it returns true if the tempo section can be moved to the requested bbt position,
3131 * leaving the tempo map in a solved state.
3132 * @param ts the tempo section to be moved
3133 * @param bbt the requested new position for the tempo section
3134 * @return true if the tempo section can be moved to the position, otherwise false.
3137 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3140 TempoSection* tempo_copy = 0;
3143 Glib::Threads::RWLock::ReaderLock lm (lock);
3144 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3150 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3152 Metrics::const_iterator d = copy.begin();
3153 while (d != copy.end()) {
3162 * 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,
3163 * taking any possible reordering as a consequence of this into account.
3164 * @param section - the section to be altered
3165 * @param bbt - the BBT time where the altered tempo will fall
3166 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3168 pair<double, framepos_t>
3169 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3172 pair<double, framepos_t> ret = make_pair (0.0, 0);
3174 Glib::Threads::RWLock::ReaderLock lm (lock);
3176 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3178 const double beat = beat_at_bbt_locked (future_map, bbt);
3180 if (section->position_lock_style() == AudioTime) {
3181 tempo_copy->set_position_lock_style (MusicTime);
3184 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3185 ret.first = tempo_copy->pulse();
3186 ret.second = tempo_copy->frame();
3188 ret.first = section->pulse();
3189 ret.second = section->frame();
3192 Metrics::const_iterator d = future_map.begin();
3193 while (d != future_map.end()) {
3200 /** moves a TempoSection to a specified position.
3201 * @param ts - the section to be moved
3202 * @param frame - the new position in frames for the tempo
3203 * @param sub_num - the snap division to use if using musical time.
3205 * if sub_num is non-zero, the frame position is used to calculate an exact
3208 * -1 | snap to bars (meter-based)
3209 * 0 | no snap - use audio frame for musical position
3210 * 1 | snap to meter-based (BBT) beat
3211 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3213 * this follows the snap convention in the gui.
3214 * if sub_num is zero, the musical position will be taken from the supplied frame.
3217 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3221 if (ts->position_lock_style() == MusicTime) {
3223 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3224 Glib::Threads::RWLock::WriterLock lm (lock);
3225 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3227 tempo_copy->set_position_lock_style (AudioTime);
3229 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3230 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3231 const double pulse = pulse_at_beat_locked (future_map, beat);
3233 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3234 solve_map_pulse (_metrics, ts, pulse);
3235 recompute_meters (_metrics);
3243 Glib::Threads::RWLock::WriterLock lm (lock);
3244 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3248 /* We're moving the object that defines the grid while snapping to it...
3249 * Placing the ts at the beat corresponding to the requested frame may shift the
3250 * grid in such a way that the mouse is left hovering over a completerly different division,
3251 * causing jittering when the mouse next moves (esp. large tempo deltas).
3252 * We fudge around this by doing this in the musical domain and then swapping back for the recompute.
3254 const double qn = exact_qn_at_frame_locked (_metrics, frame, sub_num);
3255 tempo_copy->set_position_lock_style (MusicTime);
3256 if (solve_map_pulse (future_map, tempo_copy, qn / 4.0)) {
3257 ts->set_position_lock_style (MusicTime);
3258 solve_map_pulse (_metrics, ts, qn / 4.0);
3259 ts->set_position_lock_style (AudioTime);
3260 recompute_meters (_metrics);
3263 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3264 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3265 recompute_meters (_metrics);
3271 Metrics::const_iterator d = future_map.begin();
3272 while (d != future_map.end()) {
3277 MetricPositionChanged (PropertyChange ()); // Emit Signal
3280 /** moves a MeterSection to a specified position.
3281 * @param ms - the section to be moved
3282 * @param frame - the new position in frames for the meter
3284 * as a meter cannot snap to anything but bars,
3285 * the supplied frame is rounded to the nearest bar, possibly
3286 * leaving the meter position unchanged.
3289 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3293 if (ms->position_lock_style() == AudioTime) {
3296 Glib::Threads::RWLock::WriterLock lm (lock);
3297 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3299 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3300 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3301 recompute_tempi (_metrics);
3306 Glib::Threads::RWLock::WriterLock lm (lock);
3307 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3309 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3310 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3312 if (solve_map_bbt (future_map, copy, bbt)) {
3313 solve_map_bbt (_metrics, ms, bbt);
3314 recompute_tempi (_metrics);
3319 Metrics::const_iterator d = future_map.begin();
3320 while (d != future_map.end()) {
3325 MetricPositionChanged (PropertyChange ()); // Emit Signal
3329 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3332 bool can_solve = false;
3334 Glib::Threads::RWLock::WriterLock lm (lock);
3335 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3337 if (tempo_copy->type() == TempoSection::Constant) {
3338 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3339 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3341 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3342 tempo_copy->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3345 if (ts->clamped()) {
3346 TempoSection* prev = 0;
3347 if ((prev = previous_tempo_section_locked (future_map, tempo_copy)) != 0) {
3348 prev->set_end_note_types_per_minute (tempo_copy->note_types_per_minute());
3352 recompute_tempi (future_map);
3354 if (check_solved (future_map)) {
3355 if (ts->type() == TempoSection::Constant) {
3356 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3357 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3359 ts->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3360 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3363 if (ts->clamped()) {
3364 TempoSection* prev = 0;
3365 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3366 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3370 recompute_map (_metrics);
3375 Metrics::const_iterator d = future_map.begin();
3376 while (d != future_map.end()) {
3381 MetricPositionChanged (PropertyChange ()); // Emit Signal
3388 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3391 Ts (future prev_t) Tnext
3394 |----------|----------
3401 Glib::Threads::RWLock::WriterLock lm (lock);
3407 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3413 /* minimum allowed measurement distance in frames */
3414 framepos_t const min_dframe = 2;
3417 if (prev_t->clamped()) {
3418 TempoSection* next_t = next_tempo_section_locked (future_map, prev_t);
3419 TempoSection* prev_to_prev_t = previous_tempo_section_locked (future_map, prev_t);
3420 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3421 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3423 double contribution = 0.0;
3424 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3425 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3427 framepos_t const fr_off = (end_frame - frame);
3428 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3430 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3431 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3432 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3434 new_bpm = prev_t->note_types_per_minute();
3437 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3439 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
3440 / (double) (end_frame - prev_t->frame()));
3442 new_bpm = prev_t->note_types_per_minute();
3445 new_bpm = min (new_bpm, (double) 1000.0);
3447 /* don't clamp and proceed here.
3448 testing has revealed that this can go negative,
3449 which is an entirely different thing to just being too low.
3452 if (new_bpm < 0.5) {
3456 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3457 prev_t->set_note_types_per_minute (new_bpm);
3459 prev_t->set_end_note_types_per_minute (new_bpm);
3460 prev_t->set_note_types_per_minute (new_bpm);
3463 if (prev_t->clamped()) {
3464 TempoSection* prev = 0;
3465 if ((prev = previous_tempo_section_locked (future_map, prev_t)) != 0) {
3466 prev->set_end_note_types_per_minute (prev_t->note_types_per_minute());
3470 recompute_tempi (future_map);
3471 recompute_meters (future_map);
3473 if (check_solved (future_map)) {
3474 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3475 ts->set_note_types_per_minute (new_bpm);
3477 ts->set_end_note_types_per_minute (new_bpm);
3478 ts->set_note_types_per_minute (new_bpm);
3480 if (ts->clamped()) {
3481 TempoSection* prev = 0;
3482 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3483 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3486 recompute_tempi (_metrics);
3487 recompute_meters (_metrics);
3493 Metrics::const_iterator d = future_map.begin();
3494 while (d != future_map.end()) {
3498 MetricPositionChanged (PropertyChange ()); // Emit Signal
3503 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3506 Ts (future prev_t) Tnext
3509 |----------|----------
3516 Glib::Threads::RWLock::WriterLock lm (lock);
3522 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3528 /* minimum allowed measurement distance in frames */
3529 framepos_t const min_dframe = 2;
3532 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3533 new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->frame() - frame)
3534 / (double) (prev_t->frame() - end_frame));
3536 new_bpm = prev_t->end_note_types_per_minute();
3539 new_bpm = min (new_bpm, (double) 1000.0);
3541 if (new_bpm < 0.5) {
3545 prev_t->set_end_note_types_per_minute (new_bpm);
3547 TempoSection* next = 0;
3548 if ((next = next_tempo_section_locked (future_map, prev_t)) != 0) {
3549 if (next->clamped()) {
3550 next->set_note_types_per_minute (prev_t->end_note_types_per_minute());
3554 recompute_tempi (future_map);
3555 recompute_meters (future_map);
3557 if (check_solved (future_map)) {
3558 ts->set_end_note_types_per_minute (new_bpm);
3560 TempoSection* true_next = 0;
3561 if ((true_next = next_tempo_section_locked (_metrics, ts)) != 0) {
3562 if (true_next->clamped()) {
3563 true_next->set_note_types_per_minute (ts->end_note_types_per_minute());
3567 recompute_tempi (_metrics);
3568 recompute_meters (_metrics);
3574 Metrics::const_iterator d = future_map.begin();
3575 while (d != future_map.end()) {
3580 MetricPositionChanged (PropertyChange ()); // Emit Signal
3584 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
3586 TempoSection* next_t = 0;
3587 TempoSection* next_to_next_t = 0;
3589 bool can_solve = false;
3591 /* minimum allowed measurement distance in frames */
3592 framepos_t const min_dframe = 2;
3595 Glib::Threads::RWLock::WriterLock lm (lock);
3600 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3601 TempoSection* prev_to_prev_t = 0;
3602 const frameoffset_t fr_off = end_frame - frame;
3608 if (tempo_copy->pulse() > 0.0) {
3609 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
3612 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3613 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3614 next_t = static_cast<TempoSection*> (*i);
3623 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3624 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3625 next_to_next_t = static_cast<TempoSection*> (*i);
3630 if (!next_to_next_t) {
3634 double prev_contribution = 0.0;
3636 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3637 prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3640 const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
3643 framepos_t old_tc_minute = tempo_copy->minute();
3644 double old_next_minute = next_t->minute();
3645 double old_next_to_next_minute = next_to_next_t->minute();
3648 double new_next_bpm;
3649 double new_copy_end_bpm;
3651 if (frame > tempo_copy->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > tempo_copy->frame() + min_dframe) {
3652 new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
3653 / (double) (end_frame - tempo_copy->frame()));
3655 new_bpm = tempo_copy->note_types_per_minute();
3658 /* don't clamp and proceed here.
3659 testing has revealed that this can go negative,
3660 which is an entirely different thing to just being too low.
3662 if (new_bpm < 0.5) {
3666 new_bpm = min (new_bpm, (double) 1000.0);
3668 tempo_copy->set_note_types_per_minute (new_bpm);
3669 if (tempo_copy->type() == TempoSection::Constant) {
3670 tempo_copy->set_end_note_types_per_minute (new_bpm);
3673 recompute_tempi (future_map);
3675 if (check_solved (future_map)) {
3681 ts->set_note_types_per_minute (new_bpm);
3682 if (ts->type() == TempoSection::Constant) {
3683 ts->set_end_note_types_per_minute (new_bpm);
3686 recompute_map (_metrics);
3691 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3692 if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) {
3694 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3695 / (double) ((old_next_to_next_minute) - old_next_minute));
3698 new_next_bpm = next_t->note_types_per_minute();
3701 next_t->set_note_types_per_minute (new_next_bpm);
3702 recompute_tempi (future_map);
3704 if (check_solved (future_map)) {
3705 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3706 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3707 next_t = static_cast<TempoSection*> (*i);
3715 next_t->set_note_types_per_minute (new_next_bpm);
3716 recompute_map (_metrics);
3720 double next_frame_ratio = 1.0;
3721 double copy_frame_ratio = 1.0;
3723 if (next_to_next_t) {
3724 next_frame_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute - old_next_minute);
3726 copy_frame_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute));
3729 new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
3730 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
3732 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3734 if (next_t->clamped()) {
3735 next_t->set_note_types_per_minute (new_copy_end_bpm);
3737 next_t->set_note_types_per_minute (new_next_bpm);
3740 recompute_tempi (future_map);
3742 if (check_solved (future_map)) {
3743 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3744 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3745 next_t = static_cast<TempoSection*> (*i);
3754 if (next_t->clamped()) {
3755 next_t->set_note_types_per_minute (new_copy_end_bpm);
3757 next_t->set_note_types_per_minute (new_next_bpm);
3760 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3761 recompute_map (_metrics);
3767 Metrics::const_iterator d = future_map.begin();
3768 while (d != future_map.end()) {
3773 MetricPositionChanged (PropertyChange ()); // Emit Signal
3778 /** Returns the frame position of the musical position zero */
3780 TempoMap::music_origin ()
3782 Glib::Threads::RWLock::ReaderLock lm (lock);
3784 return first_tempo().frame();
3787 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3788 * the supplied frame, possibly returning a negative value.
3790 * @param frame The session frame position.
3791 * @param sub_num The subdivision to use when rounding the beat.
3792 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3793 * Positive integers indicate quarter note (non BBT) divisions.
3794 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3795 * @return The beat position of the supplied frame.
3797 * when working to a musical grid, the use of sub_nom indicates that
3798 * the position should be interpreted musically.
3800 * it effectively snaps to meter bars, meter beats or quarter note divisions
3801 * (as per current gui convention) and returns a musical position independent of frame rate.
3803 * If the supplied frame lies before the first meter, the return will be negative,
3804 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3805 * the continuation of the tempo curve (backwards).
3807 * This function is sensitive to tempo and meter.
3810 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3812 Glib::Threads::RWLock::ReaderLock lm (lock);
3814 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3818 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3820 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3823 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3824 * the supplied frame, possibly returning a negative value.
3826 * @param frame The session frame position.
3827 * @param sub_num The subdivision to use when rounding the quarter note.
3828 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3829 * Positive integers indicate quarter note (non BBT) divisions.
3830 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3831 * @return The quarter note position of the supplied frame.
3833 * When working to a musical grid, the use of sub_nom indicates that
3834 * the frame position should be interpreted musically.
3836 * it effectively snaps to meter bars, meter beats or quarter note divisions
3837 * (as per current gui convention) and returns a musical position independent of frame rate.
3839 * If the supplied frame lies before the first meter, the return will be negative,
3840 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3841 * the continuation of the tempo curve (backwards).
3843 * This function is tempo-sensitive.
3846 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3848 Glib::Threads::RWLock::ReaderLock lm (lock);
3850 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3854 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3856 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3859 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3860 } else if (sub_num == 1) {
3861 /* the gui requested exact musical (BBT) beat */
3862 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3863 } else if (sub_num == -1) {
3865 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3869 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3871 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3873 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3883 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3884 * @param pos the frame position in the tempo map.
3885 * @param bbt the distance in BBT time from pos to calculate.
3886 * @param dir the rounding direction..
3887 * @return the duration in frames between pos and bbt
3890 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3892 Glib::Threads::RWLock::ReaderLock lm (lock);
3894 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3896 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3899 pos_bbt.bars += bbt.bars;
3901 pos_bbt.ticks += bbt.ticks;
3902 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3904 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3907 pos_bbt.beats += bbt.beats;
3908 if ((double) pos_bbt.beats > divisions) {
3910 pos_bbt.beats -= divisions;
3912 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3914 return pos_bbt_frame - pos;
3918 if (pos_bbt.bars <= bbt.bars) {
3921 pos_bbt.bars -= bbt.bars;
3924 if (pos_bbt.ticks < bbt.ticks) {
3925 if (pos_bbt.bars > 1) {
3926 if (pos_bbt.beats == 1) {
3928 pos_bbt.beats = divisions;
3932 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3938 pos_bbt.ticks -= bbt.ticks;
3941 if (pos_bbt.beats <= bbt.beats) {
3942 if (pos_bbt.bars > 1) {
3944 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3949 pos_bbt.beats -= bbt.beats;
3952 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3959 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3961 return round_to_type (fr, dir, Bar);
3965 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3967 return round_to_type (fr, dir, Beat);
3971 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3973 Glib::Threads::RWLock::ReaderLock lm (lock);
3974 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);
3975 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3976 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3978 ticks -= beats * BBT_Time::ticks_per_beat;
3981 /* round to next (or same iff dir == RoundUpMaybe) */
3983 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3985 if (mod == 0 && dir == RoundUpMaybe) {
3986 /* right on the subdivision, which is fine, so do nothing */
3988 } else if (mod == 0) {
3989 /* right on the subdivision, so the difference is just the subdivision ticks */
3990 ticks += ticks_one_subdivisions_worth;
3993 /* not on subdivision, compute distance to next subdivision */
3995 ticks += ticks_one_subdivisions_worth - mod;
3998 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3999 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
4000 // And since the "prev" direction DOES move beats, I assume this code is unintended.
4001 // But I'm keeping it around, until we determine there are no terrible consequences.
4002 // if (ticks >= BBT_Time::ticks_per_beat) {
4003 // ticks -= BBT_Time::ticks_per_beat;
4006 } else if (dir < 0) {
4008 /* round to previous (or same iff dir == RoundDownMaybe) */
4010 uint32_t difference = ticks % ticks_one_subdivisions_worth;
4012 if (difference == 0 && dir == RoundDownAlways) {
4013 /* right on the subdivision, but force-rounding down,
4014 so the difference is just the subdivision ticks */
4015 difference = ticks_one_subdivisions_worth;
4018 if (ticks < difference) {
4019 ticks = BBT_Time::ticks_per_beat - ticks;
4021 ticks -= difference;
4025 /* round to nearest */
4028 /* compute the distance to the previous and next subdivision */
4030 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
4032 /* closer to the next subdivision, so shift forward */
4034 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
4036 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
4038 if (ticks > BBT_Time::ticks_per_beat) {
4040 ticks -= BBT_Time::ticks_per_beat;
4041 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
4044 } else if (rem > 0) {
4046 /* closer to previous subdivision, so shift backward */
4050 /* can't go backwards past zero, so ... */
4051 return MusicFrame (0, 0);
4053 /* step back to previous beat */
4055 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4056 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4058 ticks = lrint (ticks - rem);
4059 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4062 /* on the subdivision, do nothing */
4066 MusicFrame ret (0, 0);
4067 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4068 ret.division = sub_num;
4074 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
4076 Glib::Threads::RWLock::ReaderLock lm (lock);
4077 const double minute = minute_at_frame (frame);
4078 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4079 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
4080 MusicFrame ret (0, 0);
4087 /* find bar previous to 'frame' */
4093 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4097 } else if (dir > 0) {
4098 /* find bar following 'frame' */
4103 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4107 /* true rounding: find nearest bar */
4108 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4111 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4113 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4115 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4116 ret.frame = next_ft;
4121 ret.frame = prev_ft;
4133 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
4136 } else if (dir > 0) {
4137 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
4141 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
4148 return MusicFrame (0, 0);
4152 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4153 framepos_t lower, framepos_t upper, uint32_t bar_mod)
4155 Glib::Threads::RWLock::ReaderLock lm (lock);
4156 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
4158 /* although the map handles negative beats, bbt doesn't. */
4163 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
4167 while (pos >= 0 && pos < upper) {
4168 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
4169 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4170 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4171 const double qn = pulse_at_beat_locked (_metrics, cnt) * 4.0;
4173 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, qn));
4177 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
4182 bbt.bars -= bbt.bars % bar_mod;
4186 while (pos >= 0 && pos < upper) {
4187 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4188 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4189 const double qn = pulse_at_bbt_locked (_metrics, bbt) * 4.0;
4191 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, qn));
4192 bbt.bars += bar_mod;
4198 TempoMap::tempo_section_at_frame (framepos_t frame) const
4200 Glib::Threads::RWLock::ReaderLock lm (lock);
4202 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4206 TempoMap::tempo_section_at_frame (framepos_t frame)
4208 Glib::Threads::RWLock::ReaderLock lm (lock);
4210 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4214 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4216 TempoSection* prev = 0;
4220 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4222 if ((*i)->is_tempo()) {
4223 t = static_cast<TempoSection*> (*i);
4227 if (prev && t->minute() > minute) {
4237 abort(); /*NOTREACHED*/
4243 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4245 TempoSection* prev = 0;
4249 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4251 if ((*i)->is_tempo()) {
4252 t = static_cast<TempoSection*> (*i);
4256 if (prev && t->minute() > minute) {
4266 abort(); /*NOTREACHED*/
4272 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4274 TempoSection* prev_t = 0;
4275 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4279 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4280 if ((*i)->is_tempo()) {
4281 t = static_cast<TempoSection*> (*i);
4287 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4298 TempoMap::previous_tempo_section (TempoSection* ts) const
4300 Glib::Threads::RWLock::ReaderLock lm (lock);
4302 return previous_tempo_section_locked (_metrics, ts);
4307 TempoMap::previous_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4313 TempoSection* prev = 0;
4315 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4317 if ((*i)->is_tempo()) {
4318 TempoSection* t = static_cast<TempoSection*> (*i);
4324 if (prev && t == ts) {
4335 abort(); /*NOTREACHED*/
4342 TempoMap::next_tempo_section (TempoSection* ts) const
4344 Glib::Threads::RWLock::ReaderLock lm (lock);
4346 return next_tempo_section_locked (_metrics, ts);
4350 TempoMap::next_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4356 TempoSection* prev = 0;
4358 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4360 if ((*i)->is_tempo()) {
4361 TempoSection* t = static_cast<TempoSection*> (*i);
4367 if (prev && prev == ts) {
4378 abort(); /*NOTREACHED*/
4383 /* don't use this to calculate length (the tempo is only correct for this frame).
4384 do that stuff based on the beat_at_frame and frame_at_beat api
4387 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4389 Glib::Threads::RWLock::ReaderLock lm (lock);
4391 const TempoSection* ts_at = 0;
4392 const TempoSection* ts_after = 0;
4393 Metrics::const_iterator i;
4396 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4398 if ((*i)->is_tempo()) {
4399 t = static_cast<TempoSection*> (*i);
4403 if (ts_at && (*i)->frame() > frame) {
4413 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4415 /* must be treated as constant tempo */
4416 return ts_at->frames_per_quarter_note (_frame_rate);
4420 TempoMap::meter_section_at_frame (framepos_t frame) const
4422 Glib::Threads::RWLock::ReaderLock lm (lock);
4423 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4427 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4429 Metrics::const_iterator i;
4430 MeterSection* prev = 0;
4434 for (i = metrics.begin(); i != metrics.end(); ++i) {
4436 if (!(*i)->is_tempo()) {
4437 m = static_cast<MeterSection*> (*i);
4439 if (prev && (*i)->minute() > minute) {
4449 abort(); /*NOTREACHED*/
4456 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4458 MeterSection* prev_m = 0;
4460 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4462 if (!(*i)->is_tempo()) {
4463 m = static_cast<MeterSection*> (*i);
4464 if (prev_m && m->beat() > beat) {
4475 TempoMap::meter_section_at_beat (double beat) const
4477 Glib::Threads::RWLock::ReaderLock lm (lock);
4478 return meter_section_at_beat_locked (_metrics, beat);
4482 TempoMap::meter_at_frame (framepos_t frame) const
4484 TempoMetric m (metric_at (frame));
4489 TempoMap::fix_legacy_session ()
4491 MeterSection* prev_m = 0;
4492 TempoSection* prev_t = 0;
4493 bool have_initial_t = false;
4495 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4499 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4501 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4504 m->set_minute (0.0);
4505 m->set_position_lock_style (AudioTime);
4510 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4511 + (m->bbt().beats - 1)
4512 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4514 m->set_beat (start);
4515 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4516 + (m->bbt().beats - 1)
4517 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4518 m->set_pulse (start_beat / prev_m->note_divisor());
4521 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4526 /* Ramp type never existed in the era of this tempo section */
4527 t->set_end_note_types_per_minute (t->note_types_per_minute());
4531 t->set_minute (0.0);
4532 t->set_position_lock_style (AudioTime);
4534 have_initial_t = true;
4539 /* some 4.x sessions have no initial (non-movable) tempo. */
4540 if (!have_initial_t) {
4541 prev_t->set_pulse (0.0);
4542 prev_t->set_minute (0.0);
4543 prev_t->set_position_lock_style (AudioTime);
4544 prev_t->set_initial (true);
4545 prev_t->set_locked_to_meter (true);
4546 have_initial_t = true;
4549 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4550 + (t->legacy_bbt().beats - 1)
4551 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4553 t->set_pulse (beat / prev_m->note_divisor());
4555 /* really shouldn't happen but.. */
4556 t->set_pulse (beat / 4.0);
4564 TempoMap::fix_legacy_end_session ()
4566 TempoSection* prev_t = 0;
4568 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4571 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4578 if (prev_t->type() != TempoSection::Constant) {
4579 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4589 TempoMap::get_state ()
4591 Metrics::const_iterator i;
4592 XMLNode *root = new XMLNode ("TempoMap");
4595 Glib::Threads::RWLock::ReaderLock lm (lock);
4596 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4597 root->add_child_nocopy ((*i)->get_state());
4605 TempoMap::set_state (const XMLNode& node, int /*version*/)
4608 Glib::Threads::RWLock::WriterLock lm (lock);
4611 XMLNodeConstIterator niter;
4612 Metrics old_metrics (_metrics);
4615 nlist = node.children();
4617 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4618 XMLNode* child = *niter;
4620 if (child->name() == TempoSection::xml_state_node_name) {
4623 TempoSection* ts = new TempoSection (*child, _frame_rate);
4624 _metrics.push_back (ts);
4627 catch (failed_constructor& err){
4628 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4629 _metrics = old_metrics;
4630 old_metrics.clear();
4634 } else if (child->name() == MeterSection::xml_state_node_name) {
4637 MeterSection* ms = new MeterSection (*child, _frame_rate);
4638 _metrics.push_back (ms);
4641 catch (failed_constructor& err) {
4642 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4643 _metrics = old_metrics;
4644 old_metrics.clear();
4650 /* check for legacy sessions where bbt was the base musical unit for tempo */
4651 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4653 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4654 if (t->legacy_bbt().bars != 0) {
4655 fix_legacy_session();
4659 if (t->end_note_types_per_minute() < 0.0) {
4660 fix_legacy_end_session();
4668 if (niter == nlist.end()) {
4669 MetricSectionSorter cmp;
4670 _metrics.sort (cmp);
4673 /* check for multiple tempo/meters at the same location, which
4674 ardour2 somehow allowed.
4677 Metrics::iterator prev = _metrics.end();
4678 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4679 if (prev != _metrics.end()) {
4681 MeterSection* prev_m;
4683 TempoSection* prev_t;
4684 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4685 if (prev_m->beat() == ms->beat()) {
4686 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
4687 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
4690 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4691 if (prev_t->pulse() == ts->pulse()) {
4692 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4693 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4701 recompute_map (_metrics);
4703 Metrics::const_iterator d = old_metrics.begin();
4704 while (d != old_metrics.end()) {
4708 old_metrics.clear ();
4711 PropertyChanged (PropertyChange ());
4717 TempoMap::dump (std::ostream& o) const
4719 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4720 const MeterSection* m;
4721 const TempoSection* t;
4722 const TempoSection* prev_t = 0;
4724 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4726 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4727 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4728 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4729 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4730 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4732 o << " current start : " << t->note_types_per_minute()
4733 << " current end : " << t->end_note_types_per_minute()
4734 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4735 o << " previous : " << prev_t->note_types_per_minute()
4736 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4737 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4738 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4739 << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4740 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4743 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4744 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4745 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4746 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4749 o << "------" << std::endl;
4753 TempoMap::n_tempos() const
4755 Glib::Threads::RWLock::ReaderLock lm (lock);
4758 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4759 if ((*i)->is_tempo()) {
4768 TempoMap::n_meters() const
4770 Glib::Threads::RWLock::ReaderLock lm (lock);
4773 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4774 if (!(*i)->is_tempo()) {
4783 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4785 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4786 if ((*i)->frame() >= where && !(*i)->initial ()) {
4790 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4791 gui_set_meter_position (ms, (*i)->frame() + amount);
4794 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4795 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4800 PropertyChanged (PropertyChange ());
4804 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4808 std::list<MetricSection*> metric_kill_list;
4810 TempoSection* last_tempo = NULL;
4811 MeterSection* last_meter = NULL;
4812 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4813 bool meter_after = false; // is there a meter marker likewise?
4815 Glib::Threads::RWLock::WriterLock lm (lock);
4816 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4817 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4818 metric_kill_list.push_back(*i);
4819 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4822 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4826 else if ((*i)->frame() >= where) {
4827 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4828 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4829 if ((*i)->frame() == where) {
4830 // marker was immediately after end of range
4831 tempo_after = dynamic_cast<TempoSection*> (*i);
4832 meter_after = dynamic_cast<MeterSection*> (*i);
4838 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4839 if (last_tempo && !tempo_after) {
4840 metric_kill_list.remove(last_tempo);
4841 last_tempo->set_minute (minute_at_frame (where));
4844 if (last_meter && !meter_after) {
4845 metric_kill_list.remove(last_meter);
4846 last_meter->set_minute (minute_at_frame (where));
4850 //remove all the remaining metrics
4851 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4852 _metrics.remove(*i);
4857 recompute_map (_metrics);
4860 PropertyChanged (PropertyChange ());
4864 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4865 * pos can be -ve, if required.
4868 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4870 Glib::Threads::RWLock::ReaderLock lm (lock);
4871 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4873 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4877 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4879 Glib::Threads::RWLock::ReaderLock lm (lock);
4881 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4882 pos_bbt.ticks += op.ticks;
4883 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4885 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4887 pos_bbt.beats += op.beats;
4888 /* the meter in effect will start on the bar */
4889 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();
4890 while (pos_bbt.beats >= divisions_per_bar + 1) {
4892 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4893 pos_bbt.beats -= divisions_per_bar;
4895 pos_bbt.bars += op.bars;
4897 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4900 /** Count the number of beats that are equivalent to distance when going forward,
4904 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4906 Glib::Threads::RWLock::ReaderLock lm (lock);
4908 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4912 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4918 operator<< (std::ostream& o, const Meter& m) {
4919 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4923 operator<< (std::ostream& o, const Tempo& t) {
4924 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4928 operator<< (std::ostream& o, const MetricSection& section) {
4930 o << "MetricSection @ " << section.frame() << ' ';
4932 const TempoSection* ts;
4933 const MeterSection* ms;
4935 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4936 o << *((const Tempo*) ts);
4937 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4938 o << *((const Meter*) ms);