2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
35 #include "ardour/types_convert.h"
41 using namespace ARDOUR;
44 using Timecode::BBT_Time;
46 /* _default tempo is 4/4 qtr=120 */
48 Meter TempoMap::_default_meter (4.0, 4.0);
49 Tempo TempoMap::_default_tempo (120.0, 4.0, 120.0);
52 MetricSection::frame_at_minute (const double& time) const
54 return (framepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
58 MetricSection::minute_at_frame (const framepos_t& frame) const
60 return (frame / (double) _sample_rate) / 60.0;
63 /***********************************************************************/
66 ARDOUR::bbt_time_to_string (const BBT_Time& bbt, std::string& str)
69 int retval = snprintf (buf, sizeof(buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, bbt.bars, bbt.beats,
72 if (retval <= 0 || retval >= (int)sizeof(buf)) {
81 ARDOUR::string_to_bbt_time (const std::string& str, BBT_Time& bbt)
83 if (sscanf (str.c_str (), "%" PRIu32 "|%" PRIu32 "|%" PRIu32, &bbt.bars, &bbt.beats,
91 /***********************************************************************/
94 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
96 /* This is tempo- and meter-sensitive. The number it returns
97 is based on the interval between any two lines in the
98 grid that is constructed from tempo and meter sections.
100 The return value IS NOT interpretable in terms of "beats".
103 return (60.0 * sr) / (tempo.note_types_per_minute() * (_note_type / tempo.note_type()));
107 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
109 return frames_per_grid (tempo, sr) * _divisions_per_bar;
112 /***********************************************************************/
115 MetricSection::add_state_to_node(XMLNode& node) const
117 node.set_property ("pulse", _pulse);
118 node.set_property ("frame", frame());
119 node.set_property ("movable", !_initial);
120 node.set_property ("lock-style", _position_lock_style);
124 MetricSection::set_state (const XMLNode& node, int /*version*/)
126 node.get_property ("pulse", _pulse);
129 if (node.get_property ("frame", frame)) {
130 set_minute (minute_at_frame (frame));
134 if (!node.get_property ("movable", tmp)) {
135 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
136 throw failed_constructor();
140 if (!node.get_property ("lock-style", _position_lock_style)) {
142 _position_lock_style = MusicTime;
144 _position_lock_style = AudioTime;
150 /***********************************************************************/
152 const string TempoSection::xml_state_node_name = "Tempo";
154 TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate)
155 : MetricSection (0.0, 0, MusicTime, true, sample_rate)
156 , Tempo (TempoMap::default_tempo())
159 , _locked_to_meter (false)
162 _legacy_bbt = BBT_Time (0, 0, 0);
165 std::string start_bbt;
166 if (node.get_property ("start", start_bbt)) {
167 if (string_to_bbt_time (start_bbt, bbt)) {
168 /* legacy session - start used to be in bbt*/
171 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
175 // Don't worry about return value, exception will be thrown on error
176 MetricSection::set_state (node, Stateful::loading_state_version);
178 if (node.get_property ("beats-per-minute", _note_types_per_minute)) {
179 if (_note_types_per_minute < 0.0) {
180 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
181 throw failed_constructor();
185 if (node.get_property ("note-type", _note_type)) {
186 if (_note_type < 1.0) {
187 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
188 throw failed_constructor();
191 /* older session, make note type be quarter by default */
195 if (!node.get_property ("clamped", _clamped)) {
199 if (node.get_property ("end-beats-per-minute", _end_note_types_per_minute)) {
200 if (_end_note_types_per_minute < 0.0) {
201 info << _("TempoSection XML node has an illegal \"end-beats-per-minute\" value") << endmsg;
202 throw failed_constructor();
206 TempoSection::Type old_type;
207 if (node.get_property ("tempo-type", old_type)) {
208 /* sessions with a tempo-type node contain no end-beats-per-minute.
209 if the legacy node indicates a constant tempo, simply fill this in with the
210 start tempo. otherwise we need the next neighbour to know what it will be.
213 if (old_type == TempoSection::Constant) {
214 _end_note_types_per_minute = _note_types_per_minute;
216 _end_note_types_per_minute = -1.0;
220 if (!node.get_property ("active", _active)) {
221 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
225 if (!node.get_property ("locked-to-meter", _locked_to_meter)) {
227 set_locked_to_meter (true);
229 set_locked_to_meter (false);
233 /* 5.5 marked initial tempo as not locked to meter. this should always be true anyway */
235 set_locked_to_meter (true);
240 TempoSection::get_state() const
242 XMLNode *root = new XMLNode (xml_state_node_name);
244 MetricSection::add_state_to_node (*root);
246 root->set_property ("beats-per-minute", _note_types_per_minute);
247 root->set_property ("note-type", _note_type);
248 root->set_property ("clamped", _clamped);
249 root->set_property ("end-beats-per-minute", _end_note_types_per_minute);
250 root->set_property ("active", _active);
251 root->set_property ("locked-to-meter", _locked_to_meter);
256 /** returns the Tempo at the session-relative minute.
259 TempoSection::tempo_at_minute (const double& m) const
261 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
263 return Tempo (note_types_per_minute(), note_type());
266 return Tempo (_tempo_at_time (m - minute()), _note_type, _end_note_types_per_minute);
269 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
270 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
271 * @param p the pulse used to calculate the returned minute for constant tempi
272 * @return the minute at the supplied tempo
274 * note that the note_type is currently ignored in this function. see below.
278 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
279 * there should be no ramp between the two even if we are ramped.
280 * in other words a ramp should only place a curve on note_types_per_minute.
281 * we should be able to use Tempo note type here, but the above
282 * complicates things a bit.
285 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
287 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
289 return ((p - pulse()) / pulses_per_minute()) + minute();
292 return _time_at_tempo (ntpm) + minute();
295 /** returns the Tempo at the supplied whole-note pulse.
298 TempoSection::tempo_at_pulse (const double& p) const
300 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
303 return Tempo (note_types_per_minute(), note_type());
306 return Tempo (_tempo_at_pulse (p - pulse()), _note_type, _end_note_types_per_minute);
309 /** returns the whole-note pulse where a tempo in note types per minute occurs.
310 * constant tempi require minute m.
311 * @param ntpm the note types per minute value used to calculate the returned pulse
312 * @param m the minute used to calculate the returned pulse if the tempo is constant
313 * @return the whole-note pulse at the supplied tempo
315 * note that note_type is currently ignored in this function. see minute_at_tempo().
317 * for constant tempi, this is anaologous to pulse_at_minute().
320 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
322 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
324 return ((m - minute()) * pulses_per_minute()) + pulse();
327 return _pulse_at_tempo (ntpm) + pulse();
330 /** returns the whole-note pulse at the supplied session-relative minute.
333 TempoSection::pulse_at_minute (const double& m) const
335 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
337 return ((m - minute()) * pulses_per_minute()) + pulse();
340 return _pulse_at_time (m - minute()) + pulse();
343 /** returns the session-relative minute at the supplied whole-note pulse.
346 TempoSection::minute_at_pulse (const double& p) const
348 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
350 return ((p - pulse()) / pulses_per_minute()) + minute();
353 return _time_at_pulse (p - pulse()) + minute();
356 /** returns thw whole-note pulse at session frame position f.
357 * @param f the frame position.
358 * @return the position in whole-note pulses corresponding to f
360 * for use with musical units whose granularity is coarser than frames (e.g. ticks)
363 TempoSection::pulse_at_frame (const framepos_t& f) const
365 const bool constant = type() == Constant || _c == 0.0 || (initial() && f < frame());
367 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
370 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
374 TempoSection::frame_at_pulse (const double& p) const
376 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
378 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
381 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
389 Tt----|-----------------*|
390 Ta----|--------------|* |
396 _______________|___|____
397 time a t (next tempo)
400 Duration in beats at time a is the integral of some Tempo function.
401 In our case, the Tempo function (Tempo at time t) is
404 with function constant
409 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
410 b(t) = T0(e^(ct) - 1) / c
412 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
413 t(b) = log((c.b / T0) + 1) / c
415 The time t at which Tempo T occurs is a as above:
416 t(T) = log(T / T0) / c
418 The beat at which a Tempo T occurs is:
421 The Tempo at which beat b occurs is:
424 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
425 Our problem is that we usually don't know t.
426 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
427 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
428 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
430 By substituting our expanded t as a in the c function above, our problem is reduced to:
431 c = T0 (e^(log (Ta / T0)) - 1) / b
433 Of course the word 'beat' has been left loosely defined above.
434 In music, a beat is defined by the musical pulse (which comes from the tempo)
435 and the meter in use at a particular time (how many pulse divisions there are in one bar).
436 It would be more accurate to substitute the work 'pulse' for 'beat' above.
440 We can now store c for future time calculations.
441 If the following tempo section (the one that defines c in conjunction with this one)
442 is changed or moved, c is no longer valid.
444 The public methods are session-relative.
446 Most of this stuff is taken from this paper:
449 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
452 Zurich University of Arts
453 Institute for Computer Music and Sound Technology
455 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
459 /** compute this ramp's function constant from some tempo-pulse point
460 * @param end_npm end tempo (in note types per minute)
461 * @param end_pulse duration (pulses into global start) of some other position.
462 * @return the calculated function constant
465 TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const
467 if (note_types_per_minute() == end_npm || type() == Constant) {
471 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
472 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
475 /** compute the function constant from some tempo-time point.
476 * @param end_npm tempo (note types/min.)
477 * @param end_minute distance (in minutes) from session origin
478 * @return the calculated function constant
481 TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const
483 if (note_types_per_minute() == end_npm || type() == Constant) {
487 return c_func (end_npm, end_minute - minute());
490 /* position function */
492 TempoSection::a_func (double end_npm, double c) const
494 return log (end_npm / note_types_per_minute()) / c;
497 /*function constant*/
499 TempoSection::c_func (double end_npm, double end_time) const
501 return log (end_npm / note_types_per_minute()) / end_time;
504 /* tempo in note types per minute at time in minutes */
506 TempoSection::_tempo_at_time (const double& time) const
508 return exp (_c * time) * note_types_per_minute();
511 /* time in minutes at tempo in note types per minute */
513 TempoSection::_time_at_tempo (const double& npm) const
515 return log (npm / note_types_per_minute()) / _c;
518 /* pulse at tempo in note types per minute */
520 TempoSection::_pulse_at_tempo (const double& npm) const
522 return ((npm - note_types_per_minute()) / _c) / _note_type;
525 /* tempo in note types per minute at pulse */
527 TempoSection::_tempo_at_pulse (const double& pulse) const
529 return (pulse * _note_type * _c) + note_types_per_minute();
532 /* pulse at time in minutes */
534 TempoSection::_pulse_at_time (const double& time) const
536 return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
539 /* time in minutes at pulse */
541 TempoSection::_time_at_pulse (const double& pulse) const
543 return log1p ((_c * pulse * _note_type) / note_types_per_minute()) / _c;
546 /***********************************************************************/
548 const string MeterSection::xml_state_node_name = "Meter";
550 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
551 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
553 pair<double, BBT_Time> start;
557 if (node.get_property ("start", bbt_str)) {
558 if (string_to_bbt_time (bbt_str, start.second)) {
559 /* legacy session - start used to be in bbt*/
560 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
563 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
567 MetricSection::set_state (node, Stateful::loading_state_version);
569 node.get_property ("beat", start.first);
571 if (node.get_property ("bbt", bbt_str)) {
572 if (!string_to_bbt_time (bbt_str, start.second)) {
573 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
574 throw failed_constructor();
577 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
582 /* beats-per-bar is old; divisions-per-bar is new */
584 if (!node.get_property ("divisions-per-bar", _divisions_per_bar)) {
585 if (!node.get_property ("beats-per-bar", _divisions_per_bar)) {
586 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
587 throw failed_constructor();
591 if (_divisions_per_bar < 0.0) {
592 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
593 throw failed_constructor();
596 if (!node.get_property ("note-type", _note_type)) {
597 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
598 throw failed_constructor();
601 if (_note_type < 0.0) {
602 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
603 throw failed_constructor();
608 MeterSection::get_state() const
610 XMLNode *root = new XMLNode (xml_state_node_name);
612 MetricSection::add_state_to_node (*root);
615 bbt_time_to_string (_bbt, bbt_str);
616 root->set_property ("bbt", bbt_str);
617 root->set_property ("beat", beat());
618 root->set_property ("note-type", _note_type);
619 root->set_property ("divisions-per-bar", _divisions_per_bar);
624 /***********************************************************************/
628 Tempo determines the rate of musical pulse determined by its components
629 note types per minute - the rate per minute of the whole note divisor _note_type
630 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
631 Meter divides the musical pulse into measures and beats according to its components
635 TempoSection - translates between time, musical pulse and tempo.
636 has a musical location in whole notes (pulses).
637 has a time location in minutes.
638 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
639 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
641 MeterSection - translates between BBT, meter-based beat and musical pulse.
642 has a musical location in whole notes (pulses)
643 has a musical location in meter-based beats
644 has a musical location in BBT time
645 has a time location expressed in minutes.
647 TempoSection and MeterSection may be locked to either audio or music (position lock style).
648 The lock style determines the location type to be kept as a reference when location is recalculated.
650 The first tempo and meter are special. they must move together, and are locked to audio.
651 Audio locked tempi which lie before the first meter are made inactive.
653 Recomputing the map is the process where the 'missing' location types are calculated.
654 We construct the tempo map by first using the locked location type of each section
655 to determine non-locked location types (pulse or minute position).
656 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
658 Having done this, we can now traverse the Metrics list by pulse or minute
659 to query its relevant meter/tempo.
661 It is important to keep the _metrics in an order that makes sense.
662 Because ramped MusicTime and AudioTime tempos can interact with each other,
663 reordering is frequent. Care must be taken to keep _metrics in a solved state.
664 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
668 Music and audio-locked objects may seem interchangeable on the surface, but when translating
669 between audio samples and beat, remember that a sample is only a quantised approximation
670 of the actual time (in minutes) of a beat.
671 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
672 mean that this frame is the actual location in time of 1|3|0.
674 You cannot use a frame measurement to determine beat distance except under special circumstances
675 (e.g. where the user has requested that a beat lie on a SMPTE frame or if the tempo is known to be constant over the duration).
677 This means is that a user operating on a musical grid must supply the desired beat position and/or current beat quantization in order for the
678 sample space the user is operating at to be translated correctly to the object.
680 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
681 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
682 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
684 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
686 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
687 The result is rounded to audio frames.
688 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
691 frame_at_beat (beat_at_frame (frame)) == frame
693 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
695 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
696 frames_between_quarter_notes () eliminats this effect when determining time duration
697 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
699 The above pointless example could instead do:
700 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
702 The Shaggs - Things I Wonder
703 https://www.youtube.com/watch?v=9wQK6zMJOoQ
706 struct MetricSectionSorter {
707 bool operator() (const MetricSection* a, const MetricSection* b) {
708 return a->pulse() < b->pulse();
712 struct MetricSectionFrameSorter {
713 bool operator() (const MetricSection* a, const MetricSection* b) {
714 return a->frame() < b->frame();
718 TempoMap::TempoMap (framecnt_t fr)
721 BBT_Time start (1, 1, 0);
723 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo, AudioTime, fr);
724 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
726 t->set_initial (true);
727 t->set_locked_to_meter (true);
729 m->set_initial (true);
731 /* note: frame time is correct (zero) for both of these */
733 _metrics.push_back (t);
734 _metrics.push_back (m);
739 TempoMap::operator= (TempoMap const & other)
741 if (&other != this) {
742 Glib::Threads::RWLock::ReaderLock lr (other.lock);
743 Glib::Threads::RWLock::WriterLock lm (lock);
744 _frame_rate = other._frame_rate;
746 Metrics::const_iterator d = _metrics.begin();
747 while (d != _metrics.end()) {
753 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
754 TempoSection const * const ts = dynamic_cast<TempoSection const * const> (*m);
755 MeterSection const * const ms = dynamic_cast<MeterSection const * const> (*m);
758 TempoSection* new_section = new TempoSection (*ts);
759 _metrics.push_back (new_section);
761 MeterSection* new_section = new MeterSection (*ms);
762 _metrics.push_back (new_section);
767 PropertyChanged (PropertyChange());
772 TempoMap::~TempoMap ()
774 Metrics::const_iterator d = _metrics.begin();
775 while (d != _metrics.end()) {
783 TempoMap::frame_at_minute (const double time) const
785 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
789 TempoMap::minute_at_frame (const framepos_t frame) const
791 return (frame / (double) _frame_rate) / 60.0;
795 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
797 bool removed = false;
800 Glib::Threads::RWLock::WriterLock lm (lock);
801 if ((removed = remove_tempo_locked (tempo))) {
802 if (complete_operation) {
803 recompute_map (_metrics);
808 if (removed && complete_operation) {
809 PropertyChanged (PropertyChange ());
814 TempoMap::remove_tempo_locked (const TempoSection& tempo)
818 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
819 if (dynamic_cast<TempoSection*> (*i) != 0) {
820 if (tempo.frame() == (*i)->frame()) {
821 if (!(*i)->initial()) {
834 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
836 bool removed = false;
839 Glib::Threads::RWLock::WriterLock lm (lock);
840 if ((removed = remove_meter_locked (tempo))) {
841 if (complete_operation) {
842 recompute_map (_metrics);
847 if (removed && complete_operation) {
848 PropertyChanged (PropertyChange ());
853 TempoMap::remove_meter_locked (const MeterSection& meter)
856 if (meter.position_lock_style() == AudioTime) {
857 /* remove meter-locked tempo */
858 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
860 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
861 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
870 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
871 if (dynamic_cast<MeterSection*> (*i) != 0) {
872 if (meter.frame() == (*i)->frame()) {
873 if (!(*i)->initial()) {
886 TempoMap::do_insert (MetricSection* section)
888 bool need_add = true;
889 /* we only allow new meters to be inserted on beat 1 of an existing
893 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
895 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
897 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
898 corrected.second.beats = 1;
899 corrected.second.ticks = 0;
900 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
901 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
902 m->bbt(), corrected.second) << endmsg;
903 //m->set_pulse (corrected);
907 /* Look for any existing MetricSection that is of the same type and
908 in the same bar as the new one, and remove it before adding
909 the new one. Note that this means that if we find a matching,
910 existing section, we can break out of the loop since we're
911 guaranteed that there is only one such match.
914 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
916 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
917 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
918 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
919 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
921 if (tempo && insert_tempo) {
924 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
925 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
927 if (tempo->initial()) {
929 /* can't (re)move this section, so overwrite
930 * its data content (but not its properties as
934 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
935 (*i)->set_position_lock_style (AudioTime);
944 } else if (meter && insert_meter) {
948 bool const ipm = insert_meter->position_lock_style() == MusicTime;
950 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
952 if (meter->initial()) {
954 /* can't (re)move this section, so overwrite
955 * its data content (but not its properties as
959 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
960 (*i)->set_position_lock_style (AudioTime);
970 /* non-matching types, so we don't care */
974 /* Add the given MetricSection, if we didn't just reset an existing
979 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
980 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
984 TempoSection* prev_t = 0;
986 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
987 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
988 bool const ipm = insert_meter->position_lock_style() == MusicTime;
991 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
995 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
999 prev_t = dynamic_cast<TempoSection*> (*i);
1002 } else if (insert_tempo) {
1003 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1004 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1007 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1008 const bool lm = insert_tempo->locked_to_meter();
1009 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1010 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1017 _metrics.insert (i, section);
1021 /* user supplies the exact pulse if pls == MusicTime */
1023 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1025 if (tempo.note_types_per_minute() <= 0.0) {
1026 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1030 TempoSection* ts = 0;
1032 Glib::Threads::RWLock::WriterLock lm (lock);
1033 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, false);
1035 recompute_map (_metrics);
1038 PropertyChanged (PropertyChange ());
1044 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1046 if (tempo.note_types_per_minute() <= 0.0) {
1047 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1051 bool const locked_to_meter = ts.locked_to_meter();
1052 bool const ts_clamped = ts.clamped();
1053 TempoSection* new_ts = 0;
1056 Glib::Threads::RWLock::WriterLock lm (lock);
1057 TempoSection& first (first_tempo());
1058 if (!ts.initial()) {
1059 if (locked_to_meter) {
1061 /* cannot move a meter-locked tempo section */
1062 *static_cast<Tempo*>(&ts) = tempo;
1063 recompute_map (_metrics);
1066 remove_tempo_locked (ts);
1067 new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter);
1068 new_ts->set_clamped (ts_clamped);
1070 if (new_ts && new_ts->type() == TempoSection::Constant) {
1071 new_ts->set_end_note_types_per_minute (new_ts->note_types_per_minute());
1076 first.set_pulse (0.0);
1077 first.set_minute (minute_at_frame (frame));
1078 first.set_position_lock_style (AudioTime);
1079 first.set_locked_to_meter (true);
1080 first.set_clamped (ts_clamped);
1082 /* cannot move the first tempo section */
1083 *static_cast<Tempo*>(&first) = tempo;
1086 recompute_map (_metrics);
1089 PropertyChanged (PropertyChange ());
1093 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1094 , PositionLockStyle pls, bool recompute, bool locked_to_meter)
1096 TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _frame_rate);
1097 t->set_locked_to_meter (locked_to_meter);
1101 TempoSection* prev_tempo = 0;
1102 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1103 TempoSection* const this_t = dynamic_cast<TempoSection*>(*i);
1105 bool const ipm = t->position_lock_style() == MusicTime;
1106 bool const lm = t->locked_to_meter();
1108 if ((ipm && this_t->pulse() == t->pulse()) || (!ipm && this_t->frame() == t->frame())
1109 || (lm && this_t->pulse() == t->pulse())) {
1110 if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1111 prev_tempo->set_end_note_types_per_minute (t->note_types_per_minute());
1115 prev_tempo = this_t;
1120 if (pls == AudioTime) {
1121 solve_map_minute (_metrics, t, t->minute());
1123 solve_map_pulse (_metrics, t, t->pulse());
1125 recompute_meters (_metrics);
1132 TempoMap::add_meter (const Meter& meter, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1134 MeterSection* m = 0;
1136 Glib::Threads::RWLock::WriterLock lm (lock);
1137 m = add_meter_locked (meter, where, frame, pls, true);
1142 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1147 PropertyChanged (PropertyChange ());
1152 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1155 Glib::Threads::RWLock::WriterLock lm (lock);
1157 if (!ms.initial()) {
1158 remove_meter_locked (ms);
1159 add_meter_locked (meter, where, frame, pls, true);
1161 MeterSection& first (first_meter());
1162 TempoSection& first_t (first_tempo());
1163 /* cannot move the first meter section */
1164 *static_cast<Meter*>(&first) = meter;
1165 first.set_position_lock_style (AudioTime);
1166 first.set_pulse (0.0);
1167 first.set_minute (minute_at_frame (frame));
1168 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1169 first.set_beat (beat);
1170 first_t.set_minute (first.minute());
1171 first_t.set_locked_to_meter (true);
1172 first_t.set_pulse (0.0);
1173 first_t.set_position_lock_style (AudioTime);
1174 recompute_map (_metrics);
1178 PropertyChanged (PropertyChange ());
1182 TempoMap::add_meter_locked (const Meter& meter, const BBT_Time& bbt, framepos_t frame, PositionLockStyle pls, bool recompute)
1184 double const minute_at_bbt = minute_at_bbt_locked (_metrics, bbt);
1185 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_bbt - minute_at_frame (1));
1186 double const pulse = ((bbt.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1187 /* the natural time of the BBT position */
1188 double const time_minutes = minute_at_pulse_locked (_metrics, pulse);
1190 if (pls == AudioTime) {
1191 /* add meter-locked tempo at the natural time in the current map (frame may differ). */
1192 Tempo const tempo_at_time = tempo_at_minute_locked (_metrics, time_minutes);
1193 TempoSection* mlt = add_tempo_locked (tempo_at_time, pulse, time_minutes, AudioTime, true, true);
1199 /* still using natural time for the position, ignoring lock style. */
1200 MeterSection* new_meter = new MeterSection (pulse, time_minutes, beat_at_bbt_locked (_metrics, bbt), bbt, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1202 bool solved = false;
1204 do_insert (new_meter);
1208 if (pls == AudioTime) {
1209 /* now set the audio locked meter's position to frame */
1210 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1211 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1212 fudge frame so that the meter ends up at its BBT position instead.
1215 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1218 solved = solve_map_bbt (_metrics, new_meter, bbt);
1219 /* required due to resetting the pulse of meter-locked tempi above.
1220 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1221 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1223 recompute_map (_metrics);
1227 if (!solved && recompute) {
1228 /* if this has failed to solve, there is little we can do other than to ensure that
1229 the new map is recalculated.
1231 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1232 recompute_map (_metrics);
1239 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1241 Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1244 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1245 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1250 Glib::Threads::RWLock::WriterLock lm (lock);
1251 *((Tempo*) t) = newtempo;
1252 recompute_map (_metrics);
1254 PropertyChanged (PropertyChange ());
1261 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1263 Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1266 TempoSection* first;
1267 Metrics::iterator i;
1269 /* find the TempoSection immediately preceding "where"
1272 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1274 if ((*i)->frame() > where) {
1280 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1293 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1303 Glib::Threads::RWLock::WriterLock lm (lock);
1304 /* cannot move the first tempo section */
1305 *((Tempo*)prev) = newtempo;
1306 recompute_map (_metrics);
1309 PropertyChanged (PropertyChange ());
1313 TempoMap::first_meter () const
1315 const MeterSection *m = 0;
1317 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1318 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1323 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1324 abort(); /*NOTREACHED*/
1329 TempoMap::first_meter ()
1331 MeterSection *m = 0;
1333 /* CALLER MUST HOLD LOCK */
1335 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1336 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1341 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1342 abort(); /*NOTREACHED*/
1347 TempoMap::first_tempo () const
1349 const TempoSection *t = 0;
1351 /* CALLER MUST HOLD LOCK */
1353 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1354 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1364 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1365 abort(); /*NOTREACHED*/
1370 TempoMap::first_tempo ()
1372 TempoSection *t = 0;
1374 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1375 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1385 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1386 abort(); /*NOTREACHED*/
1390 TempoMap::recompute_tempi (Metrics& metrics)
1392 TempoSection* prev_t = 0;
1394 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1397 if ((*i)->is_tempo()) {
1398 t = static_cast<TempoSection*> (*i);
1410 if (t->position_lock_style() == AudioTime) {
1411 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1412 if (!t->locked_to_meter()) {
1413 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1417 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1418 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1426 prev_t->set_c (0.0);
1429 /* tempos must be positioned correctly.
1430 the current approach is to use a meter's bbt time as its base position unit.
1431 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1432 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1435 TempoMap::recompute_meters (Metrics& metrics)
1437 MeterSection* meter = 0;
1438 MeterSection* prev_m = 0;
1440 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1441 if (!(*mi)->is_tempo()) {
1442 meter = static_cast<MeterSection*> (*mi);
1443 if (meter->position_lock_style() == AudioTime) {
1445 pair<double, BBT_Time> b_bbt;
1446 TempoSection* meter_locked_tempo = 0;
1447 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1449 if ((*ii)->is_tempo()) {
1450 t = static_cast<TempoSection*> (*ii);
1451 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1452 meter_locked_tempo = t;
1459 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1460 if (beats + prev_m->beat() != meter->beat()) {
1461 /* reordering caused a bbt change */
1463 beats = meter->beat() - prev_m->beat();
1464 b_bbt = make_pair (beats + prev_m->beat()
1465 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1466 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1468 } else if (!meter->initial()) {
1469 b_bbt = make_pair (meter->beat(), meter->bbt());
1470 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1473 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1475 if (meter_locked_tempo) {
1476 meter_locked_tempo->set_pulse (pulse);
1478 meter->set_beat (b_bbt);
1479 meter->set_pulse (pulse);
1484 pair<double, BBT_Time> b_bbt;
1486 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1487 if (beats + prev_m->beat() != meter->beat()) {
1488 /* reordering caused a bbt change */
1489 b_bbt = make_pair (beats + prev_m->beat()
1490 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1492 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1494 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1496 /* shouldn't happen - the first is audio-locked */
1497 pulse = pulse_at_beat_locked (metrics, meter->beat());
1498 b_bbt = make_pair (meter->beat(), meter->bbt());
1501 meter->set_beat (b_bbt);
1502 meter->set_pulse (pulse);
1503 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1512 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1514 /* CALLER MUST HOLD WRITE LOCK */
1516 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1519 /* silly call from Session::process() during startup
1524 recompute_tempi (metrics);
1525 recompute_meters (metrics);
1529 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1531 Glib::Threads::RWLock::ReaderLock lm (lock);
1532 TempoMetric m (first_meter(), first_tempo());
1535 *last = ++_metrics.begin();
1538 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1539 at something, because we insert the default tempo and meter during
1540 TempoMap construction.
1542 now see if we can find better candidates.
1545 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1547 if ((*i)->frame() > frame) {
1561 /* XX meters only */
1563 TempoMap::metric_at (BBT_Time bbt) const
1565 Glib::Threads::RWLock::ReaderLock lm (lock);
1566 TempoMetric m (first_meter(), first_tempo());
1568 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1569 at something, because we insert the default tempo and meter during
1570 TempoMap construction.
1572 now see if we can find better candidates.
1575 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1577 if (!(*i)->is_tempo()) {
1578 mw = static_cast<MeterSection*> (*i);
1579 BBT_Time section_start (mw->bbt());
1581 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1592 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1593 * @param frame The session frame position.
1594 * @return The beat duration according to the tempo map at the supplied frame.
1596 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1597 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1599 * This function uses both tempo and meter.
1602 TempoMap::beat_at_frame (const framecnt_t& frame) const
1604 Glib::Threads::RWLock::ReaderLock lm (lock);
1606 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1609 /* This function uses both tempo and meter.*/
1611 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1613 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1614 MeterSection* prev_m = 0;
1615 MeterSection* next_m = 0;
1617 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1618 if (!(*i)->is_tempo()) {
1619 if (prev_m && (*i)->minute() > minute) {
1620 next_m = static_cast<MeterSection*> (*i);
1623 prev_m = static_cast<MeterSection*> (*i);
1627 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1629 /* audio locked meters fake their beat */
1630 if (next_m && next_m->beat() < beat) {
1631 return next_m->beat();
1637 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1638 * @param beat The BBT (meter-based) beat.
1639 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1641 * This function uses both tempo and meter.
1644 TempoMap::frame_at_beat (const double& beat) const
1646 Glib::Threads::RWLock::ReaderLock lm (lock);
1648 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1651 /* meter & tempo section based */
1653 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1655 MeterSection* prev_m = 0;
1656 TempoSection* prev_t = 0;
1660 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1661 if (!(*i)->is_tempo()) {
1662 m = static_cast<MeterSection*> (*i);
1663 if (prev_m && m->beat() > beat) {
1673 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1674 if ((*i)->is_tempo()) {
1675 t = static_cast<TempoSection*> (*i);
1681 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1690 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1693 /** Returns a Tempo corresponding to the supplied frame position.
1694 * @param frame The audio frame.
1695 * @return a Tempo according to the tempo map at the supplied frame.
1699 TempoMap::tempo_at_frame (const framepos_t& frame) const
1701 Glib::Threads::RWLock::ReaderLock lm (lock);
1703 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1707 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1709 TempoSection* prev_t = 0;
1713 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1714 if ((*i)->is_tempo()) {
1715 t = static_cast<TempoSection*> (*i);
1719 if ((prev_t) && t->minute() > minute) {
1720 /* t is the section past frame */
1721 return prev_t->tempo_at_minute (minute);
1727 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1730 /** returns the frame at which the supplied tempo occurs, or
1731 * the frame of the last tempo section (search exhausted)
1732 * only the position of the first occurence will be returned
1736 TempoMap::frame_at_tempo (const Tempo& tempo) const
1738 Glib::Threads::RWLock::ReaderLock lm (lock);
1740 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1744 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1746 TempoSection* prev_t = 0;
1747 const double tempo_bpm = tempo.note_types_per_minute();
1749 Metrics::const_iterator i;
1751 for (i = metrics.begin(); i != metrics.end(); ++i) {
1753 if ((*i)->is_tempo()) {
1754 t = static_cast<TempoSection*> (*i);
1762 if (t->note_types_per_minute() == tempo_bpm) {
1767 const double prev_t_bpm = prev_t->note_types_per_minute();
1768 const double prev_t_end_bpm = prev_t->end_note_types_per_minute();
1769 if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm)
1770 || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm)
1771 || (prev_t_end_bpm == tempo_bpm)) {
1773 return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
1780 return prev_t->minute();
1784 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1786 TempoSection* prev_t = 0;
1790 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1791 if ((*i)->is_tempo()) {
1792 t = static_cast<TempoSection*> (*i);
1796 if ((prev_t) && t->pulse() > pulse) {
1797 /* t is the section past frame */
1798 return prev_t->tempo_at_pulse (pulse);
1804 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1808 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1810 TempoSection* prev_t = 0;
1811 const double tempo_bpm = tempo.note_types_per_minute();
1813 Metrics::const_iterator i;
1815 for (i = metrics.begin(); i != metrics.end(); ++i) {
1817 if ((*i)->is_tempo()) {
1818 t = static_cast<TempoSection*> (*i);
1824 const double t_bpm = t->note_types_per_minute();
1826 if (t_bpm == tempo_bpm) {
1831 const double prev_t_bpm = prev_t->note_types_per_minute();
1833 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1834 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1841 return prev_t->pulse();
1844 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1845 * @param qn the position in quarter note beats.
1846 * @return the Tempo at the supplied quarter-note.
1849 TempoMap::tempo_at_quarter_note (const double& qn) const
1851 Glib::Threads::RWLock::ReaderLock lm (lock);
1853 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1856 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1857 * @param tempo the tempo.
1858 * @return the position in quarter-note beats where the map bpm
1859 * is equal to that of the Tempo. currently ignores note_type.
1862 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1864 Glib::Threads::RWLock::ReaderLock lm (lock);
1866 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;
1869 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1870 * @param metrics the list of metric sections used to calculate the pulse.
1871 * @param beat The BBT (meter-based) beat.
1872 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1874 * a pulse or whole note is the base musical position of a MetricSection.
1875 * it is equivalent to four quarter notes.
1879 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1881 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1883 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1886 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1887 * @param metrics the list of metric sections used to calculate the beat.
1888 * @param pulse the whole-note pulse.
1889 * @return the meter-based beat at the supplied whole-note pulse.
1891 * a pulse or whole note is the base musical position of a MetricSection.
1892 * it is equivalent to four quarter notes.
1895 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1897 MeterSection* prev_m = 0;
1899 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1901 if (!(*i)->is_tempo()) {
1902 m = static_cast<MeterSection*> (*i);
1903 if (prev_m && m->pulse() > pulse) {
1911 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1915 /* tempo section based */
1917 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1919 /* HOLD (at least) THE READER LOCK */
1920 TempoSection* prev_t = 0;
1922 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1924 if ((*i)->is_tempo()) {
1925 t = static_cast<TempoSection*> (*i);
1929 if (prev_t && t->minute() > minute) {
1930 /*the previous ts is the one containing the frame */
1931 const double ret = prev_t->pulse_at_minute (minute);
1932 /* audio locked section in new meter*/
1933 if (t->pulse() < ret) {
1942 /* treated as constant for this ts */
1943 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1945 return pulses_in_section + prev_t->pulse();
1948 /* tempo section based */
1950 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1952 /* HOLD THE READER LOCK */
1954 const TempoSection* prev_t = 0;
1956 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1959 if ((*i)->is_tempo()) {
1960 t = static_cast<TempoSection*> (*i);
1964 if (prev_t && t->pulse() > pulse) {
1965 return prev_t->minute_at_pulse (pulse);
1971 /* must be treated as constant, irrespective of _type */
1972 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1974 return dtime + prev_t->minute();
1977 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1978 * @param bbt The BBT time (meter-based).
1979 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1983 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1985 Glib::Threads::RWLock::ReaderLock lm (lock);
1986 return beat_at_bbt_locked (_metrics, bbt);
1991 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1993 /* CALLER HOLDS READ LOCK */
1995 MeterSection* prev_m = 0;
1997 /* because audio-locked meters have 'fake' integral beats,
1998 there is no pulse offset here.
2002 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2003 if (!(*i)->is_tempo()) {
2004 m = static_cast<MeterSection*> (*i);
2006 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2007 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2015 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2016 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2017 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2022 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2023 * @param beat The BBT (meter-based) beat.
2024 * @return The BBT time (meter-based) at the supplied meter-based beat.
2028 TempoMap::bbt_at_beat (const double& beat)
2030 Glib::Threads::RWLock::ReaderLock lm (lock);
2031 return bbt_at_beat_locked (_metrics, beat);
2035 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2037 /* CALLER HOLDS READ LOCK */
2038 MeterSection* prev_m = 0;
2039 const double beats = max (0.0, b);
2041 MeterSection* m = 0;
2043 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2044 if (!(*i)->is_tempo()) {
2045 m = static_cast<MeterSection*> (*i);
2047 if (m->beat() > beats) {
2048 /* this is the meter after the one our beat is on*/
2058 const double beats_in_ms = beats - prev_m->beat();
2059 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2060 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2061 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2062 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2066 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2067 ret.beats = (uint32_t) floor (remaining_beats);
2068 ret.bars = total_bars;
2070 /* 0 0 0 to 1 1 0 - based mapping*/
2074 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2076 ret.ticks -= BBT_Time::ticks_per_beat;
2079 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2087 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2088 * @param bbt The BBT time (meter-based).
2089 * @return the quarter note beat at the supplied BBT time
2091 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2093 * while the input uses meter, the output does not.
2096 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2098 Glib::Threads::RWLock::ReaderLock lm (lock);
2100 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2104 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2106 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2109 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2112 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2116 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2118 /* CALLER HOLDS READ LOCK */
2120 MeterSection* prev_m = 0;
2122 /* because audio-locked meters have 'fake' integral beats,
2123 there is no pulse offset here.
2127 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2128 if (!(*i)->is_tempo()) {
2129 m = static_cast<MeterSection*> (*i);
2131 if (m->bbt().bars > bbt.bars) {
2139 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2140 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2141 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2146 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2147 * @param qn the quarter-note beat.
2148 * @return The BBT time (meter-based) at the supplied meter-based beat.
2150 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2154 TempoMap::bbt_at_quarter_note (const double& qn)
2156 Glib::Threads::RWLock::ReaderLock lm (lock);
2158 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2161 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2162 * @param metrics The list of metric sections used to determine the result.
2163 * @param pulse The whole-note pulse.
2164 * @return The BBT time at the supplied whole-note pulse.
2166 * a pulse or whole note is the basic musical position of a MetricSection.
2167 * it is equivalent to four quarter notes.
2168 * while the output uses meter, the input does not.
2171 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2173 MeterSection* prev_m = 0;
2175 MeterSection* m = 0;
2177 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2179 if (!(*i)->is_tempo()) {
2180 m = static_cast<MeterSection*> (*i);
2183 double const pulses_to_m = m->pulse() - prev_m->pulse();
2184 if (prev_m->pulse() + pulses_to_m > pulse) {
2185 /* this is the meter after the one our beat is on*/
2196 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2197 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2198 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2199 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2200 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2204 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2205 ret.beats = (uint32_t) floor (remaining_beats);
2206 ret.bars = total_bars;
2208 /* 0 0 0 to 1 1 0 mapping*/
2212 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2214 ret.ticks -= BBT_Time::ticks_per_beat;
2217 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2225 /** Returns the BBT time corresponding to the supplied frame position.
2226 * @param frame the position in audio samples.
2227 * @return the BBT time at the frame position .
2231 TempoMap::bbt_at_frame (framepos_t frame)
2239 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2244 const double minute = minute_at_frame (frame);
2246 Glib::Threads::RWLock::ReaderLock lm (lock);
2248 return bbt_at_minute_locked (_metrics, minute);
2252 TempoMap::bbt_at_frame_rt (framepos_t frame)
2254 const double minute = minute_at_frame (frame);
2256 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2259 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2262 return bbt_at_minute_locked (_metrics, minute);
2266 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2276 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2277 MeterSection* prev_m = 0;
2278 MeterSection* next_m = 0;
2282 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2283 if (!(*i)->is_tempo()) {
2284 m = static_cast<MeterSection*> (*i);
2285 if (prev_m && m->minute() > minute) {
2293 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2295 /* handle frame before first meter */
2296 if (minute < prev_m->minute()) {
2299 /* audio locked meters fake their beat */
2300 if (next_m && next_m->beat() < beat) {
2301 beat = next_m->beat();
2304 beat = max (0.0, beat);
2306 const double beats_in_ms = beat - prev_m->beat();
2307 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2308 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2309 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2310 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2314 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2315 ret.beats = (uint32_t) floor (remaining_beats);
2316 ret.bars = total_bars;
2318 /* 0 0 0 to 1 1 0 - based mapping*/
2322 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2324 ret.ticks -= BBT_Time::ticks_per_beat;
2327 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2335 /** Returns the frame position corresponding to the supplied BBT time.
2336 * @param bbt the position in BBT time.
2337 * @return the frame position at bbt.
2341 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2345 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2350 if (bbt.beats < 1) {
2351 throw std::logic_error ("beats are counted from one");
2356 Glib::Threads::RWLock::ReaderLock lm (lock);
2357 minute = minute_at_bbt_locked (_metrics, bbt);
2360 return frame_at_minute (minute);
2363 /* meter & tempo section based */
2365 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2367 /* HOLD THE READER LOCK */
2369 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2374 * Returns the quarter-note beat position corresponding to the supplied frame.
2376 * @param frame the position in frames.
2377 * @return The quarter-note position of the supplied frame. Ignores meter.
2381 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2383 const double minute = minute_at_frame (frame);
2385 Glib::Threads::RWLock::ReaderLock lm (lock);
2387 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2391 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2393 const double minute = minute_at_frame (frame);
2395 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2398 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2401 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2405 * Returns the frame position corresponding to the supplied quarter-note beat.
2407 * @param quarter_note the quarter-note position.
2408 * @return the frame position of the supplied quarter-note. Ignores meter.
2413 TempoMap::frame_at_quarter_note (const double quarter_note) const
2417 Glib::Threads::RWLock::ReaderLock lm (lock);
2419 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2422 return frame_at_minute (minute);
2425 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2426 * @param beat The BBT (meter-based) beat.
2427 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2429 * a quarter-note may be compared with and assigned to Evoral::Beats.
2433 TempoMap::quarter_note_at_beat (const double beat) const
2435 Glib::Threads::RWLock::ReaderLock lm (lock);
2437 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2440 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2441 * @param quarter_note The position in quarter-note beats.
2442 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2444 * a quarter-note is the musical unit of Evoral::Beats.
2448 TempoMap::beat_at_quarter_note (const double quarter_note) const
2450 Glib::Threads::RWLock::ReaderLock lm (lock);
2452 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2455 /** Returns the duration in frames between two supplied quarter-note beat positions.
2456 * @param start the first position in quarter-note beats.
2457 * @param end the end position in quarter-note beats.
2458 * @return the frame distance ober the quarter-note beats duration.
2460 * use this rather than e.g.
2461 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2462 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2466 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2471 Glib::Threads::RWLock::ReaderLock lm (lock);
2472 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2475 return frame_at_minute (minutes);
2479 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2482 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2486 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2488 Glib::Threads::RWLock::ReaderLock lm (lock);
2490 return quarter_notes_between_frames_locked (_metrics, start, end);
2494 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2496 const TempoSection* prev_t = 0;
2498 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2501 if ((*i)->is_tempo()) {
2502 t = static_cast<TempoSection*> (*i);
2506 if (prev_t && t->frame() > start) {
2513 const double start_qn = prev_t->pulse_at_frame (start);
2515 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2518 if ((*i)->is_tempo()) {
2519 t = static_cast<TempoSection*> (*i);
2523 if (prev_t && t->frame() > end) {
2529 const double end_qn = prev_t->pulse_at_frame (end);
2531 return (end_qn - start_qn) * 4.0;
2535 TempoMap::check_solved (const Metrics& metrics) const
2537 TempoSection* prev_t = 0;
2538 MeterSection* prev_m = 0;
2540 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2543 if ((*i)->is_tempo()) {
2544 t = static_cast<TempoSection*> (*i);
2549 /* check ordering */
2550 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2554 /* precision check ensures tempo and frames align.*/
2555 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2556 if (!t->locked_to_meter()) {
2561 /* gradient limit - who knows what it should be?
2562 things are also ok (if a little chaotic) without this
2564 if (fabs (prev_t->c()) > 1000.0) {
2565 //std::cout << "c : " << prev_t->c() << std::endl;
2572 if (!(*i)->is_tempo()) {
2573 m = static_cast<MeterSection*> (*i);
2574 if (prev_m && m->position_lock_style() == AudioTime) {
2575 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2576 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2577 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2579 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2593 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2595 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2597 if ((*i)->is_tempo()) {
2598 t = static_cast<TempoSection*> (*i);
2599 if (t->locked_to_meter()) {
2600 t->set_active (true);
2601 } else if (t->position_lock_style() == AudioTime) {
2602 if (t->frame() < frame) {
2603 t->set_active (false);
2604 t->set_pulse (-1.0);
2605 } else if (t->frame() > frame) {
2606 t->set_active (true);
2607 } else if (t->frame() == frame) {
2617 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2619 TempoSection* prev_t = 0;
2620 TempoSection* section_prev = 0;
2621 double first_m_minute = 0.0;
2622 const bool sml = section->locked_to_meter();
2624 /* can't move a tempo before the first meter */
2625 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2627 if (!(*i)->is_tempo()) {
2628 m = static_cast<MeterSection*> (*i);
2630 first_m_minute = m->minute();
2635 if (!section->initial() && minute <= first_m_minute) {
2639 section->set_active (true);
2640 section->set_minute (minute);
2642 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2644 if ((*i)->is_tempo()) {
2645 t = static_cast<TempoSection*> (*i);
2657 if (t->frame() == frame_at_minute (minute)) {
2661 const bool tlm = t->position_lock_style() == MusicTime;
2663 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2664 section_prev = prev_t;
2666 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2667 if (!section->locked_to_meter()) {
2668 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2673 if (t->position_lock_style() == MusicTime) {
2674 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2675 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2677 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2678 if (!t->locked_to_meter()) {
2679 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2688 recompute_tempi (imaginary);
2690 if (check_solved (imaginary)) {
2693 dunp (imaginary, std::cout);
2697 MetricSectionFrameSorter fcmp;
2698 imaginary.sort (fcmp);
2700 recompute_tempi (imaginary);
2702 if (check_solved (imaginary)) {
2710 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2712 TempoSection* prev_t = 0;
2713 TempoSection* section_prev = 0;
2715 section->set_pulse (pulse);
2717 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2719 if ((*i)->is_tempo()) {
2720 t = static_cast<TempoSection*> (*i);
2731 section_prev = prev_t;
2735 if (t->position_lock_style() == MusicTime) {
2736 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2737 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2739 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2740 if (!t->locked_to_meter()) {
2741 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2750 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2751 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2755 recompute_tempi (imaginary);
2757 if (check_solved (imaginary)) {
2760 dunp (imaginary, std::cout);
2764 MetricSectionSorter cmp;
2765 imaginary.sort (cmp);
2767 recompute_tempi (imaginary);
2769 * XX need a restriction here, but only for this case,
2770 * as audio locked tempos don't interact in the same way.
2772 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2774 * |50 bpm |250 bpm |60 bpm
2775 * drag 250 to the pulse after 60->
2776 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2778 if (check_solved (imaginary)) {
2786 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2788 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2789 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2790 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2794 if (section->initial()) {
2795 /* lock the first tempo to our first meter */
2796 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2801 TempoSection* meter_locked_tempo = 0;
2803 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2805 if ((*ii)->is_tempo()) {
2806 t = static_cast<TempoSection*> (*ii);
2807 if (t->locked_to_meter() && t->frame() == section->frame()) {
2808 meter_locked_tempo = t;
2814 if (!meter_locked_tempo) {
2818 MeterSection* prev_m = 0;
2820 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2821 bool solved = false;
2823 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2825 if (!(*i)->is_tempo()) {
2826 m = static_cast<MeterSection*> (*i);
2828 if (prev_m && !section->initial()) {
2829 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2830 if (beats + prev_m->beat() < section->beat()) {
2831 /* set the section pulse according to its musical position,
2832 * as an earlier time than this has been requested.
2834 const double new_pulse = ((section->beat() - prev_m->beat())
2835 / prev_m->note_divisor()) + prev_m->pulse();
2837 tempo_copy->set_position_lock_style (MusicTime);
2838 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2839 meter_locked_tempo->set_position_lock_style (MusicTime);
2840 section->set_position_lock_style (MusicTime);
2841 section->set_pulse (new_pulse);
2842 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2843 meter_locked_tempo->set_position_lock_style (AudioTime);
2844 section->set_position_lock_style (AudioTime);
2845 section->set_minute (meter_locked_tempo->minute());
2851 Metrics::const_iterator d = future_map.begin();
2852 while (d != future_map.end()) {
2861 /* all is ok. set section's locked tempo if allowed.
2862 possibly disallow if there is an adjacent audio-locked tempo.
2863 XX this check could possibly go. its never actually happened here.
2865 MeterSection* meter_copy = const_cast<MeterSection*>
2866 (&meter_section_at_minute_locked (future_map, section->minute()));
2868 meter_copy->set_minute (minute);
2870 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2871 section->set_minute (minute);
2872 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2873 / prev_m->note_divisor()) + prev_m->pulse());
2874 solve_map_minute (imaginary, meter_locked_tempo, minute);
2879 Metrics::const_iterator d = future_map.begin();
2880 while (d != future_map.end()) {
2890 /* initial (first meter atm) */
2892 tempo_copy->set_minute (minute);
2893 tempo_copy->set_pulse (0.0);
2895 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2896 section->set_minute (minute);
2897 meter_locked_tempo->set_minute (minute);
2898 meter_locked_tempo->set_pulse (0.0);
2899 solve_map_minute (imaginary, meter_locked_tempo, minute);
2904 Metrics::const_iterator d = future_map.begin();
2905 while (d != future_map.end()) {
2914 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2915 section->set_beat (b_bbt);
2916 section->set_pulse (0.0);
2926 MetricSectionFrameSorter fcmp;
2927 imaginary.sort (fcmp);
2929 recompute_meters (imaginary);
2935 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2937 /* disallow setting section to an existing meter's bbt */
2938 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2940 if (!(*i)->is_tempo()) {
2941 m = static_cast<MeterSection*> (*i);
2942 if (m != section && m->bbt().bars == when.bars) {
2948 MeterSection* prev_m = 0;
2949 MeterSection* section_prev = 0;
2951 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2953 if (!(*i)->is_tempo()) {
2954 m = static_cast<MeterSection*> (*i);
2960 pair<double, BBT_Time> b_bbt;
2961 double new_pulse = 0.0;
2963 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2964 section_prev = prev_m;
2966 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2967 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2968 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2970 section->set_beat (b_bbt);
2971 section->set_pulse (pulse);
2972 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2976 if (m->position_lock_style() == AudioTime) {
2977 TempoSection* meter_locked_tempo = 0;
2979 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2981 if ((*ii)->is_tempo()) {
2982 t = static_cast<TempoSection*> (*ii);
2983 if (t->locked_to_meter() && t->frame() == m->frame()) {
2984 meter_locked_tempo = t;
2990 if (!meter_locked_tempo) {
2995 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2997 if (beats + prev_m->beat() != m->beat()) {
2998 /* tempo/ meter change caused a change in beat (bar). */
3000 /* the user has requested that the previous section of music overlaps this one.
3001 we have no choice but to change the bar number here, as being locked to audio means
3002 we must stay where we are on the timeline.
3004 beats = m->beat() - prev_m->beat();
3005 b_bbt = make_pair (beats + prev_m->beat()
3006 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3007 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3009 } else if (!m->initial()) {
3010 b_bbt = make_pair (m->beat(), m->bbt());
3011 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3014 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3017 meter_locked_tempo->set_pulse (new_pulse);
3018 m->set_beat (b_bbt);
3019 m->set_pulse (new_pulse);
3023 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3024 if (beats + prev_m->beat() != m->beat()) {
3025 /* tempo/ meter change caused a change in beat (bar). */
3026 b_bbt = make_pair (beats + prev_m->beat()
3027 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3029 b_bbt = make_pair (beats + prev_m->beat()
3032 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3033 m->set_beat (b_bbt);
3034 m->set_pulse (new_pulse);
3035 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3042 if (!section_prev) {
3044 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3045 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3046 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3048 section->set_beat (b_bbt);
3049 section->set_pulse (pulse);
3050 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3053 MetricSectionSorter cmp;
3054 imaginary.sort (cmp);
3056 recompute_meters (imaginary);
3061 /** places a copy of _metrics into copy and returns a pointer
3062 * to section's equivalent in copy.
3065 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section) const
3067 TempoSection* ret = 0;
3069 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3070 if ((*i)->is_tempo()) {
3071 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3073 ret = new TempoSection (*t);
3074 copy.push_back (ret);
3078 TempoSection* cp = new TempoSection (*t);
3079 copy.push_back (cp);
3081 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3082 MeterSection* cp = new MeterSection (*m);
3083 copy.push_back (cp);
3091 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section) const
3093 MeterSection* ret = 0;
3095 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3096 if ((*i)->is_tempo()) {
3097 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3098 TempoSection* cp = new TempoSection (*t);
3099 copy.push_back (cp);
3101 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3103 ret = new MeterSection (*m);
3104 copy.push_back (ret);
3107 MeterSection* cp = new MeterSection (*m);
3108 copy.push_back (cp);
3115 /** answers the question "is this a valid beat position for this tempo section?".
3116 * it returns true if the tempo section can be moved to the requested bbt position,
3117 * leaving the tempo map in a solved state.
3118 * @param ts the tempo section to be moved
3119 * @param bbt the requested new position for the tempo section
3120 * @return true if the tempo section can be moved to the position, otherwise false.
3123 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3126 TempoSection* tempo_copy = 0;
3129 Glib::Threads::RWLock::ReaderLock lm (lock);
3130 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3136 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3138 Metrics::const_iterator d = copy.begin();
3139 while (d != copy.end()) {
3148 * 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,
3149 * taking any possible reordering as a consequence of this into account.
3150 * @param section - the section to be altered
3151 * @param bbt - the BBT time where the altered tempo will fall
3152 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3154 pair<double, framepos_t>
3155 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3158 pair<double, framepos_t> ret = make_pair (0.0, 0);
3160 Glib::Threads::RWLock::ReaderLock lm (lock);
3162 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3164 const double beat = beat_at_bbt_locked (future_map, bbt);
3166 if (section->position_lock_style() == AudioTime) {
3167 tempo_copy->set_position_lock_style (MusicTime);
3170 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3171 ret.first = tempo_copy->pulse();
3172 ret.second = tempo_copy->frame();
3174 ret.first = section->pulse();
3175 ret.second = section->frame();
3178 Metrics::const_iterator d = future_map.begin();
3179 while (d != future_map.end()) {
3186 /** moves a TempoSection to a specified position.
3187 * @param ts - the section to be moved
3188 * @param frame - the new position in frames for the tempo
3189 * @param sub_num - the snap division to use if using musical time.
3191 * if sub_num is non-zero, the frame position is used to calculate an exact
3194 * -1 | snap to bars (meter-based)
3195 * 0 | no snap - use audio frame for musical position
3196 * 1 | snap to meter-based (BBT) beat
3197 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3199 * this follows the snap convention in the gui.
3200 * if sub_num is zero, the musical position will be taken from the supplied frame.
3203 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3207 if (ts->position_lock_style() == MusicTime) {
3209 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3210 Glib::Threads::RWLock::WriterLock lm (lock);
3211 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3213 tempo_copy->set_position_lock_style (AudioTime);
3215 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3216 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3217 const double pulse = pulse_at_beat_locked (future_map, beat);
3219 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3220 solve_map_pulse (_metrics, ts, pulse);
3221 recompute_meters (_metrics);
3229 Glib::Threads::RWLock::WriterLock lm (lock);
3230 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3234 /* We're moving the object that defines the grid while snapping to it...
3235 * Placing the ts at the beat corresponding to the requested frame may shift the
3236 * grid in such a way that the mouse is left hovering over a completerly different division,
3237 * causing jittering when the mouse next moves (esp. large tempo deltas).
3238 * We fudge around this by doing this in the musical domain and then swapping back for the recompute.
3240 const double qn = exact_qn_at_frame_locked (_metrics, frame, sub_num);
3241 tempo_copy->set_position_lock_style (MusicTime);
3242 if (solve_map_pulse (future_map, tempo_copy, qn / 4.0)) {
3243 ts->set_position_lock_style (MusicTime);
3244 solve_map_pulse (_metrics, ts, qn / 4.0);
3245 ts->set_position_lock_style (AudioTime);
3246 recompute_meters (_metrics);
3249 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3250 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3251 recompute_meters (_metrics);
3257 Metrics::const_iterator d = future_map.begin();
3258 while (d != future_map.end()) {
3263 MetricPositionChanged (PropertyChange ()); // Emit Signal
3266 /** moves a MeterSection to a specified position.
3267 * @param ms - the section to be moved
3268 * @param frame - the new position in frames for the meter
3270 * as a meter cannot snap to anything but bars,
3271 * the supplied frame is rounded to the nearest bar, possibly
3272 * leaving the meter position unchanged.
3275 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3279 if (ms->position_lock_style() == AudioTime) {
3282 Glib::Threads::RWLock::WriterLock lm (lock);
3283 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3285 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3286 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3287 recompute_tempi (_metrics);
3292 Glib::Threads::RWLock::WriterLock lm (lock);
3293 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3295 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3296 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3298 if (solve_map_bbt (future_map, copy, bbt)) {
3299 solve_map_bbt (_metrics, ms, bbt);
3300 recompute_tempi (_metrics);
3305 Metrics::const_iterator d = future_map.begin();
3306 while (d != future_map.end()) {
3311 MetricPositionChanged (PropertyChange ()); // Emit Signal
3315 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3318 bool can_solve = false;
3320 Glib::Threads::RWLock::WriterLock lm (lock);
3321 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3323 if (tempo_copy->type() == TempoSection::Constant) {
3324 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3325 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3327 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3328 tempo_copy->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3331 if (ts->clamped()) {
3332 TempoSection* prev = 0;
3333 if ((prev = previous_tempo_section_locked (future_map, tempo_copy)) != 0) {
3334 prev->set_end_note_types_per_minute (tempo_copy->note_types_per_minute());
3338 recompute_tempi (future_map);
3340 if (check_solved (future_map)) {
3341 if (ts->type() == TempoSection::Constant) {
3342 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3343 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3345 ts->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3346 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3349 if (ts->clamped()) {
3350 TempoSection* prev = 0;
3351 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3352 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3356 recompute_map (_metrics);
3361 Metrics::const_iterator d = future_map.begin();
3362 while (d != future_map.end()) {
3367 MetricPositionChanged (PropertyChange ()); // Emit Signal
3374 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3377 Ts (future prev_t) Tnext
3380 |----------|----------
3387 Glib::Threads::RWLock::WriterLock lm (lock);
3393 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3399 /* minimum allowed measurement distance in frames */
3400 framepos_t const min_dframe = 2;
3403 if (prev_t->clamped()) {
3404 TempoSection* next_t = next_tempo_section_locked (future_map, prev_t);
3405 TempoSection* prev_to_prev_t = previous_tempo_section_locked (future_map, prev_t);
3406 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3407 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3409 double contribution = 0.0;
3410 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3411 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3413 framepos_t const fr_off = (end_frame - frame);
3414 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3416 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3417 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3418 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3420 new_bpm = prev_t->note_types_per_minute();
3423 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3425 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
3426 / (double) (end_frame - prev_t->frame()));
3428 new_bpm = prev_t->note_types_per_minute();
3431 new_bpm = min (new_bpm, (double) 1000.0);
3433 /* don't clamp and proceed here.
3434 testing has revealed that this can go negative,
3435 which is an entirely different thing to just being too low.
3438 if (new_bpm < 0.5) {
3442 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3443 prev_t->set_note_types_per_minute (new_bpm);
3445 prev_t->set_end_note_types_per_minute (new_bpm);
3446 prev_t->set_note_types_per_minute (new_bpm);
3449 if (prev_t->clamped()) {
3450 TempoSection* prev = 0;
3451 if ((prev = previous_tempo_section_locked (future_map, prev_t)) != 0) {
3452 prev->set_end_note_types_per_minute (prev_t->note_types_per_minute());
3456 recompute_tempi (future_map);
3457 recompute_meters (future_map);
3459 if (check_solved (future_map)) {
3460 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3461 ts->set_note_types_per_minute (new_bpm);
3463 ts->set_end_note_types_per_minute (new_bpm);
3464 ts->set_note_types_per_minute (new_bpm);
3466 if (ts->clamped()) {
3467 TempoSection* prev = 0;
3468 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3469 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3472 recompute_tempi (_metrics);
3473 recompute_meters (_metrics);
3479 Metrics::const_iterator d = future_map.begin();
3480 while (d != future_map.end()) {
3484 MetricPositionChanged (PropertyChange ()); // Emit Signal
3489 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3492 Ts (future prev_t) Tnext
3495 |----------|----------
3502 Glib::Threads::RWLock::WriterLock lm (lock);
3508 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3514 /* minimum allowed measurement distance in frames */
3515 framepos_t const min_dframe = 2;
3518 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3519 new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->frame() - frame)
3520 / (double) (prev_t->frame() - end_frame));
3522 new_bpm = prev_t->end_note_types_per_minute();
3525 new_bpm = min (new_bpm, (double) 1000.0);
3527 if (new_bpm < 0.5) {
3531 prev_t->set_end_note_types_per_minute (new_bpm);
3533 TempoSection* next = 0;
3534 if ((next = next_tempo_section_locked (future_map, prev_t)) != 0) {
3535 if (next->clamped()) {
3536 next->set_note_types_per_minute (prev_t->end_note_types_per_minute());
3540 recompute_tempi (future_map);
3541 recompute_meters (future_map);
3543 if (check_solved (future_map)) {
3544 ts->set_end_note_types_per_minute (new_bpm);
3546 TempoSection* true_next = 0;
3547 if ((true_next = next_tempo_section_locked (_metrics, ts)) != 0) {
3548 if (true_next->clamped()) {
3549 true_next->set_note_types_per_minute (ts->end_note_types_per_minute());
3553 recompute_tempi (_metrics);
3554 recompute_meters (_metrics);
3560 Metrics::const_iterator d = future_map.begin();
3561 while (d != future_map.end()) {
3566 MetricPositionChanged (PropertyChange ()); // Emit Signal
3570 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
3572 TempoSection* next_t = 0;
3573 TempoSection* next_to_next_t = 0;
3575 bool can_solve = false;
3577 /* minimum allowed measurement distance in frames */
3578 framepos_t const min_dframe = 2;
3581 Glib::Threads::RWLock::WriterLock lm (lock);
3586 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3587 TempoSection* prev_to_prev_t = 0;
3588 const frameoffset_t fr_off = end_frame - frame;
3594 if (tempo_copy->pulse() > 0.0) {
3595 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
3598 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3599 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3600 next_t = static_cast<TempoSection*> (*i);
3609 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3610 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3611 next_to_next_t = static_cast<TempoSection*> (*i);
3616 if (!next_to_next_t) {
3620 double prev_contribution = 0.0;
3622 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3623 prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3626 const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
3629 framepos_t old_tc_minute = tempo_copy->minute();
3630 double old_next_minute = next_t->minute();
3631 double old_next_to_next_minute = next_to_next_t->minute();
3634 double new_next_bpm;
3635 double new_copy_end_bpm;
3637 if (frame > tempo_copy->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > tempo_copy->frame() + min_dframe) {
3638 new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
3639 / (double) (end_frame - tempo_copy->frame()));
3641 new_bpm = tempo_copy->note_types_per_minute();
3644 /* don't clamp and proceed here.
3645 testing has revealed that this can go negative,
3646 which is an entirely different thing to just being too low.
3648 if (new_bpm < 0.5) {
3652 new_bpm = min (new_bpm, (double) 1000.0);
3654 tempo_copy->set_note_types_per_minute (new_bpm);
3655 if (tempo_copy->type() == TempoSection::Constant) {
3656 tempo_copy->set_end_note_types_per_minute (new_bpm);
3659 recompute_tempi (future_map);
3661 if (check_solved (future_map)) {
3667 ts->set_note_types_per_minute (new_bpm);
3668 if (ts->type() == TempoSection::Constant) {
3669 ts->set_end_note_types_per_minute (new_bpm);
3672 recompute_map (_metrics);
3677 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3678 if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) {
3680 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3681 / (double) ((old_next_to_next_minute) - old_next_minute));
3684 new_next_bpm = next_t->note_types_per_minute();
3687 next_t->set_note_types_per_minute (new_next_bpm);
3688 recompute_tempi (future_map);
3690 if (check_solved (future_map)) {
3691 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3692 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3693 next_t = static_cast<TempoSection*> (*i);
3701 next_t->set_note_types_per_minute (new_next_bpm);
3702 recompute_map (_metrics);
3706 double next_frame_ratio = 1.0;
3707 double copy_frame_ratio = 1.0;
3709 if (next_to_next_t) {
3710 next_frame_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute - old_next_minute);
3712 copy_frame_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute));
3715 new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
3716 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
3718 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3720 if (next_t->clamped()) {
3721 next_t->set_note_types_per_minute (new_copy_end_bpm);
3723 next_t->set_note_types_per_minute (new_next_bpm);
3726 recompute_tempi (future_map);
3728 if (check_solved (future_map)) {
3729 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3730 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3731 next_t = static_cast<TempoSection*> (*i);
3740 if (next_t->clamped()) {
3741 next_t->set_note_types_per_minute (new_copy_end_bpm);
3743 next_t->set_note_types_per_minute (new_next_bpm);
3746 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3747 recompute_map (_metrics);
3753 Metrics::const_iterator d = future_map.begin();
3754 while (d != future_map.end()) {
3759 MetricPositionChanged (PropertyChange ()); // Emit Signal
3764 /** Returns the frame position of the musical position zero */
3766 TempoMap::music_origin ()
3768 Glib::Threads::RWLock::ReaderLock lm (lock);
3770 return first_tempo().frame();
3773 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3774 * the supplied frame, possibly returning a negative value.
3776 * @param frame The session frame position.
3777 * @param sub_num The subdivision to use when rounding the beat.
3778 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3779 * Positive integers indicate quarter note (non BBT) divisions.
3780 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3781 * @return The beat position of the supplied frame.
3783 * when working to a musical grid, the use of sub_nom indicates that
3784 * the position should be interpreted musically.
3786 * it effectively snaps to meter bars, meter beats or quarter note divisions
3787 * (as per current gui convention) and returns a musical position independent of frame rate.
3789 * If the supplied frame lies before the first meter, the return will be negative,
3790 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3791 * the continuation of the tempo curve (backwards).
3793 * This function is sensitive to tempo and meter.
3796 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3798 Glib::Threads::RWLock::ReaderLock lm (lock);
3800 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3804 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3806 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3809 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3810 * the supplied frame, possibly returning a negative value.
3812 * @param frame The session frame position.
3813 * @param sub_num The subdivision to use when rounding the quarter note.
3814 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3815 * Positive integers indicate quarter note (non BBT) divisions.
3816 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3817 * @return The quarter note position of the supplied frame.
3819 * When working to a musical grid, the use of sub_nom indicates that
3820 * the frame position should be interpreted musically.
3822 * it effectively snaps to meter bars, meter beats or quarter note divisions
3823 * (as per current gui convention) and returns a musical position independent of frame rate.
3825 * If the supplied frame lies before the first meter, the return will be negative,
3826 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3827 * the continuation of the tempo curve (backwards).
3829 * This function is tempo-sensitive.
3832 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3834 Glib::Threads::RWLock::ReaderLock lm (lock);
3836 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3840 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3842 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3845 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3846 } else if (sub_num == 1) {
3847 /* the gui requested exact musical (BBT) beat */
3848 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3849 } else if (sub_num == -1) {
3851 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3855 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3857 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3859 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3869 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3870 * @param pos the frame position in the tempo map.
3871 * @param bbt the distance in BBT time from pos to calculate.
3872 * @param dir the rounding direction..
3873 * @return the duration in frames between pos and bbt
3876 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3878 Glib::Threads::RWLock::ReaderLock lm (lock);
3880 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3882 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3885 pos_bbt.bars += bbt.bars;
3887 pos_bbt.ticks += bbt.ticks;
3888 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3890 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3893 pos_bbt.beats += bbt.beats;
3894 if ((double) pos_bbt.beats > divisions) {
3896 pos_bbt.beats -= divisions;
3898 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3900 return pos_bbt_frame - pos;
3904 if (pos_bbt.bars <= bbt.bars) {
3907 pos_bbt.bars -= bbt.bars;
3910 if (pos_bbt.ticks < bbt.ticks) {
3911 if (pos_bbt.bars > 1) {
3912 if (pos_bbt.beats == 1) {
3914 pos_bbt.beats = divisions;
3918 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3924 pos_bbt.ticks -= bbt.ticks;
3927 if (pos_bbt.beats <= bbt.beats) {
3928 if (pos_bbt.bars > 1) {
3930 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3935 pos_bbt.beats -= bbt.beats;
3938 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3945 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3947 return round_to_type (fr, dir, Bar);
3951 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3953 return round_to_type (fr, dir, Beat);
3957 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3959 Glib::Threads::RWLock::ReaderLock lm (lock);
3960 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);
3961 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3962 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3964 ticks -= beats * BBT_Time::ticks_per_beat;
3967 /* round to next (or same iff dir == RoundUpMaybe) */
3969 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3971 if (mod == 0 && dir == RoundUpMaybe) {
3972 /* right on the subdivision, which is fine, so do nothing */
3974 } else if (mod == 0) {
3975 /* right on the subdivision, so the difference is just the subdivision ticks */
3976 ticks += ticks_one_subdivisions_worth;
3979 /* not on subdivision, compute distance to next subdivision */
3981 ticks += ticks_one_subdivisions_worth - mod;
3984 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3985 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3986 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3987 // But I'm keeping it around, until we determine there are no terrible consequences.
3988 // if (ticks >= BBT_Time::ticks_per_beat) {
3989 // ticks -= BBT_Time::ticks_per_beat;
3992 } else if (dir < 0) {
3994 /* round to previous (or same iff dir == RoundDownMaybe) */
3996 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3998 if (difference == 0 && dir == RoundDownAlways) {
3999 /* right on the subdivision, but force-rounding down,
4000 so the difference is just the subdivision ticks */
4001 difference = ticks_one_subdivisions_worth;
4004 if (ticks < difference) {
4005 ticks = BBT_Time::ticks_per_beat - ticks;
4007 ticks -= difference;
4011 /* round to nearest */
4014 /* compute the distance to the previous and next subdivision */
4016 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
4018 /* closer to the next subdivision, so shift forward */
4020 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
4022 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
4024 if (ticks > BBT_Time::ticks_per_beat) {
4026 ticks -= BBT_Time::ticks_per_beat;
4027 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
4030 } else if (rem > 0) {
4032 /* closer to previous subdivision, so shift backward */
4036 /* can't go backwards past zero, so ... */
4037 return MusicFrame (0, 0);
4039 /* step back to previous beat */
4041 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4042 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4044 ticks = lrint (ticks - rem);
4045 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4048 /* on the subdivision, do nothing */
4052 MusicFrame ret (0, 0);
4053 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4054 ret.division = sub_num;
4060 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
4062 Glib::Threads::RWLock::ReaderLock lm (lock);
4063 const double minute = minute_at_frame (frame);
4064 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4065 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
4066 MusicFrame ret (0, 0);
4073 /* find bar previous to 'frame' */
4079 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4083 } else if (dir > 0) {
4084 /* find bar following 'frame' */
4089 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4093 /* true rounding: find nearest bar */
4094 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4097 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4099 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4101 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4102 ret.frame = next_ft;
4107 ret.frame = prev_ft;
4119 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
4122 } else if (dir > 0) {
4123 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
4127 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
4134 return MusicFrame (0, 0);
4138 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4139 framepos_t lower, framepos_t upper, uint32_t bar_mod)
4141 Glib::Threads::RWLock::ReaderLock lm (lock);
4142 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
4144 /* although the map handles negative beats, bbt doesn't. */
4149 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
4153 while (pos >= 0 && pos < upper) {
4154 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
4155 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4156 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4157 const double qn = pulse_at_beat_locked (_metrics, cnt) * 4.0;
4159 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, qn));
4163 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
4168 bbt.bars -= bbt.bars % bar_mod;
4172 while (pos >= 0 && pos < upper) {
4173 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4174 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4175 const double qn = pulse_at_bbt_locked (_metrics, bbt) * 4.0;
4177 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, qn));
4178 bbt.bars += bar_mod;
4184 TempoMap::tempo_section_at_frame (framepos_t frame) const
4186 Glib::Threads::RWLock::ReaderLock lm (lock);
4188 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4192 TempoMap::tempo_section_at_frame (framepos_t frame)
4194 Glib::Threads::RWLock::ReaderLock lm (lock);
4196 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4200 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4202 TempoSection* prev = 0;
4206 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4208 if ((*i)->is_tempo()) {
4209 t = static_cast<TempoSection*> (*i);
4213 if (prev && t->minute() > minute) {
4223 abort(); /*NOTREACHED*/
4229 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4231 TempoSection* prev = 0;
4235 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4237 if ((*i)->is_tempo()) {
4238 t = static_cast<TempoSection*> (*i);
4242 if (prev && t->minute() > minute) {
4252 abort(); /*NOTREACHED*/
4258 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4260 TempoSection* prev_t = 0;
4261 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4265 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4266 if ((*i)->is_tempo()) {
4267 t = static_cast<TempoSection*> (*i);
4273 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4284 TempoMap::previous_tempo_section (TempoSection* ts) const
4286 Glib::Threads::RWLock::ReaderLock lm (lock);
4288 return previous_tempo_section_locked (_metrics, ts);
4293 TempoMap::previous_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4299 TempoSection* prev = 0;
4301 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4303 if ((*i)->is_tempo()) {
4304 TempoSection* t = static_cast<TempoSection*> (*i);
4310 if (prev && t == ts) {
4321 abort(); /*NOTREACHED*/
4328 TempoMap::next_tempo_section (TempoSection* ts) const
4330 Glib::Threads::RWLock::ReaderLock lm (lock);
4332 return next_tempo_section_locked (_metrics, ts);
4336 TempoMap::next_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4342 TempoSection* prev = 0;
4344 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4346 if ((*i)->is_tempo()) {
4347 TempoSection* t = static_cast<TempoSection*> (*i);
4353 if (prev && prev == ts) {
4364 abort(); /*NOTREACHED*/
4369 /* don't use this to calculate length (the tempo is only correct for this frame).
4370 do that stuff based on the beat_at_frame and frame_at_beat api
4373 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4375 Glib::Threads::RWLock::ReaderLock lm (lock);
4377 const TempoSection* ts_at = 0;
4378 const TempoSection* ts_after = 0;
4379 Metrics::const_iterator i;
4382 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4384 if ((*i)->is_tempo()) {
4385 t = static_cast<TempoSection*> (*i);
4389 if (ts_at && (*i)->frame() > frame) {
4399 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4401 /* must be treated as constant tempo */
4402 return ts_at->frames_per_quarter_note (_frame_rate);
4406 TempoMap::meter_section_at_frame (framepos_t frame) const
4408 Glib::Threads::RWLock::ReaderLock lm (lock);
4409 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4413 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4415 Metrics::const_iterator i;
4416 MeterSection* prev = 0;
4420 for (i = metrics.begin(); i != metrics.end(); ++i) {
4422 if (!(*i)->is_tempo()) {
4423 m = static_cast<MeterSection*> (*i);
4425 if (prev && (*i)->minute() > minute) {
4435 abort(); /*NOTREACHED*/
4442 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4444 MeterSection* prev_m = 0;
4446 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4448 if (!(*i)->is_tempo()) {
4449 m = static_cast<MeterSection*> (*i);
4450 if (prev_m && m->beat() > beat) {
4461 TempoMap::meter_section_at_beat (double beat) const
4463 Glib::Threads::RWLock::ReaderLock lm (lock);
4464 return meter_section_at_beat_locked (_metrics, beat);
4468 TempoMap::meter_at_frame (framepos_t frame) const
4470 TempoMetric m (metric_at (frame));
4475 TempoMap::fix_legacy_session ()
4477 MeterSection* prev_m = 0;
4478 TempoSection* prev_t = 0;
4479 bool have_initial_t = false;
4481 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4485 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4487 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4490 m->set_minute (0.0);
4491 m->set_position_lock_style (AudioTime);
4496 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4497 + (m->bbt().beats - 1)
4498 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4500 m->set_beat (start);
4501 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4502 + (m->bbt().beats - 1)
4503 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4504 m->set_pulse (start_beat / prev_m->note_divisor());
4507 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4512 /* Ramp type never existed in the era of this tempo section */
4513 t->set_end_note_types_per_minute (t->note_types_per_minute());
4517 t->set_minute (0.0);
4518 t->set_position_lock_style (AudioTime);
4520 have_initial_t = true;
4525 /* some 4.x sessions have no initial (non-movable) tempo. */
4526 if (!have_initial_t) {
4527 prev_t->set_pulse (0.0);
4528 prev_t->set_minute (0.0);
4529 prev_t->set_position_lock_style (AudioTime);
4530 prev_t->set_initial (true);
4531 prev_t->set_locked_to_meter (true);
4532 have_initial_t = true;
4535 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4536 + (t->legacy_bbt().beats - 1)
4537 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4539 t->set_pulse (beat / prev_m->note_divisor());
4541 /* really shouldn't happen but.. */
4542 t->set_pulse (beat / 4.0);
4550 TempoMap::fix_legacy_end_session ()
4552 TempoSection* prev_t = 0;
4554 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4557 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4564 if (prev_t->type() != TempoSection::Constant) {
4565 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4575 TempoMap::get_state ()
4577 Metrics::const_iterator i;
4578 XMLNode *root = new XMLNode ("TempoMap");
4581 Glib::Threads::RWLock::ReaderLock lm (lock);
4582 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4583 root->add_child_nocopy ((*i)->get_state());
4591 TempoMap::set_state (const XMLNode& node, int /*version*/)
4594 Glib::Threads::RWLock::WriterLock lm (lock);
4597 XMLNodeConstIterator niter;
4598 Metrics old_metrics (_metrics);
4601 nlist = node.children();
4603 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4604 XMLNode* child = *niter;
4606 if (child->name() == TempoSection::xml_state_node_name) {
4609 TempoSection* ts = new TempoSection (*child, _frame_rate);
4610 _metrics.push_back (ts);
4613 catch (failed_constructor& err){
4614 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4615 _metrics = old_metrics;
4616 old_metrics.clear();
4620 } else if (child->name() == MeterSection::xml_state_node_name) {
4623 MeterSection* ms = new MeterSection (*child, _frame_rate);
4624 _metrics.push_back (ms);
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();
4636 /* check for legacy sessions where bbt was the base musical unit for tempo */
4637 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4639 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4640 if (t->legacy_bbt().bars != 0) {
4641 fix_legacy_session();
4645 if (t->end_note_types_per_minute() < 0.0) {
4646 fix_legacy_end_session();
4654 if (niter == nlist.end()) {
4655 MetricSectionSorter cmp;
4656 _metrics.sort (cmp);
4659 /* check for multiple tempo/meters at the same location, which
4660 ardour2 somehow allowed.
4663 Metrics::iterator prev = _metrics.end();
4664 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4665 if (prev != _metrics.end()) {
4667 MeterSection* prev_m;
4669 TempoSection* prev_t;
4670 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4671 if (prev_m->beat() == ms->beat()) {
4672 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
4673 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->beat()) << endmsg;
4676 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4677 if (prev_t->pulse() == ts->pulse()) {
4678 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4679 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4687 recompute_map (_metrics);
4689 Metrics::const_iterator d = old_metrics.begin();
4690 while (d != old_metrics.end()) {
4694 old_metrics.clear ();
4697 PropertyChanged (PropertyChange ());
4703 TempoMap::dump (std::ostream& o) const
4705 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4706 const MeterSection* m;
4707 const TempoSection* t;
4708 const TempoSection* prev_t = 0;
4710 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4712 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4713 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4714 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4715 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4716 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4718 o << " current start : " << t->note_types_per_minute()
4719 << " current end : " << t->end_note_types_per_minute()
4720 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4721 o << " previous : " << prev_t->note_types_per_minute()
4722 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4723 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4724 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4725 << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4726 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4729 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4730 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4731 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4732 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4735 o << "------" << std::endl;
4739 TempoMap::n_tempos() const
4741 Glib::Threads::RWLock::ReaderLock lm (lock);
4744 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4745 if ((*i)->is_tempo()) {
4754 TempoMap::n_meters() const
4756 Glib::Threads::RWLock::ReaderLock lm (lock);
4759 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4760 if (!(*i)->is_tempo()) {
4769 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4771 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4772 if ((*i)->frame() >= where && !(*i)->initial ()) {
4776 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4777 gui_set_meter_position (ms, (*i)->frame() + amount);
4780 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4781 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4786 PropertyChanged (PropertyChange ());
4790 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4794 std::list<MetricSection*> metric_kill_list;
4796 TempoSection* last_tempo = NULL;
4797 MeterSection* last_meter = NULL;
4798 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4799 bool meter_after = false; // is there a meter marker likewise?
4801 Glib::Threads::RWLock::WriterLock lm (lock);
4802 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4803 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4804 metric_kill_list.push_back(*i);
4805 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4808 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4812 else if ((*i)->frame() >= where) {
4813 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4814 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4815 if ((*i)->frame() == where) {
4816 // marker was immediately after end of range
4817 tempo_after = dynamic_cast<TempoSection*> (*i);
4818 meter_after = dynamic_cast<MeterSection*> (*i);
4824 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4825 if (last_tempo && !tempo_after) {
4826 metric_kill_list.remove(last_tempo);
4827 last_tempo->set_minute (minute_at_frame (where));
4830 if (last_meter && !meter_after) {
4831 metric_kill_list.remove(last_meter);
4832 last_meter->set_minute (minute_at_frame (where));
4836 //remove all the remaining metrics
4837 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4838 _metrics.remove(*i);
4843 recompute_map (_metrics);
4846 PropertyChanged (PropertyChange ());
4850 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4851 * pos can be -ve, if required.
4854 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4856 Glib::Threads::RWLock::ReaderLock lm (lock);
4857 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4859 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4863 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4865 Glib::Threads::RWLock::ReaderLock lm (lock);
4867 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4868 pos_bbt.ticks += op.ticks;
4869 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4871 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4873 pos_bbt.beats += op.beats;
4874 /* the meter in effect will start on the bar */
4875 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();
4876 while (pos_bbt.beats >= divisions_per_bar + 1) {
4878 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4879 pos_bbt.beats -= divisions_per_bar;
4881 pos_bbt.bars += op.bars;
4883 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4886 /** Count the number of beats that are equivalent to distance when going forward,
4890 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4892 Glib::Threads::RWLock::ReaderLock lm (lock);
4894 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4898 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4904 operator<< (std::ostream& o, const Meter& m) {
4905 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4909 operator<< (std::ostream& o, const Tempo& t) {
4910 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4914 operator<< (std::ostream& o, const MetricSection& section) {
4916 o << "MetricSection @ " << section.frame() << ' ';
4918 const TempoSection* ts;
4919 const MeterSection* ms;
4921 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4922 o << *((const Tempo*) ts);
4923 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4924 o << *((const Meter*) ms);