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)
161 , _legacy_end (false)
165 _legacy_bbt = BBT_Time (0, 0, 0);
168 std::string start_bbt;
169 if (node.get_property ("start", start_bbt)) {
170 if (string_to_bbt_time (start_bbt, bbt)) {
171 /* legacy session - start used to be in bbt*/
174 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
178 // Don't worry about return value, exception will be thrown on error
179 MetricSection::set_state (node, Stateful::loading_state_version);
181 if (node.get_property ("beats-per-minute", _note_types_per_minute)) {
182 if (_note_types_per_minute < 0.0) {
183 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
184 throw failed_constructor();
188 if (node.get_property ("note-type", _note_type)) {
189 if (_note_type < 1.0) {
190 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
191 throw failed_constructor();
194 /* older session, make note type be quarter by default */
198 if (!node.get_property ("clamped", _clamped)) {
202 if (node.get_property ("end-beats-per-minute", _end_note_types_per_minute)) {
203 if (_end_note_types_per_minute < 0.0) {
204 info << _("TempoSection XML node has an illegal \"in-beats-per-minute\" value") << endmsg;
205 //throw failed_constructor();
206 _end_note_types_per_minute = _note_types_per_minute;
213 TempoSection::Type old_type;
214 if (!node.get_property ("tempo-type", old_type)) {
215 if (old_type == TempoSection::Constant) {
216 _end_note_types_per_minute = _note_types_per_minute;
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);
245 MetricSection::add_state_to_node (*root);
247 root->set_property ("beats-per-minute", _note_types_per_minute);
248 root->set_property ("note-type", _note_type);
249 root->set_property ("clamped", _clamped);
250 root->set_property ("end-beats-per-minute", _end_note_types_per_minute);
251 root->set_property ("active", _active);
252 root->set_property ("locked-to-meter", _locked_to_meter);
257 /** returns the Tempo at the session-relative minute.
260 TempoSection::tempo_at_minute (const double& m) const
262 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
264 return Tempo (note_types_per_minute(), note_type());
267 return Tempo (_tempo_at_time (m - minute()), _note_type, _end_note_types_per_minute);
270 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
271 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
272 * @param p the pulse used to calculate the returned minute for constant tempi
273 * @return the minute at the supplied tempo
275 * note that the note_type is currently ignored in this function. see below.
279 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
280 * there should be no ramp between the two even if we are ramped.
281 * in other words a ramp should only place a curve on note_types_per_minute.
282 * we should be able to use Tempo note type here, but the above
283 * complicates things a bit.
286 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
288 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
290 return ((p - pulse()) / pulses_per_minute()) + minute();
293 return _time_at_tempo (ntpm) + minute();
296 /** returns the Tempo at the supplied whole-note pulse.
299 TempoSection::tempo_at_pulse (const double& p) const
301 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
304 return Tempo (note_types_per_minute(), note_type());
307 return Tempo (_tempo_at_pulse (p - pulse()), _note_type, _end_note_types_per_minute);
310 /** returns the whole-note pulse where a tempo in note types per minute occurs.
311 * constant tempi require minute m.
312 * @param ntpm the note types per minute value used to calculate the returned pulse
313 * @param m the minute used to calculate the returned pulse if the tempo is constant
314 * @return the whole-note pulse at the supplied tempo
316 * note that note_type is currently ignored in this function. see minute_at_tempo().
318 * for constant tempi, this is anaologous to pulse_at_minute().
321 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
323 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
325 return ((m - minute()) * pulses_per_minute()) + pulse();
328 return _pulse_at_tempo (ntpm) + pulse();
331 /** returns the whole-note pulse at the supplied session-relative minute.
334 TempoSection::pulse_at_minute (const double& m) const
336 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
338 return ((m - minute()) * pulses_per_minute()) + pulse();
341 return _pulse_at_time (m - minute()) + pulse();
344 /** returns the session-relative minute at the supplied whole-note pulse.
347 TempoSection::minute_at_pulse (const double& p) const
349 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
351 return ((p - pulse()) / pulses_per_minute()) + minute();
354 return _time_at_pulse (p - pulse()) + minute();
357 /** returns thw whole-note pulse at session frame position f.
358 * @param f the frame position.
359 * @return the position in whole-note pulses corresponding to f
361 * for use with musical units whose granularity is coarser than frames (e.g. ticks)
364 TempoSection::pulse_at_frame (const framepos_t& f) const
366 const bool constant = type() == Constant || _c == 0.0 || (initial() && f < frame());
368 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
371 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
375 TempoSection::frame_at_pulse (const double& p) const
377 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
379 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
382 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
390 Tt----|-----------------*|
391 Ta----|--------------|* |
397 _______________|___|____
398 time a t (next tempo)
401 Duration in beats at time a is the integral of some Tempo function.
402 In our case, the Tempo function (Tempo at time t) is
405 with function constant
410 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
411 b(t) = T0(e^(ct) - 1) / c
413 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:
414 t(b) = log((c.b / T0) + 1) / c
416 The time t at which Tempo T occurs is a as above:
417 t(T) = log(T / T0) / c
419 The beat at which a Tempo T occurs is:
422 The Tempo at which beat b occurs is:
425 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
426 Our problem is that we usually don't know t.
427 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.
428 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
429 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
431 By substituting our expanded t as a in the c function above, our problem is reduced to:
432 c = T0 (e^(log (Ta / T0)) - 1) / b
434 Of course the word 'beat' has been left loosely defined above.
435 In music, a beat is defined by the musical pulse (which comes from the tempo)
436 and the meter in use at a particular time (how many pulse divisions there are in one bar).
437 It would be more accurate to substitute the work 'pulse' for 'beat' above.
441 We can now store c for future time calculations.
442 If the following tempo section (the one that defines c in conjunction with this one)
443 is changed or moved, c is no longer valid.
445 The public methods are session-relative.
447 Most of this stuff is taken from this paper:
450 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
453 Zurich University of Arts
454 Institute for Computer Music and Sound Technology
456 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
460 /** compute this ramp's function constant from some tempo-pulse point
461 * @param end_npm end tempo (in note types per minute)
462 * @param end_pulse duration (pulses into global start) of some other position.
463 * @return the calculated function constant
466 TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const
468 if (note_types_per_minute() == end_npm || type() == Constant) {
472 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
473 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
476 /** compute the function constant from some tempo-time point.
477 * @param end_npm tempo (note types/min.)
478 * @param end_minute distance (in minutes) from session origin
479 * @return the calculated function constant
482 TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const
484 if (note_types_per_minute() == end_npm || type() == Constant) {
488 return c_func (end_npm, end_minute - minute());
491 /* position function */
493 TempoSection::a_func (double end_npm, double c) const
495 return log (end_npm / note_types_per_minute()) / c;
498 /*function constant*/
500 TempoSection::c_func (double end_npm, double end_time) const
502 return log (end_npm / note_types_per_minute()) / end_time;
505 /* tempo in note types per minute at time in minutes */
507 TempoSection::_tempo_at_time (const double& time) const
509 return exp (_c * time) * note_types_per_minute();
512 /* time in minutes at tempo in note types per minute */
514 TempoSection::_time_at_tempo (const double& npm) const
516 return log (npm / note_types_per_minute()) / _c;
519 /* pulse at tempo in note types per minute */
521 TempoSection::_pulse_at_tempo (const double& npm) const
523 return ((npm - note_types_per_minute()) / _c) / _note_type;
526 /* tempo in note types per minute at pulse */
528 TempoSection::_tempo_at_pulse (const double& pulse) const
530 return (pulse * _note_type * _c) + note_types_per_minute();
533 /* pulse at time in minutes */
535 TempoSection::_pulse_at_time (const double& time) const
537 return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
540 /* time in minutes at pulse */
542 TempoSection::_time_at_pulse (const double& pulse) const
544 return log1p ((_c * pulse * _note_type) / note_types_per_minute()) / _c;
547 /***********************************************************************/
549 const string MeterSection::xml_state_node_name = "Meter";
551 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
552 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
555 pair<double, BBT_Time> start;
559 if (node.get_property ("start", bbt_str)) {
560 if (string_to_bbt_time (bbt_str, bbt)) {
561 /* legacy session - start used to be in bbt*/
562 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
565 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
569 MetricSection::set_state (node, Stateful::loading_state_version);
572 node.get_property ("beat", start.first);
574 if (node.get_property ("bbt", bbt_str)) {
575 if (!string_to_bbt_time (bbt_str, start.second)) {
576 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
577 throw failed_constructor();
580 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
585 /* beats-per-bar is old; divisions-per-bar is new */
587 if (!node.get_property ("divisions-per-bar", _divisions_per_bar)) {
588 if (!node.get_property ("beats-per-bar", _divisions_per_bar)) {
589 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
590 throw failed_constructor();
594 if (_divisions_per_bar < 0.0) {
595 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
596 throw failed_constructor();
599 if (!node.get_property ("note-type", _note_type)) {
600 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
601 throw failed_constructor();
604 if (_note_type < 0.0) {
605 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
606 throw failed_constructor();
611 MeterSection::get_state() const
613 XMLNode *root = new XMLNode (xml_state_node_name);
616 MetricSection::add_state_to_node (*root);
619 bbt_time_to_string (_bbt, bbt_str);
620 root->set_property ("bbt", bbt_str);
621 root->set_property ("beat", beat());
622 root->set_property ("note-type", _note_type);
623 root->set_property ("divisions-per-bar", _divisions_per_bar);
628 /***********************************************************************/
632 Tempo determines the rate of musical pulse determined by its components
633 note types per minute - the rate per minute of the whole note divisor _note_type
634 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
635 Meter divides the musical pulse into measures and beats according to its components
639 TempoSection - translates between time, musical pulse and tempo.
640 has a musical location in whole notes (pulses).
641 has a time location in minutes.
642 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
643 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
645 MeterSection - translates between BBT, meter-based beat and musical pulse.
646 has a musical location in whole notes (pulses)
647 has a musical location in meter-based beats
648 has a musical location in BBT time
649 has a time location expressed in minutes.
651 TempoSection and MeterSection may be locked to either audio or music (position lock style).
652 The lock style determines the location type to be kept as a reference when location is recalculated.
654 The first tempo and meter are special. they must move together, and are locked to audio.
655 Audio locked tempi which lie before the first meter are made inactive.
657 Recomputing the map is the process where the 'missing' location types are calculated.
658 We construct the tempo map by first using the locked location type of each section
659 to determine non-locked location types (pulse or minute position).
660 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
662 Having done this, we can now traverse the Metrics list by pulse or minute
663 to query its relevant meter/tempo.
665 It is important to keep the _metrics in an order that makes sense.
666 Because ramped MusicTime and AudioTime tempos can interact with each other,
667 reordering is frequent. Care must be taken to keep _metrics in a solved state.
668 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
672 Music and audio-locked objects may seem interchangeable on the surface, but when translating
673 between audio samples and beat, remember that a sample is only a quantised approximation
674 of the actual time (in minutes) of a beat.
675 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
676 mean that this frame is the actual location in time of 1|3|0.
678 You cannot use a frame measurement to determine beat distance except under special circumstances
679 (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).
681 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
682 sample space the user is operating at to be translated correctly to the object.
684 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
685 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
686 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
688 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
690 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
691 The result is rounded to audio frames.
692 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
695 frame_at_beat (beat_at_frame (frame)) == frame
697 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
699 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
700 frames_between_quarter_notes () eliminats this effect when determining time duration
701 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
703 The above pointless example could instead do:
704 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
706 The Shaggs - Things I Wonder
707 https://www.youtube.com/watch?v=9wQK6zMJOoQ
710 struct MetricSectionSorter {
711 bool operator() (const MetricSection* a, const MetricSection* b) {
712 return a->pulse() < b->pulse();
716 struct MetricSectionFrameSorter {
717 bool operator() (const MetricSection* a, const MetricSection* b) {
718 return a->frame() < b->frame();
722 TempoMap::TempoMap (framecnt_t fr)
725 BBT_Time start (1, 1, 0);
727 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo, AudioTime, fr);
728 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
730 t->set_initial (true);
731 t->set_locked_to_meter (true);
733 m->set_initial (true);
735 /* note: frame time is correct (zero) for both of these */
737 _metrics.push_back (t);
738 _metrics.push_back (m);
743 TempoMap::operator= (TempoMap const & other)
745 if (&other != this) {
746 Glib::Threads::RWLock::ReaderLock lr (other.lock);
747 Glib::Threads::RWLock::WriterLock lm (lock);
748 _frame_rate = other._frame_rate;
750 Metrics::const_iterator d = _metrics.begin();
751 while (d != _metrics.end()) {
757 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
758 TempoSection const * const ts = dynamic_cast<TempoSection const * const> (*m);
759 MeterSection const * const ms = dynamic_cast<MeterSection const * const> (*m);
762 TempoSection* new_section = new TempoSection (*ts);
763 _metrics.push_back (new_section);
765 MeterSection* new_section = new MeterSection (*ms);
766 _metrics.push_back (new_section);
771 PropertyChanged (PropertyChange());
776 TempoMap::~TempoMap ()
778 Metrics::const_iterator d = _metrics.begin();
779 while (d != _metrics.end()) {
787 TempoMap::frame_at_minute (const double time) const
789 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
793 TempoMap::minute_at_frame (const framepos_t frame) const
795 return (frame / (double) _frame_rate) / 60.0;
799 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
801 bool removed = false;
804 Glib::Threads::RWLock::WriterLock lm (lock);
805 if ((removed = remove_tempo_locked (tempo))) {
806 if (complete_operation) {
807 recompute_map (_metrics);
812 if (removed && complete_operation) {
813 PropertyChanged (PropertyChange ());
818 TempoMap::remove_tempo_locked (const TempoSection& tempo)
822 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
823 if (dynamic_cast<TempoSection*> (*i) != 0) {
824 if (tempo.frame() == (*i)->frame()) {
825 if (!(*i)->initial()) {
838 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
840 bool removed = false;
843 Glib::Threads::RWLock::WriterLock lm (lock);
844 if ((removed = remove_meter_locked (tempo))) {
845 if (complete_operation) {
846 recompute_map (_metrics);
851 if (removed && complete_operation) {
852 PropertyChanged (PropertyChange ());
857 TempoMap::remove_meter_locked (const MeterSection& meter)
860 if (meter.position_lock_style() == AudioTime) {
861 /* remove meter-locked tempo */
862 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
864 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
865 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
874 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
875 if (dynamic_cast<MeterSection*> (*i) != 0) {
876 if (meter.frame() == (*i)->frame()) {
877 if (!(*i)->initial()) {
890 TempoMap::do_insert (MetricSection* section)
892 bool need_add = true;
893 /* we only allow new meters to be inserted on beat 1 of an existing
897 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
899 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
901 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
902 corrected.second.beats = 1;
903 corrected.second.ticks = 0;
904 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
905 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
906 m->bbt(), corrected.second) << endmsg;
907 //m->set_pulse (corrected);
911 /* Look for any existing MetricSection that is of the same type and
912 in the same bar as the new one, and remove it before adding
913 the new one. Note that this means that if we find a matching,
914 existing section, we can break out of the loop since we're
915 guaranteed that there is only one such match.
918 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
920 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
921 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
922 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
923 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
925 if (tempo && insert_tempo) {
928 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
929 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
931 if (tempo->initial()) {
933 /* can't (re)move this section, so overwrite
934 * its data content (but not its properties as
938 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
939 (*i)->set_position_lock_style (AudioTime);
948 } else if (meter && insert_meter) {
952 bool const ipm = insert_meter->position_lock_style() == MusicTime;
954 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
956 if (meter->initial()) {
958 /* can't (re)move this section, so overwrite
959 * its data content (but not its properties as
963 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
964 (*i)->set_position_lock_style (AudioTime);
974 /* non-matching types, so we don't care */
978 /* Add the given MetricSection, if we didn't just reset an existing
983 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
984 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
988 TempoSection* prev_t = 0;
990 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
991 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
992 bool const ipm = insert_meter->position_lock_style() == MusicTime;
995 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
999 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
1003 prev_t = dynamic_cast<TempoSection*> (*i);
1006 } else if (insert_tempo) {
1007 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1008 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1011 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1012 const bool lm = insert_tempo->locked_to_meter();
1013 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1014 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1021 _metrics.insert (i, section);
1025 /* user supplies the exact pulse if pls == MusicTime */
1027 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1029 if (tempo.note_types_per_minute() <= 0.0) {
1030 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1034 TempoSection* ts = 0;
1035 TempoSection* prev_tempo = 0;
1037 Glib::Threads::RWLock::WriterLock lm (lock);
1038 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true);
1039 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1041 if ((*i)->is_tempo()) {
1042 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1044 bool const ipm = ts->position_lock_style() == MusicTime;
1045 bool const lm = ts->locked_to_meter();
1046 if ((ipm && this_t->pulse() == ts->pulse()) || (!ipm && this_t->frame() == ts->frame())
1047 || (lm && this_t->pulse() == ts->pulse())) {
1048 if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1049 prev_tempo->set_end_note_types_per_minute (ts->note_types_per_minute());
1053 prev_tempo = this_t;
1056 recompute_map (_metrics);
1059 PropertyChanged (PropertyChange ());
1065 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1067 if (tempo.note_types_per_minute() <= 0.0) {
1068 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1072 bool const locked_to_meter = ts.locked_to_meter();
1073 bool const ts_clamped = ts.clamped();
1074 TempoSection* new_ts = 0;
1077 Glib::Threads::RWLock::WriterLock lm (lock);
1078 TempoSection& first (first_tempo());
1079 if (!ts.initial()) {
1080 if (locked_to_meter) {
1082 /* cannot move a meter-locked tempo section */
1083 *static_cast<Tempo*>(&ts) = tempo;
1084 recompute_map (_metrics);
1087 remove_tempo_locked (ts);
1088 new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter);
1089 new_ts->set_clamped (ts_clamped);
1091 if (new_ts && new_ts->type() == TempoSection::Constant) {
1092 new_ts->set_end_note_types_per_minute (new_ts->note_types_per_minute());
1094 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1096 if ((*i)->is_tempo()) {
1097 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1099 bool const ipm = new_ts->position_lock_style() == MusicTime;
1100 bool const lm = new_ts->locked_to_meter();
1101 if ((ipm && this_t->pulse() > new_ts->pulse()) || (!ipm && this_t->frame() > new_ts->frame())
1102 || (lm && this_t->pulse() > new_ts->pulse())) {
1103 new_ts->set_end_note_types_per_minute (tempo.end_note_types_per_minute());
1113 first.set_pulse (0.0);
1114 first.set_minute (minute_at_frame (frame));
1115 first.set_position_lock_style (AudioTime);
1116 first.set_locked_to_meter (true);
1117 first.set_clamped (ts_clamped);
1119 /* cannot move the first tempo section */
1120 *static_cast<Tempo*>(&first) = tempo;
1123 recompute_map (_metrics);
1126 PropertyChanged (PropertyChange ());
1130 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1131 , PositionLockStyle pls, bool recompute, bool locked_to_meter)
1133 TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _frame_rate);
1134 t->set_locked_to_meter (locked_to_meter);
1139 if (pls == AudioTime) {
1140 solve_map_minute (_metrics, t, t->minute());
1142 solve_map_pulse (_metrics, t, t->pulse());
1144 recompute_meters (_metrics);
1151 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1153 MeterSection* m = 0;
1155 Glib::Threads::RWLock::WriterLock lm (lock);
1156 m = add_meter_locked (meter, beat, where, frame, pls, true);
1161 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1166 PropertyChanged (PropertyChange ());
1171 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1174 Glib::Threads::RWLock::WriterLock lm (lock);
1175 const double beat = beat_at_bbt_locked (_metrics, where);
1177 if (!ms.initial()) {
1178 remove_meter_locked (ms);
1179 add_meter_locked (meter, beat, where, frame, pls, true);
1181 MeterSection& first (first_meter());
1182 TempoSection& first_t (first_tempo());
1183 /* cannot move the first meter section */
1184 *static_cast<Meter*>(&first) = meter;
1185 first.set_position_lock_style (AudioTime);
1186 first.set_pulse (0.0);
1187 first.set_minute (minute_at_frame (frame));
1188 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1189 first.set_beat (beat);
1190 first_t.set_minute (first.minute());
1191 first_t.set_locked_to_meter (true);
1192 first_t.set_pulse (0.0);
1193 first_t.set_position_lock_style (AudioTime);
1194 recompute_map (_metrics);
1198 PropertyChanged (PropertyChange ());
1202 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1204 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1205 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1206 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1207 TempoSection* mlt = 0;
1209 if (pls == AudioTime) {
1210 /* add meter-locked tempo */
1211 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), AudioTime, true, true);
1219 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1221 bool solved = false;
1223 do_insert (new_meter);
1227 if (pls == AudioTime) {
1228 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1229 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1230 fudge frame so that the meter ends up at its BBT position instead.
1233 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1236 solved = solve_map_bbt (_metrics, new_meter, where);
1237 /* required due to resetting the pulse of meter-locked tempi above.
1238 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1239 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1241 recompute_map (_metrics);
1245 if (!solved && recompute) {
1246 /* if this has failed to solve, there is little we can do other than to ensure that
1247 the new map is recalculated.
1249 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1250 recompute_map (_metrics);
1257 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1259 Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1262 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1263 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1268 Glib::Threads::RWLock::WriterLock lm (lock);
1269 *((Tempo*) t) = newtempo;
1270 recompute_map (_metrics);
1272 PropertyChanged (PropertyChange ());
1279 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1281 Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1284 TempoSection* first;
1285 Metrics::iterator i;
1287 /* find the TempoSection immediately preceding "where"
1290 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1292 if ((*i)->frame() > where) {
1298 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1311 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1321 Glib::Threads::RWLock::WriterLock lm (lock);
1322 /* cannot move the first tempo section */
1323 *((Tempo*)prev) = newtempo;
1324 recompute_map (_metrics);
1327 PropertyChanged (PropertyChange ());
1331 TempoMap::first_meter () const
1333 const MeterSection *m = 0;
1335 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1336 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1341 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1342 abort(); /*NOTREACHED*/
1347 TempoMap::first_meter ()
1349 MeterSection *m = 0;
1351 /* CALLER MUST HOLD LOCK */
1353 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1354 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1359 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1360 abort(); /*NOTREACHED*/
1365 TempoMap::first_tempo () const
1367 const TempoSection *t = 0;
1369 /* CALLER MUST HOLD LOCK */
1371 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1372 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1382 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1383 abort(); /*NOTREACHED*/
1388 TempoMap::first_tempo ()
1390 TempoSection *t = 0;
1392 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1393 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1403 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1404 abort(); /*NOTREACHED*/
1408 TempoMap::recompute_tempi (Metrics& metrics)
1410 TempoSection* prev_t = 0;
1412 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1415 if ((*i)->is_tempo()) {
1416 t = static_cast<TempoSection*> (*i);
1428 if (t->position_lock_style() == AudioTime) {
1429 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1430 if (!t->locked_to_meter()) {
1431 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1435 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1436 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1444 prev_t->set_c (0.0);
1447 /* tempos must be positioned correctly.
1448 the current approach is to use a meter's bbt time as its base position unit.
1449 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1450 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1453 TempoMap::recompute_meters (Metrics& metrics)
1455 MeterSection* meter = 0;
1456 MeterSection* prev_m = 0;
1458 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1459 if (!(*mi)->is_tempo()) {
1460 meter = static_cast<MeterSection*> (*mi);
1461 if (meter->position_lock_style() == AudioTime) {
1463 pair<double, BBT_Time> b_bbt;
1464 TempoSection* meter_locked_tempo = 0;
1465 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1467 if ((*ii)->is_tempo()) {
1468 t = static_cast<TempoSection*> (*ii);
1469 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1470 meter_locked_tempo = t;
1477 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1478 if (beats + prev_m->beat() != meter->beat()) {
1479 /* reordering caused a bbt change */
1481 beats = meter->beat() - prev_m->beat();
1482 b_bbt = make_pair (beats + prev_m->beat()
1483 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1484 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1486 } else if (!meter->initial()) {
1487 b_bbt = make_pair (meter->beat(), meter->bbt());
1488 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1491 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1493 if (meter_locked_tempo) {
1494 meter_locked_tempo->set_pulse (pulse);
1496 meter->set_beat (b_bbt);
1497 meter->set_pulse (pulse);
1502 pair<double, BBT_Time> b_bbt;
1504 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1505 if (beats + prev_m->beat() != meter->beat()) {
1506 /* reordering caused a bbt change */
1507 b_bbt = make_pair (beats + prev_m->beat()
1508 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1510 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1512 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1514 /* shouldn't happen - the first is audio-locked */
1515 pulse = pulse_at_beat_locked (metrics, meter->beat());
1516 b_bbt = make_pair (meter->beat(), meter->bbt());
1519 meter->set_beat (b_bbt);
1520 meter->set_pulse (pulse);
1521 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1530 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1532 /* CALLER MUST HOLD WRITE LOCK */
1534 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1537 /* silly call from Session::process() during startup
1542 recompute_tempi (metrics);
1543 recompute_meters (metrics);
1547 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1549 Glib::Threads::RWLock::ReaderLock lm (lock);
1550 TempoMetric m (first_meter(), first_tempo());
1553 *last = ++_metrics.begin();
1556 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1557 at something, because we insert the default tempo and meter during
1558 TempoMap construction.
1560 now see if we can find better candidates.
1563 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1565 if ((*i)->frame() > frame) {
1579 /* XX meters only */
1581 TempoMap::metric_at (BBT_Time bbt) const
1583 Glib::Threads::RWLock::ReaderLock lm (lock);
1584 TempoMetric m (first_meter(), first_tempo());
1586 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1587 at something, because we insert the default tempo and meter during
1588 TempoMap construction.
1590 now see if we can find better candidates.
1593 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1595 if (!(*i)->is_tempo()) {
1596 mw = static_cast<MeterSection*> (*i);
1597 BBT_Time section_start (mw->bbt());
1599 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1610 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1611 * @param frame The session frame position.
1612 * @return The beat duration according to the tempo map at the supplied frame.
1614 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1615 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1617 * This function uses both tempo and meter.
1620 TempoMap::beat_at_frame (const framecnt_t& frame) const
1622 Glib::Threads::RWLock::ReaderLock lm (lock);
1624 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1627 /* This function uses both tempo and meter.*/
1629 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1631 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1632 MeterSection* prev_m = 0;
1633 MeterSection* next_m = 0;
1635 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1636 if (!(*i)->is_tempo()) {
1637 if (prev_m && (*i)->minute() > minute) {
1638 next_m = static_cast<MeterSection*> (*i);
1641 prev_m = static_cast<MeterSection*> (*i);
1645 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1647 /* audio locked meters fake their beat */
1648 if (next_m && next_m->beat() < beat) {
1649 return next_m->beat();
1655 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1656 * @param beat The BBT (meter-based) beat.
1657 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1659 * This function uses both tempo and meter.
1662 TempoMap::frame_at_beat (const double& beat) const
1664 Glib::Threads::RWLock::ReaderLock lm (lock);
1666 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1669 /* meter & tempo section based */
1671 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1673 MeterSection* prev_m = 0;
1674 TempoSection* prev_t = 0;
1678 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1679 if (!(*i)->is_tempo()) {
1680 m = static_cast<MeterSection*> (*i);
1681 if (prev_m && m->beat() > beat) {
1691 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1692 if ((*i)->is_tempo()) {
1693 t = static_cast<TempoSection*> (*i);
1699 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1708 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1711 /** Returns a Tempo corresponding to the supplied frame position.
1712 * @param frame The audio frame.
1713 * @return a Tempo according to the tempo map at the supplied frame.
1717 TempoMap::tempo_at_frame (const framepos_t& frame) const
1719 Glib::Threads::RWLock::ReaderLock lm (lock);
1721 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1725 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1727 TempoSection* prev_t = 0;
1731 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1732 if ((*i)->is_tempo()) {
1733 t = static_cast<TempoSection*> (*i);
1737 if ((prev_t) && t->minute() > minute) {
1738 /* t is the section past frame */
1739 return prev_t->tempo_at_minute (minute);
1745 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1748 /** returns the frame at which the supplied tempo occurs, or
1749 * the frame of the last tempo section (search exhausted)
1750 * only the position of the first occurence will be returned
1754 TempoMap::frame_at_tempo (const Tempo& tempo) const
1756 Glib::Threads::RWLock::ReaderLock lm (lock);
1758 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1762 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1764 TempoSection* prev_t = 0;
1765 const double tempo_bpm = tempo.note_types_per_minute();
1767 Metrics::const_iterator i;
1769 for (i = metrics.begin(); i != metrics.end(); ++i) {
1771 if ((*i)->is_tempo()) {
1772 t = static_cast<TempoSection*> (*i);
1780 if (t->note_types_per_minute() == tempo_bpm) {
1785 const double prev_t_bpm = prev_t->note_types_per_minute();
1786 const double prev_t_end_bpm = prev_t->end_note_types_per_minute();
1787 if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm)
1788 || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm)
1789 || (prev_t_end_bpm == tempo_bpm)) {
1791 return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
1798 return prev_t->minute();
1802 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1804 TempoSection* prev_t = 0;
1808 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1809 if ((*i)->is_tempo()) {
1810 t = static_cast<TempoSection*> (*i);
1814 if ((prev_t) && t->pulse() > pulse) {
1815 /* t is the section past frame */
1816 return prev_t->tempo_at_pulse (pulse);
1822 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1826 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1828 TempoSection* prev_t = 0;
1829 const double tempo_bpm = tempo.note_types_per_minute();
1831 Metrics::const_iterator i;
1833 for (i = metrics.begin(); i != metrics.end(); ++i) {
1835 if ((*i)->is_tempo()) {
1836 t = static_cast<TempoSection*> (*i);
1842 const double t_bpm = t->note_types_per_minute();
1844 if (t_bpm == tempo_bpm) {
1849 const double prev_t_bpm = prev_t->note_types_per_minute();
1851 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1852 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1859 return prev_t->pulse();
1862 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1863 * @param qn the position in quarter note beats.
1864 * @return the Tempo at the supplied quarter-note.
1867 TempoMap::tempo_at_quarter_note (const double& qn) const
1869 Glib::Threads::RWLock::ReaderLock lm (lock);
1871 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1874 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1875 * @param tempo the tempo.
1876 * @return the position in quarter-note beats where the map bpm
1877 * is equal to that of the Tempo. currently ignores note_type.
1880 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1882 Glib::Threads::RWLock::ReaderLock lm (lock);
1884 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1887 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1888 * @param metrics the list of metric sections used to calculate the pulse.
1889 * @param beat The BBT (meter-based) beat.
1890 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1892 * a pulse or whole note is the base musical position of a MetricSection.
1893 * it is equivalent to four quarter notes.
1897 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1899 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1901 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1904 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1905 * @param metrics the list of metric sections used to calculate the beat.
1906 * @param pulse the whole-note pulse.
1907 * @return the meter-based beat at the supplied whole-note pulse.
1909 * a pulse or whole note is the base musical position of a MetricSection.
1910 * it is equivalent to four quarter notes.
1913 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1915 MeterSection* prev_m = 0;
1917 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1919 if (!(*i)->is_tempo()) {
1920 m = static_cast<MeterSection*> (*i);
1921 if (prev_m && m->pulse() > pulse) {
1929 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1933 /* tempo section based */
1935 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1937 /* HOLD (at least) THE READER LOCK */
1938 TempoSection* prev_t = 0;
1940 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1942 if ((*i)->is_tempo()) {
1943 t = static_cast<TempoSection*> (*i);
1947 if (prev_t && t->minute() > minute) {
1948 /*the previous ts is the one containing the frame */
1949 const double ret = prev_t->pulse_at_minute (minute);
1950 /* audio locked section in new meter*/
1951 if (t->pulse() < ret) {
1960 /* treated as constant for this ts */
1961 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1963 return pulses_in_section + prev_t->pulse();
1966 /* tempo section based */
1968 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1970 /* HOLD THE READER LOCK */
1972 const TempoSection* prev_t = 0;
1974 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1977 if ((*i)->is_tempo()) {
1978 t = static_cast<TempoSection*> (*i);
1982 if (prev_t && t->pulse() > pulse) {
1983 return prev_t->minute_at_pulse (pulse);
1989 /* must be treated as constant, irrespective of _type */
1990 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1992 return dtime + prev_t->minute();
1995 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1996 * @param bbt The BBT time (meter-based).
1997 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
2001 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
2003 Glib::Threads::RWLock::ReaderLock lm (lock);
2004 return beat_at_bbt_locked (_metrics, bbt);
2009 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2011 /* CALLER HOLDS READ LOCK */
2013 MeterSection* prev_m = 0;
2015 /* because audio-locked meters have 'fake' integral beats,
2016 there is no pulse offset here.
2020 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2021 if (!(*i)->is_tempo()) {
2022 m = static_cast<MeterSection*> (*i);
2024 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2025 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2033 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2034 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2035 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2040 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2041 * @param beat The BBT (meter-based) beat.
2042 * @return The BBT time (meter-based) at the supplied meter-based beat.
2046 TempoMap::bbt_at_beat (const double& beat)
2048 Glib::Threads::RWLock::ReaderLock lm (lock);
2049 return bbt_at_beat_locked (_metrics, beat);
2053 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2055 /* CALLER HOLDS READ LOCK */
2056 MeterSection* prev_m = 0;
2057 const double beats = max (0.0, b);
2059 MeterSection* m = 0;
2061 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2062 if (!(*i)->is_tempo()) {
2063 m = static_cast<MeterSection*> (*i);
2065 if (m->beat() > beats) {
2066 /* this is the meter after the one our beat is on*/
2076 const double beats_in_ms = beats - prev_m->beat();
2077 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2078 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2079 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2080 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2084 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2085 ret.beats = (uint32_t) floor (remaining_beats);
2086 ret.bars = total_bars;
2088 /* 0 0 0 to 1 1 0 - based mapping*/
2092 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2094 ret.ticks -= BBT_Time::ticks_per_beat;
2097 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2105 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2106 * @param bbt The BBT time (meter-based).
2107 * @return the quarter note beat at the supplied BBT time
2109 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2111 * while the input uses meter, the output does not.
2114 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2116 Glib::Threads::RWLock::ReaderLock lm (lock);
2118 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2122 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2124 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2127 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2130 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2134 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2136 /* CALLER HOLDS READ LOCK */
2138 MeterSection* prev_m = 0;
2140 /* because audio-locked meters have 'fake' integral beats,
2141 there is no pulse offset here.
2145 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2146 if (!(*i)->is_tempo()) {
2147 m = static_cast<MeterSection*> (*i);
2149 if (m->bbt().bars > bbt.bars) {
2157 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2158 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2159 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2164 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2165 * @param qn the quarter-note beat.
2166 * @return The BBT time (meter-based) at the supplied meter-based beat.
2168 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2172 TempoMap::bbt_at_quarter_note (const double& qn)
2174 Glib::Threads::RWLock::ReaderLock lm (lock);
2176 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2179 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2180 * @param metrics The list of metric sections used to determine the result.
2181 * @param pulse The whole-note pulse.
2182 * @return The BBT time at the supplied whole-note pulse.
2184 * a pulse or whole note is the basic musical position of a MetricSection.
2185 * it is equivalent to four quarter notes.
2186 * while the output uses meter, the input does not.
2189 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2191 MeterSection* prev_m = 0;
2193 MeterSection* m = 0;
2195 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2197 if (!(*i)->is_tempo()) {
2198 m = static_cast<MeterSection*> (*i);
2201 double const pulses_to_m = m->pulse() - prev_m->pulse();
2202 if (prev_m->pulse() + pulses_to_m > pulse) {
2203 /* this is the meter after the one our beat is on*/
2214 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2215 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2216 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2217 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2218 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2222 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2223 ret.beats = (uint32_t) floor (remaining_beats);
2224 ret.bars = total_bars;
2226 /* 0 0 0 to 1 1 0 mapping*/
2230 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2232 ret.ticks -= BBT_Time::ticks_per_beat;
2235 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2243 /** Returns the BBT time corresponding to the supplied frame position.
2244 * @param frame the position in audio samples.
2245 * @return the BBT time at the frame position .
2249 TempoMap::bbt_at_frame (framepos_t frame)
2257 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2262 const double minute = minute_at_frame (frame);
2264 Glib::Threads::RWLock::ReaderLock lm (lock);
2266 return bbt_at_minute_locked (_metrics, minute);
2270 TempoMap::bbt_at_frame_rt (framepos_t frame)
2272 const double minute = minute_at_frame (frame);
2274 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2277 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2280 return bbt_at_minute_locked (_metrics, minute);
2284 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2294 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2295 MeterSection* prev_m = 0;
2296 MeterSection* next_m = 0;
2300 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2301 if (!(*i)->is_tempo()) {
2302 m = static_cast<MeterSection*> (*i);
2303 if (prev_m && m->minute() > minute) {
2311 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2313 /* handle frame before first meter */
2314 if (minute < prev_m->minute()) {
2317 /* audio locked meters fake their beat */
2318 if (next_m && next_m->beat() < beat) {
2319 beat = next_m->beat();
2322 beat = max (0.0, beat);
2324 const double beats_in_ms = beat - prev_m->beat();
2325 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2326 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2327 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2328 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2332 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2333 ret.beats = (uint32_t) floor (remaining_beats);
2334 ret.bars = total_bars;
2336 /* 0 0 0 to 1 1 0 - based mapping*/
2340 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2342 ret.ticks -= BBT_Time::ticks_per_beat;
2345 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2353 /** Returns the frame position corresponding to the supplied BBT time.
2354 * @param bbt the position in BBT time.
2355 * @return the frame position at bbt.
2359 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2363 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2368 if (bbt.beats < 1) {
2369 throw std::logic_error ("beats are counted from one");
2374 Glib::Threads::RWLock::ReaderLock lm (lock);
2375 minute = minute_at_bbt_locked (_metrics, bbt);
2378 return frame_at_minute (minute);
2381 /* meter & tempo section based */
2383 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2385 /* HOLD THE READER LOCK */
2387 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2392 * Returns the quarter-note beat position corresponding to the supplied frame.
2394 * @param frame the position in frames.
2395 * @return The quarter-note position of the supplied frame. Ignores meter.
2399 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2401 const double minute = minute_at_frame (frame);
2403 Glib::Threads::RWLock::ReaderLock lm (lock);
2405 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2409 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2411 const double minute = minute_at_frame (frame);
2413 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2416 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2419 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2423 * Returns the frame position corresponding to the supplied quarter-note beat.
2425 * @param quarter_note the quarter-note position.
2426 * @return the frame position of the supplied quarter-note. Ignores meter.
2431 TempoMap::frame_at_quarter_note (const double quarter_note) const
2435 Glib::Threads::RWLock::ReaderLock lm (lock);
2437 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2440 return frame_at_minute (minute);
2443 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2444 * @param beat The BBT (meter-based) beat.
2445 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2447 * a quarter-note may be compared with and assigned to Evoral::Beats.
2451 TempoMap::quarter_note_at_beat (const double beat) const
2453 Glib::Threads::RWLock::ReaderLock lm (lock);
2455 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2458 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2459 * @param quarter_note The position in quarter-note beats.
2460 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2462 * a quarter-note is the musical unit of Evoral::Beats.
2466 TempoMap::beat_at_quarter_note (const double quarter_note) const
2468 Glib::Threads::RWLock::ReaderLock lm (lock);
2470 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2473 /** Returns the duration in frames between two supplied quarter-note beat positions.
2474 * @param start the first position in quarter-note beats.
2475 * @param end the end position in quarter-note beats.
2476 * @return the frame distance ober the quarter-note beats duration.
2478 * use this rather than e.g.
2479 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2480 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2484 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2489 Glib::Threads::RWLock::ReaderLock lm (lock);
2490 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2493 return frame_at_minute (minutes);
2497 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2500 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2504 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2506 Glib::Threads::RWLock::ReaderLock lm (lock);
2508 return quarter_notes_between_frames_locked (_metrics, start, end);
2512 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2514 const TempoSection* prev_t = 0;
2516 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2519 if ((*i)->is_tempo()) {
2520 t = static_cast<TempoSection*> (*i);
2524 if (prev_t && t->frame() > start) {
2531 const double start_qn = prev_t->pulse_at_frame (start);
2533 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2536 if ((*i)->is_tempo()) {
2537 t = static_cast<TempoSection*> (*i);
2541 if (prev_t && t->frame() > end) {
2547 const double end_qn = prev_t->pulse_at_frame (end);
2549 return (end_qn - start_qn) * 4.0;
2553 TempoMap::check_solved (const Metrics& metrics) const
2555 TempoSection* prev_t = 0;
2556 MeterSection* prev_m = 0;
2558 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2561 if ((*i)->is_tempo()) {
2562 t = static_cast<TempoSection*> (*i);
2567 /* check ordering */
2568 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2572 /* precision check ensures tempo and frames align.*/
2573 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2574 if (!t->locked_to_meter()) {
2579 /* gradient limit - who knows what it should be?
2580 things are also ok (if a little chaotic) without this
2582 if (fabs (prev_t->c()) > 1000.0) {
2583 //std::cout << "c : " << prev_t->c() << std::endl;
2590 if (!(*i)->is_tempo()) {
2591 m = static_cast<MeterSection*> (*i);
2592 if (prev_m && m->position_lock_style() == AudioTime) {
2593 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2594 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2595 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2597 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2611 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2613 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2615 if ((*i)->is_tempo()) {
2616 t = static_cast<TempoSection*> (*i);
2617 if (t->locked_to_meter()) {
2618 t->set_active (true);
2619 } else if (t->position_lock_style() == AudioTime) {
2620 if (t->frame() < frame) {
2621 t->set_active (false);
2622 t->set_pulse (-1.0);
2623 } else if (t->frame() > frame) {
2624 t->set_active (true);
2625 } else if (t->frame() == frame) {
2635 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2637 TempoSection* prev_t = 0;
2638 TempoSection* section_prev = 0;
2639 double first_m_minute = 0.0;
2640 const bool sml = section->locked_to_meter();
2642 /* can't move a tempo before the first meter */
2643 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2645 if (!(*i)->is_tempo()) {
2646 m = static_cast<MeterSection*> (*i);
2648 first_m_minute = m->minute();
2653 if (!section->initial() && minute <= first_m_minute) {
2657 section->set_active (true);
2658 section->set_minute (minute);
2660 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2662 if ((*i)->is_tempo()) {
2663 t = static_cast<TempoSection*> (*i);
2675 if (t->frame() == frame_at_minute (minute)) {
2679 const bool tlm = t->position_lock_style() == MusicTime;
2681 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2682 section_prev = prev_t;
2684 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2685 if (!section->locked_to_meter()) {
2686 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2691 if (t->position_lock_style() == MusicTime) {
2692 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2693 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2695 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2696 if (!t->locked_to_meter()) {
2697 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2706 recompute_tempi (imaginary);
2708 if (check_solved (imaginary)) {
2711 dunp (imaginary, std::cout);
2715 MetricSectionFrameSorter fcmp;
2716 imaginary.sort (fcmp);
2718 recompute_tempi (imaginary);
2720 if (check_solved (imaginary)) {
2728 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2730 TempoSection* prev_t = 0;
2731 TempoSection* section_prev = 0;
2733 section->set_pulse (pulse);
2735 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2737 if ((*i)->is_tempo()) {
2738 t = static_cast<TempoSection*> (*i);
2749 section_prev = prev_t;
2753 if (t->position_lock_style() == MusicTime) {
2754 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2755 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2757 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2758 if (!t->locked_to_meter()) {
2759 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2768 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2769 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2773 recompute_tempi (imaginary);
2775 if (check_solved (imaginary)) {
2778 dunp (imaginary, std::cout);
2782 MetricSectionSorter cmp;
2783 imaginary.sort (cmp);
2785 recompute_tempi (imaginary);
2787 * XX need a restriction here, but only for this case,
2788 * as audio locked tempos don't interact in the same way.
2790 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2792 * |50 bpm |250 bpm |60 bpm
2793 * drag 250 to the pulse after 60->
2794 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2796 if (check_solved (imaginary)) {
2804 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2806 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2807 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2808 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2812 if (section->initial()) {
2813 /* lock the first tempo to our first meter */
2814 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2819 TempoSection* meter_locked_tempo = 0;
2821 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2823 if ((*ii)->is_tempo()) {
2824 t = static_cast<TempoSection*> (*ii);
2825 if (t->locked_to_meter() && t->frame() == section->frame()) {
2826 meter_locked_tempo = t;
2832 if (!meter_locked_tempo) {
2836 MeterSection* prev_m = 0;
2838 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2839 bool solved = false;
2841 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2843 if (!(*i)->is_tempo()) {
2844 m = static_cast<MeterSection*> (*i);
2846 if (prev_m && !section->initial()) {
2847 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2848 if (beats + prev_m->beat() < section->beat()) {
2849 /* set the section pulse according to its musical position,
2850 * as an earlier time than this has been requested.
2852 const double new_pulse = ((section->beat() - prev_m->beat())
2853 / prev_m->note_divisor()) + prev_m->pulse();
2855 tempo_copy->set_position_lock_style (MusicTime);
2856 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2857 meter_locked_tempo->set_position_lock_style (MusicTime);
2858 section->set_position_lock_style (MusicTime);
2859 section->set_pulse (new_pulse);
2860 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2861 meter_locked_tempo->set_position_lock_style (AudioTime);
2862 section->set_position_lock_style (AudioTime);
2863 section->set_minute (meter_locked_tempo->minute());
2869 Metrics::const_iterator d = future_map.begin();
2870 while (d != future_map.end()) {
2879 /* all is ok. set section's locked tempo if allowed.
2880 possibly disallow if there is an adjacent audio-locked tempo.
2881 XX this check could possibly go. its never actually happened here.
2883 MeterSection* meter_copy = const_cast<MeterSection*>
2884 (&meter_section_at_minute_locked (future_map, section->minute()));
2886 meter_copy->set_minute (minute);
2888 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2889 section->set_minute (minute);
2890 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2891 / prev_m->note_divisor()) + prev_m->pulse());
2892 solve_map_minute (imaginary, meter_locked_tempo, minute);
2897 Metrics::const_iterator d = future_map.begin();
2898 while (d != future_map.end()) {
2908 /* initial (first meter atm) */
2910 tempo_copy->set_minute (minute);
2911 tempo_copy->set_pulse (0.0);
2913 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2914 section->set_minute (minute);
2915 meter_locked_tempo->set_minute (minute);
2916 meter_locked_tempo->set_pulse (0.0);
2917 solve_map_minute (imaginary, meter_locked_tempo, minute);
2922 Metrics::const_iterator d = future_map.begin();
2923 while (d != future_map.end()) {
2932 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2933 section->set_beat (b_bbt);
2934 section->set_pulse (0.0);
2944 MetricSectionFrameSorter fcmp;
2945 imaginary.sort (fcmp);
2947 recompute_meters (imaginary);
2953 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2955 /* disallow setting section to an existing meter's bbt */
2956 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2958 if (!(*i)->is_tempo()) {
2959 m = static_cast<MeterSection*> (*i);
2960 if (m != section && m->bbt().bars == when.bars) {
2966 MeterSection* prev_m = 0;
2967 MeterSection* section_prev = 0;
2969 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2971 if (!(*i)->is_tempo()) {
2972 m = static_cast<MeterSection*> (*i);
2978 pair<double, BBT_Time> b_bbt;
2979 double new_pulse = 0.0;
2981 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2982 section_prev = prev_m;
2984 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2985 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2986 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2988 section->set_beat (b_bbt);
2989 section->set_pulse (pulse);
2990 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2994 if (m->position_lock_style() == AudioTime) {
2995 TempoSection* meter_locked_tempo = 0;
2997 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2999 if ((*ii)->is_tempo()) {
3000 t = static_cast<TempoSection*> (*ii);
3001 if (t->locked_to_meter() && t->frame() == m->frame()) {
3002 meter_locked_tempo = t;
3008 if (!meter_locked_tempo) {
3013 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3015 if (beats + prev_m->beat() != m->beat()) {
3016 /* tempo/ meter change caused a change in beat (bar). */
3018 /* the user has requested that the previous section of music overlaps this one.
3019 we have no choice but to change the bar number here, as being locked to audio means
3020 we must stay where we are on the timeline.
3022 beats = m->beat() - prev_m->beat();
3023 b_bbt = make_pair (beats + prev_m->beat()
3024 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3025 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3027 } else if (!m->initial()) {
3028 b_bbt = make_pair (m->beat(), m->bbt());
3029 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3032 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3035 meter_locked_tempo->set_pulse (new_pulse);
3036 m->set_beat (b_bbt);
3037 m->set_pulse (new_pulse);
3041 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3042 if (beats + prev_m->beat() != m->beat()) {
3043 /* tempo/ meter change caused a change in beat (bar). */
3044 b_bbt = make_pair (beats + prev_m->beat()
3045 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3047 b_bbt = make_pair (beats + prev_m->beat()
3050 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3051 m->set_beat (b_bbt);
3052 m->set_pulse (new_pulse);
3053 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3060 if (!section_prev) {
3062 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3063 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3064 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3066 section->set_beat (b_bbt);
3067 section->set_pulse (pulse);
3068 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3071 MetricSectionSorter cmp;
3072 imaginary.sort (cmp);
3074 recompute_meters (imaginary);
3079 /** places a copy of _metrics into copy and returns a pointer
3080 * to section's equivalent in copy.
3083 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section) const
3085 TempoSection* ret = 0;
3087 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3088 if ((*i)->is_tempo()) {
3089 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3091 ret = new TempoSection (*t);
3092 copy.push_back (ret);
3096 TempoSection* cp = new TempoSection (*t);
3097 copy.push_back (cp);
3099 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3100 MeterSection* cp = new MeterSection (*m);
3101 copy.push_back (cp);
3109 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section) const
3111 MeterSection* ret = 0;
3113 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3114 if ((*i)->is_tempo()) {
3115 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3116 TempoSection* cp = new TempoSection (*t);
3117 copy.push_back (cp);
3119 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3121 ret = new MeterSection (*m);
3122 copy.push_back (ret);
3125 MeterSection* cp = new MeterSection (*m);
3126 copy.push_back (cp);
3133 /** answers the question "is this a valid beat position for this tempo section?".
3134 * it returns true if the tempo section can be moved to the requested bbt position,
3135 * leaving the tempo map in a solved state.
3136 * @param ts the tempo section to be moved
3137 * @param bbt the requested new position for the tempo section
3138 * @return true if the tempo section can be moved to the position, otherwise false.
3141 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3144 TempoSection* tempo_copy = 0;
3147 Glib::Threads::RWLock::ReaderLock lm (lock);
3148 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3154 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3156 Metrics::const_iterator d = copy.begin();
3157 while (d != copy.end()) {
3166 * 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,
3167 * taking any possible reordering as a consequence of this into account.
3168 * @param section - the section to be altered
3169 * @param bbt - the BBT time where the altered tempo will fall
3170 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3172 pair<double, framepos_t>
3173 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3176 pair<double, framepos_t> ret = make_pair (0.0, 0);
3178 Glib::Threads::RWLock::ReaderLock lm (lock);
3180 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3182 const double beat = beat_at_bbt_locked (future_map, bbt);
3184 if (section->position_lock_style() == AudioTime) {
3185 tempo_copy->set_position_lock_style (MusicTime);
3188 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3189 ret.first = tempo_copy->pulse();
3190 ret.second = tempo_copy->frame();
3192 ret.first = section->pulse();
3193 ret.second = section->frame();
3196 Metrics::const_iterator d = future_map.begin();
3197 while (d != future_map.end()) {
3204 /** moves a TempoSection to a specified position.
3205 * @param ts - the section to be moved
3206 * @param frame - the new position in frames for the tempo
3207 * @param sub_num - the snap division to use if using musical time.
3209 * if sub_num is non-zero, the frame position is used to calculate an exact
3212 * -1 | snap to bars (meter-based)
3213 * 0 | no snap - use audio frame for musical position
3214 * 1 | snap to meter-based (BBT) beat
3215 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3217 * this follows the snap convention in the gui.
3218 * if sub_num is zero, the musical position will be taken from the supplied frame.
3221 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3225 if (ts->position_lock_style() == MusicTime) {
3227 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3228 Glib::Threads::RWLock::WriterLock lm (lock);
3229 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3231 tempo_copy->set_position_lock_style (AudioTime);
3233 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3234 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3235 const double pulse = pulse_at_beat_locked (future_map, beat);
3237 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3238 solve_map_pulse (_metrics, ts, pulse);
3239 recompute_meters (_metrics);
3247 Glib::Threads::RWLock::WriterLock lm (lock);
3248 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3250 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3252 /* We're moving the object that defines the grid while snapping to it...
3253 * Placing the ts at the beat corresponding to the requested frame may shift the
3254 * grid in such a way that the mouse is left hovering over a completerly different division,
3255 * causing jittering when the mouse next moves (esp. large tempo deltas).
3257 * This alters the snap behaviour slightly in that we snap to beat divisions
3258 * in the future map rather than the existing one.
3260 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3261 const framepos_t snapped_frame = frame_at_minute (minute_at_pulse_locked (future_map, qn / 4.0));
3263 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3264 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3265 ts->set_pulse (qn / 4.0);
3266 recompute_meters (_metrics);
3269 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3270 recompute_meters (_metrics);
3276 Metrics::const_iterator d = future_map.begin();
3277 while (d != future_map.end()) {
3282 MetricPositionChanged (PropertyChange ()); // Emit Signal
3285 /** moves a MeterSection to a specified position.
3286 * @param ms - the section to be moved
3287 * @param frame - the new position in frames for the meter
3289 * as a meter cannot snap to anything but bars,
3290 * the supplied frame is rounded to the nearest bar, possibly
3291 * leaving the meter position unchanged.
3294 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3298 if (ms->position_lock_style() == AudioTime) {
3301 Glib::Threads::RWLock::WriterLock lm (lock);
3302 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3304 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3305 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3306 recompute_tempi (_metrics);
3311 Glib::Threads::RWLock::WriterLock lm (lock);
3312 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3314 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3315 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3317 if (solve_map_bbt (future_map, copy, bbt)) {
3318 solve_map_bbt (_metrics, ms, bbt);
3319 recompute_tempi (_metrics);
3324 Metrics::const_iterator d = future_map.begin();
3325 while (d != future_map.end()) {
3330 MetricPositionChanged (PropertyChange ()); // Emit Signal
3334 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3337 bool can_solve = false;
3339 Glib::Threads::RWLock::WriterLock lm (lock);
3340 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3342 if (tempo_copy->type() == TempoSection::Constant) {
3343 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3344 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3346 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3347 tempo_copy->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3350 if (ts->clamped()) {
3351 TempoSection* prev = 0;
3352 if ((prev = previous_tempo_section_locked (future_map, tempo_copy)) != 0) {
3353 prev->set_end_note_types_per_minute (tempo_copy->note_types_per_minute());
3357 recompute_tempi (future_map);
3359 if (check_solved (future_map)) {
3360 if (ts->type() == TempoSection::Constant) {
3361 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3362 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3364 ts->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3365 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3368 if (ts->clamped()) {
3369 TempoSection* prev = 0;
3370 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3371 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3375 recompute_map (_metrics);
3380 Metrics::const_iterator d = future_map.begin();
3381 while (d != future_map.end()) {
3386 MetricPositionChanged (PropertyChange ()); // Emit Signal
3393 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3396 Ts (future prev_t) Tnext
3399 |----------|----------
3406 Glib::Threads::RWLock::WriterLock lm (lock);
3412 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3418 /* minimum allowed measurement distance in frames */
3419 framepos_t const min_dframe = 2;
3422 if (prev_t->clamped()) {
3423 TempoSection* next_t = next_tempo_section_locked (future_map, prev_t);
3424 TempoSection* prev_to_prev_t = previous_tempo_section_locked (future_map, prev_t);
3425 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3426 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3428 double contribution = 0.0;
3429 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3430 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3432 framepos_t const fr_off = (end_frame - frame);
3433 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3435 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3436 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3437 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3439 new_bpm = prev_t->note_types_per_minute();
3442 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3444 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
3445 / (double) (end_frame - prev_t->frame()));
3447 new_bpm = prev_t->note_types_per_minute();
3450 new_bpm = min (new_bpm, (double) 1000.0);
3452 /* don't clamp and proceed here.
3453 testing has revealed that this can go negative,
3454 which is an entirely different thing to just being too low.
3457 if (new_bpm < 0.5) {
3461 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3462 prev_t->set_note_types_per_minute (new_bpm);
3464 prev_t->set_end_note_types_per_minute (new_bpm);
3465 prev_t->set_note_types_per_minute (new_bpm);
3468 if (prev_t->clamped()) {
3469 TempoSection* prev = 0;
3470 if ((prev = previous_tempo_section_locked (future_map, prev_t)) != 0) {
3471 prev->set_end_note_types_per_minute (prev_t->note_types_per_minute());
3475 recompute_tempi (future_map);
3476 recompute_meters (future_map);
3478 if (check_solved (future_map)) {
3479 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3480 ts->set_note_types_per_minute (new_bpm);
3482 ts->set_end_note_types_per_minute (new_bpm);
3483 ts->set_note_types_per_minute (new_bpm);
3485 if (ts->clamped()) {
3486 TempoSection* prev = 0;
3487 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3488 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3491 recompute_tempi (_metrics);
3492 recompute_meters (_metrics);
3498 Metrics::const_iterator d = future_map.begin();
3499 while (d != future_map.end()) {
3503 MetricPositionChanged (PropertyChange ()); // Emit Signal
3508 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3511 Ts (future prev_t) Tnext
3514 |----------|----------
3521 Glib::Threads::RWLock::WriterLock lm (lock);
3527 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3533 /* minimum allowed measurement distance in frames */
3534 framepos_t const min_dframe = 2;
3537 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3538 new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->frame() - frame)
3539 / (double) (prev_t->frame() - end_frame));
3541 new_bpm = prev_t->end_note_types_per_minute();
3544 new_bpm = min (new_bpm, (double) 1000.0);
3546 if (new_bpm < 0.5) {
3550 prev_t->set_end_note_types_per_minute (new_bpm);
3552 TempoSection* next = 0;
3553 if ((next = next_tempo_section_locked (future_map, prev_t)) != 0) {
3554 if (next->clamped()) {
3555 next->set_note_types_per_minute (prev_t->end_note_types_per_minute());
3559 recompute_tempi (future_map);
3560 recompute_meters (future_map);
3562 if (check_solved (future_map)) {
3563 ts->set_end_note_types_per_minute (new_bpm);
3565 TempoSection* true_next = 0;
3566 if ((true_next = next_tempo_section_locked (_metrics, ts)) != 0) {
3567 if (true_next->clamped()) {
3568 true_next->set_note_types_per_minute (ts->end_note_types_per_minute());
3572 recompute_tempi (_metrics);
3573 recompute_meters (_metrics);
3579 Metrics::const_iterator d = future_map.begin();
3580 while (d != future_map.end()) {
3585 MetricPositionChanged (PropertyChange ()); // Emit Signal
3589 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
3591 TempoSection* next_t = 0;
3592 TempoSection* next_to_next_t = 0;
3594 bool can_solve = false;
3596 /* minimum allowed measurement distance in frames */
3597 framepos_t const min_dframe = 2;
3600 Glib::Threads::RWLock::WriterLock lm (lock);
3605 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3606 TempoSection* prev_to_prev_t = 0;
3607 const frameoffset_t fr_off = end_frame - frame;
3613 if (tempo_copy->pulse() > 0.0) {
3614 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
3617 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3618 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3619 next_t = static_cast<TempoSection*> (*i);
3628 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3629 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3630 next_to_next_t = static_cast<TempoSection*> (*i);
3635 if (!next_to_next_t) {
3639 double prev_contribution = 0.0;
3641 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3642 prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3645 const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
3648 framepos_t old_tc_minute = tempo_copy->minute();
3649 double old_next_minute = next_t->minute();
3650 double old_next_to_next_minute = next_to_next_t->minute();
3653 double new_next_bpm;
3654 double new_copy_end_bpm;
3656 if (frame > tempo_copy->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > tempo_copy->frame() + min_dframe) {
3657 new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
3658 / (double) (end_frame - tempo_copy->frame()));
3660 new_bpm = tempo_copy->note_types_per_minute();
3663 /* don't clamp and proceed here.
3664 testing has revealed that this can go negative,
3665 which is an entirely different thing to just being too low.
3667 if (new_bpm < 0.5) {
3671 new_bpm = min (new_bpm, (double) 1000.0);
3673 tempo_copy->set_note_types_per_minute (new_bpm);
3674 if (tempo_copy->type() == TempoSection::Constant) {
3675 tempo_copy->set_end_note_types_per_minute (new_bpm);
3678 recompute_tempi (future_map);
3680 if (check_solved (future_map)) {
3686 ts->set_note_types_per_minute (new_bpm);
3687 if (ts->type() == TempoSection::Constant) {
3688 ts->set_end_note_types_per_minute (new_bpm);
3691 recompute_map (_metrics);
3696 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3697 if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) {
3699 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3700 / (double) ((old_next_to_next_minute) - old_next_minute));
3703 new_next_bpm = next_t->note_types_per_minute();
3706 next_t->set_note_types_per_minute (new_next_bpm);
3707 recompute_tempi (future_map);
3709 if (check_solved (future_map)) {
3710 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3711 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3712 next_t = static_cast<TempoSection*> (*i);
3720 next_t->set_note_types_per_minute (new_next_bpm);
3721 recompute_map (_metrics);
3725 double next_frame_ratio = 1.0;
3726 double copy_frame_ratio = 1.0;
3728 if (next_to_next_t) {
3729 next_frame_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute - old_next_minute);
3731 copy_frame_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute));
3734 new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
3735 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
3737 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3739 if (next_t->clamped()) {
3740 next_t->set_note_types_per_minute (new_copy_end_bpm);
3742 next_t->set_note_types_per_minute (new_next_bpm);
3745 recompute_tempi (future_map);
3747 if (check_solved (future_map)) {
3748 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3749 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3750 next_t = static_cast<TempoSection*> (*i);
3759 if (next_t->clamped()) {
3760 next_t->set_note_types_per_minute (new_copy_end_bpm);
3762 next_t->set_note_types_per_minute (new_next_bpm);
3765 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3766 recompute_map (_metrics);
3772 Metrics::const_iterator d = future_map.begin();
3773 while (d != future_map.end()) {
3778 MetricPositionChanged (PropertyChange ()); // Emit Signal
3782 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3783 * the supplied frame, possibly returning a negative value.
3785 * @param frame The session frame position.
3786 * @param sub_num The subdivision to use when rounding the beat.
3787 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3788 * Positive integers indicate quarter note (non BBT) divisions.
3789 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3790 * @return The beat position of the supplied frame.
3792 * when working to a musical grid, the use of sub_nom indicates that
3793 * the position should be interpreted musically.
3795 * it effectively snaps to meter bars, meter beats or quarter note divisions
3796 * (as per current gui convention) and returns a musical position independent of frame rate.
3798 * If the supplied frame lies before the first meter, the return will be negative,
3799 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3800 * the continuation of the tempo curve (backwards).
3802 * This function is sensitive to tempo and meter.
3805 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3807 Glib::Threads::RWLock::ReaderLock lm (lock);
3809 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3813 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3815 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3818 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3819 * the supplied frame, possibly returning a negative value.
3821 * @param frame The session frame position.
3822 * @param sub_num The subdivision to use when rounding the quarter note.
3823 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3824 * Positive integers indicate quarter note (non BBT) divisions.
3825 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3826 * @return The quarter note position of the supplied frame.
3828 * When working to a musical grid, the use of sub_nom indicates that
3829 * the frame position should be interpreted musically.
3831 * it effectively snaps to meter bars, meter beats or quarter note divisions
3832 * (as per current gui convention) and returns a musical position independent of frame rate.
3834 * If the supplied frame lies before the first meter, the return will be negative,
3835 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3836 * the continuation of the tempo curve (backwards).
3838 * This function is tempo-sensitive.
3841 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3843 Glib::Threads::RWLock::ReaderLock lm (lock);
3845 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3849 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3851 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3854 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3855 } else if (sub_num == 1) {
3856 /* the gui requested exact musical (BBT) beat */
3857 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3858 } else if (sub_num == -1) {
3860 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3864 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3866 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3868 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3878 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3879 * @param pos the frame position in the tempo map.
3880 * @param bbt the distance in BBT time from pos to calculate.
3881 * @param dir the rounding direction..
3882 * @return the duration in frames between pos and bbt
3885 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3887 Glib::Threads::RWLock::ReaderLock lm (lock);
3889 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3891 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3894 pos_bbt.bars += bbt.bars;
3896 pos_bbt.ticks += bbt.ticks;
3897 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3899 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3902 pos_bbt.beats += bbt.beats;
3903 if ((double) pos_bbt.beats > divisions) {
3905 pos_bbt.beats -= divisions;
3907 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3909 return pos_bbt_frame - pos;
3913 if (pos_bbt.bars <= bbt.bars) {
3916 pos_bbt.bars -= bbt.bars;
3919 if (pos_bbt.ticks < bbt.ticks) {
3920 if (pos_bbt.bars > 1) {
3921 if (pos_bbt.beats == 1) {
3923 pos_bbt.beats = divisions;
3927 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3933 pos_bbt.ticks -= bbt.ticks;
3936 if (pos_bbt.beats <= bbt.beats) {
3937 if (pos_bbt.bars > 1) {
3939 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3944 pos_bbt.beats -= bbt.beats;
3947 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3954 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3956 return round_to_type (fr, dir, Bar);
3960 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3962 return round_to_type (fr, dir, Beat);
3966 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3968 Glib::Threads::RWLock::ReaderLock lm (lock);
3969 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);
3970 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3971 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3973 ticks -= beats * BBT_Time::ticks_per_beat;
3976 /* round to next (or same iff dir == RoundUpMaybe) */
3978 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3980 if (mod == 0 && dir == RoundUpMaybe) {
3981 /* right on the subdivision, which is fine, so do nothing */
3983 } else if (mod == 0) {
3984 /* right on the subdivision, so the difference is just the subdivision ticks */
3985 ticks += ticks_one_subdivisions_worth;
3988 /* not on subdivision, compute distance to next subdivision */
3990 ticks += ticks_one_subdivisions_worth - mod;
3993 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3994 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3995 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3996 // But I'm keeping it around, until we determine there are no terrible consequences.
3997 // if (ticks >= BBT_Time::ticks_per_beat) {
3998 // ticks -= BBT_Time::ticks_per_beat;
4001 } else if (dir < 0) {
4003 /* round to previous (or same iff dir == RoundDownMaybe) */
4005 uint32_t difference = ticks % ticks_one_subdivisions_worth;
4007 if (difference == 0 && dir == RoundDownAlways) {
4008 /* right on the subdivision, but force-rounding down,
4009 so the difference is just the subdivision ticks */
4010 difference = ticks_one_subdivisions_worth;
4013 if (ticks < difference) {
4014 ticks = BBT_Time::ticks_per_beat - ticks;
4016 ticks -= difference;
4020 /* round to nearest */
4023 /* compute the distance to the previous and next subdivision */
4025 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
4027 /* closer to the next subdivision, so shift forward */
4029 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
4031 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
4033 if (ticks > BBT_Time::ticks_per_beat) {
4035 ticks -= BBT_Time::ticks_per_beat;
4036 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
4039 } else if (rem > 0) {
4041 /* closer to previous subdivision, so shift backward */
4045 /* can't go backwards past zero, so ... */
4046 return MusicFrame (0, 0);
4048 /* step back to previous beat */
4050 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4051 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4053 ticks = lrint (ticks - rem);
4054 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4057 /* on the subdivision, do nothing */
4061 MusicFrame ret (0, 0);
4062 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4063 ret.division = sub_num;
4069 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
4071 Glib::Threads::RWLock::ReaderLock lm (lock);
4072 const double minute = minute_at_frame (frame);
4073 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4074 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
4075 MusicFrame ret (0, 0);
4082 /* find bar previous to 'frame' */
4088 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4092 } else if (dir > 0) {
4093 /* find bar following 'frame' */
4098 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4102 /* true rounding: find nearest bar */
4103 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4106 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4108 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4110 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4111 ret.frame = next_ft;
4116 ret.frame = prev_ft;
4128 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
4131 } else if (dir > 0) {
4132 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
4136 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
4143 return MusicFrame (0, 0);
4147 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4148 framepos_t lower, framepos_t upper, uint32_t bar_mod)
4150 Glib::Threads::RWLock::ReaderLock lm (lock);
4151 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
4153 /* although the map handles negative beats, bbt doesn't. */
4158 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
4162 while (pos >= 0 && pos < upper) {
4163 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
4164 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4165 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4166 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4168 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4172 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
4177 bbt.bars -= bbt.bars % bar_mod;
4181 while (pos >= 0 && pos < upper) {
4182 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4183 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4184 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4185 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4186 bbt.bars += bar_mod;
4192 TempoMap::tempo_section_at_frame (framepos_t frame) const
4194 Glib::Threads::RWLock::ReaderLock lm (lock);
4196 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4200 TempoMap::tempo_section_at_frame (framepos_t frame)
4202 Glib::Threads::RWLock::ReaderLock lm (lock);
4204 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4208 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4210 TempoSection* prev = 0;
4214 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4216 if ((*i)->is_tempo()) {
4217 t = static_cast<TempoSection*> (*i);
4221 if (prev && t->minute() > minute) {
4231 abort(); /*NOTREACHED*/
4237 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4239 TempoSection* prev = 0;
4243 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4245 if ((*i)->is_tempo()) {
4246 t = static_cast<TempoSection*> (*i);
4250 if (prev && t->minute() > minute) {
4260 abort(); /*NOTREACHED*/
4266 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4268 TempoSection* prev_t = 0;
4269 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4273 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4274 if ((*i)->is_tempo()) {
4275 t = static_cast<TempoSection*> (*i);
4281 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4292 TempoMap::previous_tempo_section (TempoSection* ts) const
4294 Glib::Threads::RWLock::ReaderLock lm (lock);
4296 return previous_tempo_section_locked (_metrics, ts);
4301 TempoMap::previous_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4307 TempoSection* prev = 0;
4309 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4311 if ((*i)->is_tempo()) {
4312 TempoSection* t = static_cast<TempoSection*> (*i);
4318 if (prev && t == ts) {
4329 abort(); /*NOTREACHED*/
4336 TempoMap::next_tempo_section (TempoSection* ts) const
4338 Glib::Threads::RWLock::ReaderLock lm (lock);
4340 return next_tempo_section_locked (_metrics, ts);
4344 TempoMap::next_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4350 TempoSection* prev = 0;
4352 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4354 if ((*i)->is_tempo()) {
4355 TempoSection* t = static_cast<TempoSection*> (*i);
4361 if (prev && prev == ts) {
4372 abort(); /*NOTREACHED*/
4377 /* don't use this to calculate length (the tempo is only correct for this frame).
4378 do that stuff based on the beat_at_frame and frame_at_beat api
4381 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4383 Glib::Threads::RWLock::ReaderLock lm (lock);
4385 const TempoSection* ts_at = 0;
4386 const TempoSection* ts_after = 0;
4387 Metrics::const_iterator i;
4390 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4392 if ((*i)->is_tempo()) {
4393 t = static_cast<TempoSection*> (*i);
4397 if (ts_at && (*i)->frame() > frame) {
4407 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4409 /* must be treated as constant tempo */
4410 return ts_at->frames_per_quarter_note (_frame_rate);
4414 TempoMap::meter_section_at_frame (framepos_t frame) const
4416 Glib::Threads::RWLock::ReaderLock lm (lock);
4417 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4421 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4423 Metrics::const_iterator i;
4424 MeterSection* prev = 0;
4428 for (i = metrics.begin(); i != metrics.end(); ++i) {
4430 if (!(*i)->is_tempo()) {
4431 m = static_cast<MeterSection*> (*i);
4433 if (prev && (*i)->minute() > minute) {
4443 abort(); /*NOTREACHED*/
4450 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4452 MeterSection* prev_m = 0;
4454 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4456 if (!(*i)->is_tempo()) {
4457 m = static_cast<MeterSection*> (*i);
4458 if (prev_m && m->beat() > beat) {
4469 TempoMap::meter_section_at_beat (double beat) const
4471 Glib::Threads::RWLock::ReaderLock lm (lock);
4472 return meter_section_at_beat_locked (_metrics, beat);
4476 TempoMap::meter_at_frame (framepos_t frame) const
4478 TempoMetric m (metric_at (frame));
4483 TempoMap::fix_legacy_session ()
4485 MeterSection* prev_m = 0;
4486 TempoSection* prev_t = 0;
4487 bool have_initial_t = false;
4489 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4493 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4495 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4498 m->set_minute (0.0);
4499 m->set_position_lock_style (AudioTime);
4504 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4505 + (m->bbt().beats - 1)
4506 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4508 m->set_beat (start);
4509 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4510 + (m->bbt().beats - 1)
4511 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4512 m->set_pulse (start_beat / prev_m->note_divisor());
4515 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4523 t->set_minute (0.0);
4524 t->set_position_lock_style (AudioTime);
4526 have_initial_t = true;
4531 /* some 4.x sessions have no initial (non-movable) tempo. */
4532 if (!have_initial_t) {
4533 prev_t->set_pulse (0.0);
4534 prev_t->set_minute (0.0);
4535 prev_t->set_position_lock_style (AudioTime);
4536 prev_t->set_initial (true);
4537 prev_t->set_locked_to_meter (true);
4538 have_initial_t = true;
4541 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4542 + (t->legacy_bbt().beats - 1)
4543 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4545 t->set_pulse (beat / prev_m->note_divisor());
4547 /* really shouldn't happen but.. */
4548 t->set_pulse (beat / 4.0);
4556 TempoMap::fix_legacy_end_session ()
4558 TempoSection* prev_t = 0;
4560 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4563 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4570 if (prev_t->type() != TempoSection::Constant) {
4571 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4581 TempoMap::get_state ()
4583 Metrics::const_iterator i;
4584 XMLNode *root = new XMLNode ("TempoMap");
4587 Glib::Threads::RWLock::ReaderLock lm (lock);
4588 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4589 root->add_child_nocopy ((*i)->get_state());
4597 TempoMap::set_state (const XMLNode& node, int /*version*/)
4600 Glib::Threads::RWLock::WriterLock lm (lock);
4603 XMLNodeConstIterator niter;
4604 Metrics old_metrics (_metrics);
4607 nlist = node.children();
4609 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4610 XMLNode* child = *niter;
4612 if (child->name() == TempoSection::xml_state_node_name) {
4615 TempoSection* ts = new TempoSection (*child, _frame_rate);
4616 _metrics.push_back (ts);
4619 catch (failed_constructor& err){
4620 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4621 _metrics = old_metrics;
4622 old_metrics.clear();
4626 } else if (child->name() == MeterSection::xml_state_node_name) {
4629 MeterSection* ms = new MeterSection (*child, _frame_rate);
4630 _metrics.push_back (ms);
4633 catch (failed_constructor& err) {
4634 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4635 _metrics = old_metrics;
4636 old_metrics.clear();
4642 if (niter == nlist.end()) {
4643 MetricSectionSorter cmp;
4644 _metrics.sort (cmp);
4647 /* check for legacy sessions where bbt was the base musical unit for tempo */
4648 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4650 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4651 if (t->legacy_bbt().bars != 0) {
4652 fix_legacy_session();
4656 if (t->legacy_end()) {
4657 fix_legacy_end_session();
4665 /* check for multiple tempo/meters at the same location, which
4666 ardour2 somehow allowed.
4669 Metrics::iterator prev = _metrics.end();
4670 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4671 if (prev != _metrics.end()) {
4673 MeterSection* prev_m;
4675 TempoSection* prev_t;
4676 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4677 if (prev_m->pulse() == ms->pulse()) {
4678 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4679 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4682 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4683 if (prev_t->pulse() == ts->pulse()) {
4684 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4685 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4693 recompute_map (_metrics);
4695 Metrics::const_iterator d = old_metrics.begin();
4696 while (d != old_metrics.end()) {
4700 old_metrics.clear ();
4703 PropertyChanged (PropertyChange ());
4709 TempoMap::dump (std::ostream& o) const
4711 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4712 const MeterSection* m;
4713 const TempoSection* t;
4714 const TempoSection* prev_t = 0;
4716 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4718 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4719 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4720 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4721 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4722 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4724 o << " current start : " << t->note_types_per_minute()
4725 << " current end : " << t->end_note_types_per_minute()
4726 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4727 o << " previous : " << prev_t->note_types_per_minute()
4728 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4729 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4730 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4731 << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4732 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4735 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4736 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4737 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4738 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4741 o << "------" << std::endl;
4745 TempoMap::n_tempos() const
4747 Glib::Threads::RWLock::ReaderLock lm (lock);
4750 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4751 if ((*i)->is_tempo()) {
4760 TempoMap::n_meters() const
4762 Glib::Threads::RWLock::ReaderLock lm (lock);
4765 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4766 if (!(*i)->is_tempo()) {
4775 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4777 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4778 if ((*i)->frame() >= where && !(*i)->initial ()) {
4782 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4783 gui_set_meter_position (ms, (*i)->frame() + amount);
4786 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4787 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4792 PropertyChanged (PropertyChange ());
4796 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4800 std::list<MetricSection*> metric_kill_list;
4802 TempoSection* last_tempo = NULL;
4803 MeterSection* last_meter = NULL;
4804 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4805 bool meter_after = false; // is there a meter marker likewise?
4807 Glib::Threads::RWLock::WriterLock lm (lock);
4808 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4809 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4810 metric_kill_list.push_back(*i);
4811 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4814 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4818 else if ((*i)->frame() >= where) {
4819 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4820 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4821 if ((*i)->frame() == where) {
4822 // marker was immediately after end of range
4823 tempo_after = dynamic_cast<TempoSection*> (*i);
4824 meter_after = dynamic_cast<MeterSection*> (*i);
4830 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4831 if (last_tempo && !tempo_after) {
4832 metric_kill_list.remove(last_tempo);
4833 last_tempo->set_minute (minute_at_frame (where));
4836 if (last_meter && !meter_after) {
4837 metric_kill_list.remove(last_meter);
4838 last_meter->set_minute (minute_at_frame (where));
4842 //remove all the remaining metrics
4843 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4844 _metrics.remove(*i);
4849 recompute_map (_metrics);
4852 PropertyChanged (PropertyChange ());
4856 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4857 * pos can be -ve, if required.
4860 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4862 Glib::Threads::RWLock::ReaderLock lm (lock);
4863 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4865 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4869 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4871 Glib::Threads::RWLock::ReaderLock lm (lock);
4873 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4874 pos_bbt.ticks += op.ticks;
4875 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4877 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4879 pos_bbt.beats += op.beats;
4880 /* the meter in effect will start on the bar */
4881 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();
4882 while (pos_bbt.beats >= divisions_per_bar + 1) {
4884 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4885 pos_bbt.beats -= divisions_per_bar;
4887 pos_bbt.bars += op.bars;
4889 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4892 /** Count the number of beats that are equivalent to distance when going forward,
4896 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4898 Glib::Threads::RWLock::ReaderLock lm (lock);
4900 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4904 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4910 operator<< (std::ostream& o, const Meter& m) {
4911 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4915 operator<< (std::ostream& o, const Tempo& t) {
4916 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4920 operator<< (std::ostream& o, const MetricSection& section) {
4922 o << "MetricSection @ " << section.frame() << ' ';
4924 const TempoSection* ts;
4925 const MeterSection* ms;
4927 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4928 o << *((const Tempo*) ts);
4929 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4930 o << *((const Meter*) ms);