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, start.second)) {
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);
571 node.get_property ("beat", start.first);
573 if (node.get_property ("bbt", bbt_str)) {
574 if (!string_to_bbt_time (bbt_str, start.second)) {
575 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
576 throw failed_constructor();
579 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
584 /* beats-per-bar is old; divisions-per-bar is new */
586 if (!node.get_property ("divisions-per-bar", _divisions_per_bar)) {
587 if (!node.get_property ("beats-per-bar", _divisions_per_bar)) {
588 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
589 throw failed_constructor();
593 if (_divisions_per_bar < 0.0) {
594 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
595 throw failed_constructor();
598 if (!node.get_property ("note-type", _note_type)) {
599 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
600 throw failed_constructor();
603 if (_note_type < 0.0) {
604 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
605 throw failed_constructor();
610 MeterSection::get_state() const
612 XMLNode *root = new XMLNode (xml_state_node_name);
615 MetricSection::add_state_to_node (*root);
618 bbt_time_to_string (_bbt, bbt_str);
619 root->set_property ("bbt", bbt_str);
620 root->set_property ("beat", beat());
621 root->set_property ("note-type", _note_type);
622 root->set_property ("divisions-per-bar", _divisions_per_bar);
627 /***********************************************************************/
631 Tempo determines the rate of musical pulse determined by its components
632 note types per minute - the rate per minute of the whole note divisor _note_type
633 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
634 Meter divides the musical pulse into measures and beats according to its components
638 TempoSection - translates between time, musical pulse and tempo.
639 has a musical location in whole notes (pulses).
640 has a time location in minutes.
641 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
642 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
644 MeterSection - translates between BBT, meter-based beat and musical pulse.
645 has a musical location in whole notes (pulses)
646 has a musical location in meter-based beats
647 has a musical location in BBT time
648 has a time location expressed in minutes.
650 TempoSection and MeterSection may be locked to either audio or music (position lock style).
651 The lock style determines the location type to be kept as a reference when location is recalculated.
653 The first tempo and meter are special. they must move together, and are locked to audio.
654 Audio locked tempi which lie before the first meter are made inactive.
656 Recomputing the map is the process where the 'missing' location types are calculated.
657 We construct the tempo map by first using the locked location type of each section
658 to determine non-locked location types (pulse or minute position).
659 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
661 Having done this, we can now traverse the Metrics list by pulse or minute
662 to query its relevant meter/tempo.
664 It is important to keep the _metrics in an order that makes sense.
665 Because ramped MusicTime and AudioTime tempos can interact with each other,
666 reordering is frequent. Care must be taken to keep _metrics in a solved state.
667 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
671 Music and audio-locked objects may seem interchangeable on the surface, but when translating
672 between audio samples and beat, remember that a sample is only a quantised approximation
673 of the actual time (in minutes) of a beat.
674 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
675 mean that this frame is the actual location in time of 1|3|0.
677 You cannot use a frame measurement to determine beat distance except under special circumstances
678 (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).
680 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
681 sample space the user is operating at to be translated correctly to the object.
683 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
684 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
685 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
687 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
689 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
690 The result is rounded to audio frames.
691 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
694 frame_at_beat (beat_at_frame (frame)) == frame
696 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
698 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
699 frames_between_quarter_notes () eliminats this effect when determining time duration
700 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
702 The above pointless example could instead do:
703 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
705 The Shaggs - Things I Wonder
706 https://www.youtube.com/watch?v=9wQK6zMJOoQ
709 struct MetricSectionSorter {
710 bool operator() (const MetricSection* a, const MetricSection* b) {
711 return a->pulse() < b->pulse();
715 struct MetricSectionFrameSorter {
716 bool operator() (const MetricSection* a, const MetricSection* b) {
717 return a->frame() < b->frame();
721 TempoMap::TempoMap (framecnt_t fr)
724 BBT_Time start (1, 1, 0);
726 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo, AudioTime, fr);
727 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
729 t->set_initial (true);
730 t->set_locked_to_meter (true);
732 m->set_initial (true);
734 /* note: frame time is correct (zero) for both of these */
736 _metrics.push_back (t);
737 _metrics.push_back (m);
742 TempoMap::operator= (TempoMap const & other)
744 if (&other != this) {
745 Glib::Threads::RWLock::ReaderLock lr (other.lock);
746 Glib::Threads::RWLock::WriterLock lm (lock);
747 _frame_rate = other._frame_rate;
749 Metrics::const_iterator d = _metrics.begin();
750 while (d != _metrics.end()) {
756 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
757 TempoSection const * const ts = dynamic_cast<TempoSection const * const> (*m);
758 MeterSection const * const ms = dynamic_cast<MeterSection const * const> (*m);
761 TempoSection* new_section = new TempoSection (*ts);
762 _metrics.push_back (new_section);
764 MeterSection* new_section = new MeterSection (*ms);
765 _metrics.push_back (new_section);
770 PropertyChanged (PropertyChange());
775 TempoMap::~TempoMap ()
777 Metrics::const_iterator d = _metrics.begin();
778 while (d != _metrics.end()) {
786 TempoMap::frame_at_minute (const double time) const
788 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
792 TempoMap::minute_at_frame (const framepos_t frame) const
794 return (frame / (double) _frame_rate) / 60.0;
798 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
800 bool removed = false;
803 Glib::Threads::RWLock::WriterLock lm (lock);
804 if ((removed = remove_tempo_locked (tempo))) {
805 if (complete_operation) {
806 recompute_map (_metrics);
811 if (removed && complete_operation) {
812 PropertyChanged (PropertyChange ());
817 TempoMap::remove_tempo_locked (const TempoSection& tempo)
821 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
822 if (dynamic_cast<TempoSection*> (*i) != 0) {
823 if (tempo.frame() == (*i)->frame()) {
824 if (!(*i)->initial()) {
837 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
839 bool removed = false;
842 Glib::Threads::RWLock::WriterLock lm (lock);
843 if ((removed = remove_meter_locked (tempo))) {
844 if (complete_operation) {
845 recompute_map (_metrics);
850 if (removed && complete_operation) {
851 PropertyChanged (PropertyChange ());
856 TempoMap::remove_meter_locked (const MeterSection& meter)
859 if (meter.position_lock_style() == AudioTime) {
860 /* remove meter-locked tempo */
861 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
863 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
864 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
873 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
874 if (dynamic_cast<MeterSection*> (*i) != 0) {
875 if (meter.frame() == (*i)->frame()) {
876 if (!(*i)->initial()) {
889 TempoMap::do_insert (MetricSection* section)
891 bool need_add = true;
892 /* we only allow new meters to be inserted on beat 1 of an existing
896 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
898 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
900 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
901 corrected.second.beats = 1;
902 corrected.second.ticks = 0;
903 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
904 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
905 m->bbt(), corrected.second) << endmsg;
906 //m->set_pulse (corrected);
910 /* Look for any existing MetricSection that is of the same type and
911 in the same bar as the new one, and remove it before adding
912 the new one. Note that this means that if we find a matching,
913 existing section, we can break out of the loop since we're
914 guaranteed that there is only one such match.
917 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
919 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
920 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
921 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
922 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
924 if (tempo && insert_tempo) {
927 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
928 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
930 if (tempo->initial()) {
932 /* can't (re)move this section, so overwrite
933 * its data content (but not its properties as
937 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
938 (*i)->set_position_lock_style (AudioTime);
947 } else if (meter && insert_meter) {
951 bool const ipm = insert_meter->position_lock_style() == MusicTime;
953 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
955 if (meter->initial()) {
957 /* can't (re)move this section, so overwrite
958 * its data content (but not its properties as
962 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
963 (*i)->set_position_lock_style (AudioTime);
973 /* non-matching types, so we don't care */
977 /* Add the given MetricSection, if we didn't just reset an existing
982 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
983 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
987 TempoSection* prev_t = 0;
989 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
990 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
991 bool const ipm = insert_meter->position_lock_style() == MusicTime;
994 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
998 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
1002 prev_t = dynamic_cast<TempoSection*> (*i);
1005 } else if (insert_tempo) {
1006 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1007 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1010 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1011 const bool lm = insert_tempo->locked_to_meter();
1012 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1013 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1020 _metrics.insert (i, section);
1024 /* user supplies the exact pulse if pls == MusicTime */
1026 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1028 if (tempo.note_types_per_minute() <= 0.0) {
1029 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1033 TempoSection* ts = 0;
1034 TempoSection* prev_tempo = 0;
1036 Glib::Threads::RWLock::WriterLock lm (lock);
1037 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true);
1038 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1040 if ((*i)->is_tempo()) {
1041 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1043 bool const ipm = ts->position_lock_style() == MusicTime;
1044 bool const lm = ts->locked_to_meter();
1045 if ((ipm && this_t->pulse() == ts->pulse()) || (!ipm && this_t->frame() == ts->frame())
1046 || (lm && this_t->pulse() == ts->pulse())) {
1047 if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1048 prev_tempo->set_end_note_types_per_minute (ts->note_types_per_minute());
1052 prev_tempo = this_t;
1055 recompute_map (_metrics);
1058 PropertyChanged (PropertyChange ());
1064 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1066 if (tempo.note_types_per_minute() <= 0.0) {
1067 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1071 bool const locked_to_meter = ts.locked_to_meter();
1072 bool const ts_clamped = ts.clamped();
1073 TempoSection* new_ts = 0;
1076 Glib::Threads::RWLock::WriterLock lm (lock);
1077 TempoSection& first (first_tempo());
1078 if (!ts.initial()) {
1079 if (locked_to_meter) {
1081 /* cannot move a meter-locked tempo section */
1082 *static_cast<Tempo*>(&ts) = tempo;
1083 recompute_map (_metrics);
1086 remove_tempo_locked (ts);
1087 new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter);
1088 new_ts->set_clamped (ts_clamped);
1090 if (new_ts && new_ts->type() == TempoSection::Constant) {
1091 new_ts->set_end_note_types_per_minute (new_ts->note_types_per_minute());
1093 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1095 if ((*i)->is_tempo()) {
1096 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1098 bool const ipm = new_ts->position_lock_style() == MusicTime;
1099 bool const lm = new_ts->locked_to_meter();
1100 if ((ipm && this_t->pulse() > new_ts->pulse()) || (!ipm && this_t->frame() > new_ts->frame())
1101 || (lm && this_t->pulse() > new_ts->pulse())) {
1102 new_ts->set_end_note_types_per_minute (tempo.end_note_types_per_minute());
1112 first.set_pulse (0.0);
1113 first.set_minute (minute_at_frame (frame));
1114 first.set_position_lock_style (AudioTime);
1115 first.set_locked_to_meter (true);
1116 first.set_clamped (ts_clamped);
1118 /* cannot move the first tempo section */
1119 *static_cast<Tempo*>(&first) = tempo;
1122 recompute_map (_metrics);
1125 PropertyChanged (PropertyChange ());
1129 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1130 , PositionLockStyle pls, bool recompute, bool locked_to_meter)
1132 TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _frame_rate);
1133 t->set_locked_to_meter (locked_to_meter);
1138 if (pls == AudioTime) {
1139 solve_map_minute (_metrics, t, t->minute());
1141 solve_map_pulse (_metrics, t, t->pulse());
1143 recompute_meters (_metrics);
1150 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1152 MeterSection* m = 0;
1154 Glib::Threads::RWLock::WriterLock lm (lock);
1155 m = add_meter_locked (meter, beat, where, frame, pls, true);
1160 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1165 PropertyChanged (PropertyChange ());
1170 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1173 Glib::Threads::RWLock::WriterLock lm (lock);
1174 const double beat = beat_at_bbt_locked (_metrics, where);
1176 if (!ms.initial()) {
1177 remove_meter_locked (ms);
1178 add_meter_locked (meter, beat, where, frame, pls, true);
1180 MeterSection& first (first_meter());
1181 TempoSection& first_t (first_tempo());
1182 /* cannot move the first meter section */
1183 *static_cast<Meter*>(&first) = meter;
1184 first.set_position_lock_style (AudioTime);
1185 first.set_pulse (0.0);
1186 first.set_minute (minute_at_frame (frame));
1187 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1188 first.set_beat (beat);
1189 first_t.set_minute (first.minute());
1190 first_t.set_locked_to_meter (true);
1191 first_t.set_pulse (0.0);
1192 first_t.set_position_lock_style (AudioTime);
1193 recompute_map (_metrics);
1197 PropertyChanged (PropertyChange ());
1201 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1203 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1204 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1205 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1206 TempoSection* mlt = 0;
1208 if (pls == AudioTime) {
1209 /* add meter-locked tempo */
1210 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), AudioTime, true, true);
1218 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1220 bool solved = false;
1222 do_insert (new_meter);
1226 if (pls == AudioTime) {
1227 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1228 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1229 fudge frame so that the meter ends up at its BBT position instead.
1232 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1235 solved = solve_map_bbt (_metrics, new_meter, where);
1236 /* required due to resetting the pulse of meter-locked tempi above.
1237 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1238 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1240 recompute_map (_metrics);
1244 if (!solved && recompute) {
1245 /* if this has failed to solve, there is little we can do other than to ensure that
1246 the new map is recalculated.
1248 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1249 recompute_map (_metrics);
1256 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1258 Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1261 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1262 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1267 Glib::Threads::RWLock::WriterLock lm (lock);
1268 *((Tempo*) t) = newtempo;
1269 recompute_map (_metrics);
1271 PropertyChanged (PropertyChange ());
1278 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1280 Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1283 TempoSection* first;
1284 Metrics::iterator i;
1286 /* find the TempoSection immediately preceding "where"
1289 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1291 if ((*i)->frame() > where) {
1297 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1310 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1320 Glib::Threads::RWLock::WriterLock lm (lock);
1321 /* cannot move the first tempo section */
1322 *((Tempo*)prev) = newtempo;
1323 recompute_map (_metrics);
1326 PropertyChanged (PropertyChange ());
1330 TempoMap::first_meter () const
1332 const MeterSection *m = 0;
1334 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1335 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1340 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1341 abort(); /*NOTREACHED*/
1346 TempoMap::first_meter ()
1348 MeterSection *m = 0;
1350 /* CALLER MUST HOLD LOCK */
1352 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1353 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1358 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1359 abort(); /*NOTREACHED*/
1364 TempoMap::first_tempo () const
1366 const TempoSection *t = 0;
1368 /* CALLER MUST HOLD LOCK */
1370 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1371 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1381 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1382 abort(); /*NOTREACHED*/
1387 TempoMap::first_tempo ()
1389 TempoSection *t = 0;
1391 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1392 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1402 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1403 abort(); /*NOTREACHED*/
1407 TempoMap::recompute_tempi (Metrics& metrics)
1409 TempoSection* prev_t = 0;
1411 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1414 if ((*i)->is_tempo()) {
1415 t = static_cast<TempoSection*> (*i);
1427 if (t->position_lock_style() == AudioTime) {
1428 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1429 if (!t->locked_to_meter()) {
1430 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1434 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1435 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1443 prev_t->set_c (0.0);
1446 /* tempos must be positioned correctly.
1447 the current approach is to use a meter's bbt time as its base position unit.
1448 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1449 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1452 TempoMap::recompute_meters (Metrics& metrics)
1454 MeterSection* meter = 0;
1455 MeterSection* prev_m = 0;
1457 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1458 if (!(*mi)->is_tempo()) {
1459 meter = static_cast<MeterSection*> (*mi);
1460 if (meter->position_lock_style() == AudioTime) {
1462 pair<double, BBT_Time> b_bbt;
1463 TempoSection* meter_locked_tempo = 0;
1464 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1466 if ((*ii)->is_tempo()) {
1467 t = static_cast<TempoSection*> (*ii);
1468 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1469 meter_locked_tempo = t;
1476 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1477 if (beats + prev_m->beat() != meter->beat()) {
1478 /* reordering caused a bbt change */
1480 beats = meter->beat() - prev_m->beat();
1481 b_bbt = make_pair (beats + prev_m->beat()
1482 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1483 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1485 } else if (!meter->initial()) {
1486 b_bbt = make_pair (meter->beat(), meter->bbt());
1487 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1490 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1492 if (meter_locked_tempo) {
1493 meter_locked_tempo->set_pulse (pulse);
1495 meter->set_beat (b_bbt);
1496 meter->set_pulse (pulse);
1501 pair<double, BBT_Time> b_bbt;
1503 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1504 if (beats + prev_m->beat() != meter->beat()) {
1505 /* reordering caused a bbt change */
1506 b_bbt = make_pair (beats + prev_m->beat()
1507 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1509 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1511 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1513 /* shouldn't happen - the first is audio-locked */
1514 pulse = pulse_at_beat_locked (metrics, meter->beat());
1515 b_bbt = make_pair (meter->beat(), meter->bbt());
1518 meter->set_beat (b_bbt);
1519 meter->set_pulse (pulse);
1520 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1529 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1531 /* CALLER MUST HOLD WRITE LOCK */
1533 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1536 /* silly call from Session::process() during startup
1541 recompute_tempi (metrics);
1542 recompute_meters (metrics);
1546 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1548 Glib::Threads::RWLock::ReaderLock lm (lock);
1549 TempoMetric m (first_meter(), first_tempo());
1552 *last = ++_metrics.begin();
1555 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1556 at something, because we insert the default tempo and meter during
1557 TempoMap construction.
1559 now see if we can find better candidates.
1562 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1564 if ((*i)->frame() > frame) {
1578 /* XX meters only */
1580 TempoMap::metric_at (BBT_Time bbt) const
1582 Glib::Threads::RWLock::ReaderLock lm (lock);
1583 TempoMetric m (first_meter(), first_tempo());
1585 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1586 at something, because we insert the default tempo and meter during
1587 TempoMap construction.
1589 now see if we can find better candidates.
1592 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1594 if (!(*i)->is_tempo()) {
1595 mw = static_cast<MeterSection*> (*i);
1596 BBT_Time section_start (mw->bbt());
1598 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1609 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1610 * @param frame The session frame position.
1611 * @return The beat duration according to the tempo map at the supplied frame.
1613 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1614 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1616 * This function uses both tempo and meter.
1619 TempoMap::beat_at_frame (const framecnt_t& frame) const
1621 Glib::Threads::RWLock::ReaderLock lm (lock);
1623 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1626 /* This function uses both tempo and meter.*/
1628 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1630 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1631 MeterSection* prev_m = 0;
1632 MeterSection* next_m = 0;
1634 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1635 if (!(*i)->is_tempo()) {
1636 if (prev_m && (*i)->minute() > minute) {
1637 next_m = static_cast<MeterSection*> (*i);
1640 prev_m = static_cast<MeterSection*> (*i);
1644 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1646 /* audio locked meters fake their beat */
1647 if (next_m && next_m->beat() < beat) {
1648 return next_m->beat();
1654 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1655 * @param beat The BBT (meter-based) beat.
1656 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1658 * This function uses both tempo and meter.
1661 TempoMap::frame_at_beat (const double& beat) const
1663 Glib::Threads::RWLock::ReaderLock lm (lock);
1665 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1668 /* meter & tempo section based */
1670 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1672 MeterSection* prev_m = 0;
1673 TempoSection* prev_t = 0;
1677 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1678 if (!(*i)->is_tempo()) {
1679 m = static_cast<MeterSection*> (*i);
1680 if (prev_m && m->beat() > beat) {
1690 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1691 if ((*i)->is_tempo()) {
1692 t = static_cast<TempoSection*> (*i);
1698 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1707 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1710 /** Returns a Tempo corresponding to the supplied frame position.
1711 * @param frame The audio frame.
1712 * @return a Tempo according to the tempo map at the supplied frame.
1716 TempoMap::tempo_at_frame (const framepos_t& frame) const
1718 Glib::Threads::RWLock::ReaderLock lm (lock);
1720 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1724 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1726 TempoSection* prev_t = 0;
1730 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1731 if ((*i)->is_tempo()) {
1732 t = static_cast<TempoSection*> (*i);
1736 if ((prev_t) && t->minute() > minute) {
1737 /* t is the section past frame */
1738 return prev_t->tempo_at_minute (minute);
1744 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1747 /** returns the frame at which the supplied tempo occurs, or
1748 * the frame of the last tempo section (search exhausted)
1749 * only the position of the first occurence will be returned
1753 TempoMap::frame_at_tempo (const Tempo& tempo) const
1755 Glib::Threads::RWLock::ReaderLock lm (lock);
1757 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1761 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1763 TempoSection* prev_t = 0;
1764 const double tempo_bpm = tempo.note_types_per_minute();
1766 Metrics::const_iterator i;
1768 for (i = metrics.begin(); i != metrics.end(); ++i) {
1770 if ((*i)->is_tempo()) {
1771 t = static_cast<TempoSection*> (*i);
1779 if (t->note_types_per_minute() == tempo_bpm) {
1784 const double prev_t_bpm = prev_t->note_types_per_minute();
1785 const double prev_t_end_bpm = prev_t->end_note_types_per_minute();
1786 if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm)
1787 || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm)
1788 || (prev_t_end_bpm == tempo_bpm)) {
1790 return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
1797 return prev_t->minute();
1801 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1803 TempoSection* prev_t = 0;
1807 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1808 if ((*i)->is_tempo()) {
1809 t = static_cast<TempoSection*> (*i);
1813 if ((prev_t) && t->pulse() > pulse) {
1814 /* t is the section past frame */
1815 return prev_t->tempo_at_pulse (pulse);
1821 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1825 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1827 TempoSection* prev_t = 0;
1828 const double tempo_bpm = tempo.note_types_per_minute();
1830 Metrics::const_iterator i;
1832 for (i = metrics.begin(); i != metrics.end(); ++i) {
1834 if ((*i)->is_tempo()) {
1835 t = static_cast<TempoSection*> (*i);
1841 const double t_bpm = t->note_types_per_minute();
1843 if (t_bpm == tempo_bpm) {
1848 const double prev_t_bpm = prev_t->note_types_per_minute();
1850 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1851 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1858 return prev_t->pulse();
1861 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1862 * @param qn the position in quarter note beats.
1863 * @return the Tempo at the supplied quarter-note.
1866 TempoMap::tempo_at_quarter_note (const double& qn) const
1868 Glib::Threads::RWLock::ReaderLock lm (lock);
1870 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1873 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1874 * @param tempo the tempo.
1875 * @return the position in quarter-note beats where the map bpm
1876 * is equal to that of the Tempo. currently ignores note_type.
1879 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1881 Glib::Threads::RWLock::ReaderLock lm (lock);
1883 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1886 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1887 * @param metrics the list of metric sections used to calculate the pulse.
1888 * @param beat The BBT (meter-based) beat.
1889 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1891 * a pulse or whole note is the base musical position of a MetricSection.
1892 * it is equivalent to four quarter notes.
1896 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1898 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1900 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1903 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1904 * @param metrics the list of metric sections used to calculate the beat.
1905 * @param pulse the whole-note pulse.
1906 * @return the meter-based beat at the supplied whole-note pulse.
1908 * a pulse or whole note is the base musical position of a MetricSection.
1909 * it is equivalent to four quarter notes.
1912 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1914 MeterSection* prev_m = 0;
1916 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1918 if (!(*i)->is_tempo()) {
1919 m = static_cast<MeterSection*> (*i);
1920 if (prev_m && m->pulse() > pulse) {
1928 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1932 /* tempo section based */
1934 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1936 /* HOLD (at least) THE READER LOCK */
1937 TempoSection* prev_t = 0;
1939 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1941 if ((*i)->is_tempo()) {
1942 t = static_cast<TempoSection*> (*i);
1946 if (prev_t && t->minute() > minute) {
1947 /*the previous ts is the one containing the frame */
1948 const double ret = prev_t->pulse_at_minute (minute);
1949 /* audio locked section in new meter*/
1950 if (t->pulse() < ret) {
1959 /* treated as constant for this ts */
1960 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1962 return pulses_in_section + prev_t->pulse();
1965 /* tempo section based */
1967 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1969 /* HOLD THE READER LOCK */
1971 const TempoSection* prev_t = 0;
1973 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1976 if ((*i)->is_tempo()) {
1977 t = static_cast<TempoSection*> (*i);
1981 if (prev_t && t->pulse() > pulse) {
1982 return prev_t->minute_at_pulse (pulse);
1988 /* must be treated as constant, irrespective of _type */
1989 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1991 return dtime + prev_t->minute();
1994 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1995 * @param bbt The BBT time (meter-based).
1996 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
2000 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
2002 Glib::Threads::RWLock::ReaderLock lm (lock);
2003 return beat_at_bbt_locked (_metrics, bbt);
2008 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2010 /* CALLER HOLDS READ LOCK */
2012 MeterSection* prev_m = 0;
2014 /* because audio-locked meters have 'fake' integral beats,
2015 there is no pulse offset here.
2019 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2020 if (!(*i)->is_tempo()) {
2021 m = static_cast<MeterSection*> (*i);
2023 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2024 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2032 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2033 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2034 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2039 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2040 * @param beat The BBT (meter-based) beat.
2041 * @return The BBT time (meter-based) at the supplied meter-based beat.
2045 TempoMap::bbt_at_beat (const double& beat)
2047 Glib::Threads::RWLock::ReaderLock lm (lock);
2048 return bbt_at_beat_locked (_metrics, beat);
2052 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2054 /* CALLER HOLDS READ LOCK */
2055 MeterSection* prev_m = 0;
2056 const double beats = max (0.0, b);
2058 MeterSection* m = 0;
2060 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2061 if (!(*i)->is_tempo()) {
2062 m = static_cast<MeterSection*> (*i);
2064 if (m->beat() > beats) {
2065 /* this is the meter after the one our beat is on*/
2075 const double beats_in_ms = beats - prev_m->beat();
2076 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2077 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2078 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2079 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2083 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2084 ret.beats = (uint32_t) floor (remaining_beats);
2085 ret.bars = total_bars;
2087 /* 0 0 0 to 1 1 0 - based mapping*/
2091 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2093 ret.ticks -= BBT_Time::ticks_per_beat;
2096 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2104 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2105 * @param bbt The BBT time (meter-based).
2106 * @return the quarter note beat at the supplied BBT time
2108 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2110 * while the input uses meter, the output does not.
2113 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2115 Glib::Threads::RWLock::ReaderLock lm (lock);
2117 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2121 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2123 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2126 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2129 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2133 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2135 /* CALLER HOLDS READ LOCK */
2137 MeterSection* prev_m = 0;
2139 /* because audio-locked meters have 'fake' integral beats,
2140 there is no pulse offset here.
2144 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2145 if (!(*i)->is_tempo()) {
2146 m = static_cast<MeterSection*> (*i);
2148 if (m->bbt().bars > bbt.bars) {
2156 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2157 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2158 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2163 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2164 * @param qn the quarter-note beat.
2165 * @return The BBT time (meter-based) at the supplied meter-based beat.
2167 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2171 TempoMap::bbt_at_quarter_note (const double& qn)
2173 Glib::Threads::RWLock::ReaderLock lm (lock);
2175 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2178 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2179 * @param metrics The list of metric sections used to determine the result.
2180 * @param pulse The whole-note pulse.
2181 * @return The BBT time at the supplied whole-note pulse.
2183 * a pulse or whole note is the basic musical position of a MetricSection.
2184 * it is equivalent to four quarter notes.
2185 * while the output uses meter, the input does not.
2188 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2190 MeterSection* prev_m = 0;
2192 MeterSection* m = 0;
2194 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2196 if (!(*i)->is_tempo()) {
2197 m = static_cast<MeterSection*> (*i);
2200 double const pulses_to_m = m->pulse() - prev_m->pulse();
2201 if (prev_m->pulse() + pulses_to_m > pulse) {
2202 /* this is the meter after the one our beat is on*/
2213 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2214 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2215 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2216 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2217 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2221 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2222 ret.beats = (uint32_t) floor (remaining_beats);
2223 ret.bars = total_bars;
2225 /* 0 0 0 to 1 1 0 mapping*/
2229 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2231 ret.ticks -= BBT_Time::ticks_per_beat;
2234 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2242 /** Returns the BBT time corresponding to the supplied frame position.
2243 * @param frame the position in audio samples.
2244 * @return the BBT time at the frame position .
2248 TempoMap::bbt_at_frame (framepos_t frame)
2256 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2261 const double minute = minute_at_frame (frame);
2263 Glib::Threads::RWLock::ReaderLock lm (lock);
2265 return bbt_at_minute_locked (_metrics, minute);
2269 TempoMap::bbt_at_frame_rt (framepos_t frame)
2271 const double minute = minute_at_frame (frame);
2273 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2276 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2279 return bbt_at_minute_locked (_metrics, minute);
2283 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2293 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2294 MeterSection* prev_m = 0;
2295 MeterSection* next_m = 0;
2299 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2300 if (!(*i)->is_tempo()) {
2301 m = static_cast<MeterSection*> (*i);
2302 if (prev_m && m->minute() > minute) {
2310 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2312 /* handle frame before first meter */
2313 if (minute < prev_m->minute()) {
2316 /* audio locked meters fake their beat */
2317 if (next_m && next_m->beat() < beat) {
2318 beat = next_m->beat();
2321 beat = max (0.0, beat);
2323 const double beats_in_ms = beat - prev_m->beat();
2324 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2325 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2326 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2327 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2331 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2332 ret.beats = (uint32_t) floor (remaining_beats);
2333 ret.bars = total_bars;
2335 /* 0 0 0 to 1 1 0 - based mapping*/
2339 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2341 ret.ticks -= BBT_Time::ticks_per_beat;
2344 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2352 /** Returns the frame position corresponding to the supplied BBT time.
2353 * @param bbt the position in BBT time.
2354 * @return the frame position at bbt.
2358 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2362 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2367 if (bbt.beats < 1) {
2368 throw std::logic_error ("beats are counted from one");
2373 Glib::Threads::RWLock::ReaderLock lm (lock);
2374 minute = minute_at_bbt_locked (_metrics, bbt);
2377 return frame_at_minute (minute);
2380 /* meter & tempo section based */
2382 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2384 /* HOLD THE READER LOCK */
2386 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2391 * Returns the quarter-note beat position corresponding to the supplied frame.
2393 * @param frame the position in frames.
2394 * @return The quarter-note position of the supplied frame. Ignores meter.
2398 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2400 const double minute = minute_at_frame (frame);
2402 Glib::Threads::RWLock::ReaderLock lm (lock);
2404 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2408 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2410 const double minute = minute_at_frame (frame);
2412 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2415 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2418 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2422 * Returns the frame position corresponding to the supplied quarter-note beat.
2424 * @param quarter_note the quarter-note position.
2425 * @return the frame position of the supplied quarter-note. Ignores meter.
2430 TempoMap::frame_at_quarter_note (const double quarter_note) const
2434 Glib::Threads::RWLock::ReaderLock lm (lock);
2436 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2439 return frame_at_minute (minute);
2442 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2443 * @param beat The BBT (meter-based) beat.
2444 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2446 * a quarter-note may be compared with and assigned to Evoral::Beats.
2450 TempoMap::quarter_note_at_beat (const double beat) const
2452 Glib::Threads::RWLock::ReaderLock lm (lock);
2454 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2457 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2458 * @param quarter_note The position in quarter-note beats.
2459 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2461 * a quarter-note is the musical unit of Evoral::Beats.
2465 TempoMap::beat_at_quarter_note (const double quarter_note) const
2467 Glib::Threads::RWLock::ReaderLock lm (lock);
2469 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2472 /** Returns the duration in frames between two supplied quarter-note beat positions.
2473 * @param start the first position in quarter-note beats.
2474 * @param end the end position in quarter-note beats.
2475 * @return the frame distance ober the quarter-note beats duration.
2477 * use this rather than e.g.
2478 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2479 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2483 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2488 Glib::Threads::RWLock::ReaderLock lm (lock);
2489 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2492 return frame_at_minute (minutes);
2496 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2499 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2503 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2505 Glib::Threads::RWLock::ReaderLock lm (lock);
2507 return quarter_notes_between_frames_locked (_metrics, start, end);
2511 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2513 const TempoSection* prev_t = 0;
2515 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2518 if ((*i)->is_tempo()) {
2519 t = static_cast<TempoSection*> (*i);
2523 if (prev_t && t->frame() > start) {
2530 const double start_qn = prev_t->pulse_at_frame (start);
2532 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2535 if ((*i)->is_tempo()) {
2536 t = static_cast<TempoSection*> (*i);
2540 if (prev_t && t->frame() > end) {
2546 const double end_qn = prev_t->pulse_at_frame (end);
2548 return (end_qn - start_qn) * 4.0;
2552 TempoMap::check_solved (const Metrics& metrics) const
2554 TempoSection* prev_t = 0;
2555 MeterSection* prev_m = 0;
2557 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2560 if ((*i)->is_tempo()) {
2561 t = static_cast<TempoSection*> (*i);
2566 /* check ordering */
2567 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2571 /* precision check ensures tempo and frames align.*/
2572 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2573 if (!t->locked_to_meter()) {
2578 /* gradient limit - who knows what it should be?
2579 things are also ok (if a little chaotic) without this
2581 if (fabs (prev_t->c()) > 1000.0) {
2582 //std::cout << "c : " << prev_t->c() << std::endl;
2589 if (!(*i)->is_tempo()) {
2590 m = static_cast<MeterSection*> (*i);
2591 if (prev_m && m->position_lock_style() == AudioTime) {
2592 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2593 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2594 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2596 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2610 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2612 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2614 if ((*i)->is_tempo()) {
2615 t = static_cast<TempoSection*> (*i);
2616 if (t->locked_to_meter()) {
2617 t->set_active (true);
2618 } else if (t->position_lock_style() == AudioTime) {
2619 if (t->frame() < frame) {
2620 t->set_active (false);
2621 t->set_pulse (-1.0);
2622 } else if (t->frame() > frame) {
2623 t->set_active (true);
2624 } else if (t->frame() == frame) {
2634 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2636 TempoSection* prev_t = 0;
2637 TempoSection* section_prev = 0;
2638 double first_m_minute = 0.0;
2639 const bool sml = section->locked_to_meter();
2641 /* can't move a tempo before the first meter */
2642 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2644 if (!(*i)->is_tempo()) {
2645 m = static_cast<MeterSection*> (*i);
2647 first_m_minute = m->minute();
2652 if (!section->initial() && minute <= first_m_minute) {
2656 section->set_active (true);
2657 section->set_minute (minute);
2659 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2661 if ((*i)->is_tempo()) {
2662 t = static_cast<TempoSection*> (*i);
2674 if (t->frame() == frame_at_minute (minute)) {
2678 const bool tlm = t->position_lock_style() == MusicTime;
2680 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2681 section_prev = prev_t;
2683 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2684 if (!section->locked_to_meter()) {
2685 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2690 if (t->position_lock_style() == MusicTime) {
2691 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2692 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2694 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2695 if (!t->locked_to_meter()) {
2696 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2705 recompute_tempi (imaginary);
2707 if (check_solved (imaginary)) {
2710 dunp (imaginary, std::cout);
2714 MetricSectionFrameSorter fcmp;
2715 imaginary.sort (fcmp);
2717 recompute_tempi (imaginary);
2719 if (check_solved (imaginary)) {
2727 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2729 TempoSection* prev_t = 0;
2730 TempoSection* section_prev = 0;
2732 section->set_pulse (pulse);
2734 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2736 if ((*i)->is_tempo()) {
2737 t = static_cast<TempoSection*> (*i);
2748 section_prev = prev_t;
2752 if (t->position_lock_style() == MusicTime) {
2753 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2754 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2756 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2757 if (!t->locked_to_meter()) {
2758 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2767 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2768 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2772 recompute_tempi (imaginary);
2774 if (check_solved (imaginary)) {
2777 dunp (imaginary, std::cout);
2781 MetricSectionSorter cmp;
2782 imaginary.sort (cmp);
2784 recompute_tempi (imaginary);
2786 * XX need a restriction here, but only for this case,
2787 * as audio locked tempos don't interact in the same way.
2789 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2791 * |50 bpm |250 bpm |60 bpm
2792 * drag 250 to the pulse after 60->
2793 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2795 if (check_solved (imaginary)) {
2803 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2805 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2806 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2807 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2811 if (section->initial()) {
2812 /* lock the first tempo to our first meter */
2813 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2818 TempoSection* meter_locked_tempo = 0;
2820 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2822 if ((*ii)->is_tempo()) {
2823 t = static_cast<TempoSection*> (*ii);
2824 if (t->locked_to_meter() && t->frame() == section->frame()) {
2825 meter_locked_tempo = t;
2831 if (!meter_locked_tempo) {
2835 MeterSection* prev_m = 0;
2837 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2838 bool solved = false;
2840 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2842 if (!(*i)->is_tempo()) {
2843 m = static_cast<MeterSection*> (*i);
2845 if (prev_m && !section->initial()) {
2846 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2847 if (beats + prev_m->beat() < section->beat()) {
2848 /* set the section pulse according to its musical position,
2849 * as an earlier time than this has been requested.
2851 const double new_pulse = ((section->beat() - prev_m->beat())
2852 / prev_m->note_divisor()) + prev_m->pulse();
2854 tempo_copy->set_position_lock_style (MusicTime);
2855 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2856 meter_locked_tempo->set_position_lock_style (MusicTime);
2857 section->set_position_lock_style (MusicTime);
2858 section->set_pulse (new_pulse);
2859 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2860 meter_locked_tempo->set_position_lock_style (AudioTime);
2861 section->set_position_lock_style (AudioTime);
2862 section->set_minute (meter_locked_tempo->minute());
2868 Metrics::const_iterator d = future_map.begin();
2869 while (d != future_map.end()) {
2878 /* all is ok. set section's locked tempo if allowed.
2879 possibly disallow if there is an adjacent audio-locked tempo.
2880 XX this check could possibly go. its never actually happened here.
2882 MeterSection* meter_copy = const_cast<MeterSection*>
2883 (&meter_section_at_minute_locked (future_map, section->minute()));
2885 meter_copy->set_minute (minute);
2887 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2888 section->set_minute (minute);
2889 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2890 / prev_m->note_divisor()) + prev_m->pulse());
2891 solve_map_minute (imaginary, meter_locked_tempo, minute);
2896 Metrics::const_iterator d = future_map.begin();
2897 while (d != future_map.end()) {
2907 /* initial (first meter atm) */
2909 tempo_copy->set_minute (minute);
2910 tempo_copy->set_pulse (0.0);
2912 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2913 section->set_minute (minute);
2914 meter_locked_tempo->set_minute (minute);
2915 meter_locked_tempo->set_pulse (0.0);
2916 solve_map_minute (imaginary, meter_locked_tempo, minute);
2921 Metrics::const_iterator d = future_map.begin();
2922 while (d != future_map.end()) {
2931 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2932 section->set_beat (b_bbt);
2933 section->set_pulse (0.0);
2943 MetricSectionFrameSorter fcmp;
2944 imaginary.sort (fcmp);
2946 recompute_meters (imaginary);
2952 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2954 /* disallow setting section to an existing meter's bbt */
2955 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2957 if (!(*i)->is_tempo()) {
2958 m = static_cast<MeterSection*> (*i);
2959 if (m != section && m->bbt().bars == when.bars) {
2965 MeterSection* prev_m = 0;
2966 MeterSection* section_prev = 0;
2968 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2970 if (!(*i)->is_tempo()) {
2971 m = static_cast<MeterSection*> (*i);
2977 pair<double, BBT_Time> b_bbt;
2978 double new_pulse = 0.0;
2980 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2981 section_prev = prev_m;
2983 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2984 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2985 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2987 section->set_beat (b_bbt);
2988 section->set_pulse (pulse);
2989 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2993 if (m->position_lock_style() == AudioTime) {
2994 TempoSection* meter_locked_tempo = 0;
2996 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2998 if ((*ii)->is_tempo()) {
2999 t = static_cast<TempoSection*> (*ii);
3000 if (t->locked_to_meter() && t->frame() == m->frame()) {
3001 meter_locked_tempo = t;
3007 if (!meter_locked_tempo) {
3012 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3014 if (beats + prev_m->beat() != m->beat()) {
3015 /* tempo/ meter change caused a change in beat (bar). */
3017 /* the user has requested that the previous section of music overlaps this one.
3018 we have no choice but to change the bar number here, as being locked to audio means
3019 we must stay where we are on the timeline.
3021 beats = m->beat() - prev_m->beat();
3022 b_bbt = make_pair (beats + prev_m->beat()
3023 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3024 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3026 } else if (!m->initial()) {
3027 b_bbt = make_pair (m->beat(), m->bbt());
3028 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3031 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3034 meter_locked_tempo->set_pulse (new_pulse);
3035 m->set_beat (b_bbt);
3036 m->set_pulse (new_pulse);
3040 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3041 if (beats + prev_m->beat() != m->beat()) {
3042 /* tempo/ meter change caused a change in beat (bar). */
3043 b_bbt = make_pair (beats + prev_m->beat()
3044 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3046 b_bbt = make_pair (beats + prev_m->beat()
3049 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3050 m->set_beat (b_bbt);
3051 m->set_pulse (new_pulse);
3052 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3059 if (!section_prev) {
3061 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3062 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3063 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3065 section->set_beat (b_bbt);
3066 section->set_pulse (pulse);
3067 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3070 MetricSectionSorter cmp;
3071 imaginary.sort (cmp);
3073 recompute_meters (imaginary);
3078 /** places a copy of _metrics into copy and returns a pointer
3079 * to section's equivalent in copy.
3082 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section) const
3084 TempoSection* ret = 0;
3086 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3087 if ((*i)->is_tempo()) {
3088 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3090 ret = new TempoSection (*t);
3091 copy.push_back (ret);
3095 TempoSection* cp = new TempoSection (*t);
3096 copy.push_back (cp);
3098 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3099 MeterSection* cp = new MeterSection (*m);
3100 copy.push_back (cp);
3108 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section) const
3110 MeterSection* ret = 0;
3112 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3113 if ((*i)->is_tempo()) {
3114 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3115 TempoSection* cp = new TempoSection (*t);
3116 copy.push_back (cp);
3118 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3120 ret = new MeterSection (*m);
3121 copy.push_back (ret);
3124 MeterSection* cp = new MeterSection (*m);
3125 copy.push_back (cp);
3132 /** answers the question "is this a valid beat position for this tempo section?".
3133 * it returns true if the tempo section can be moved to the requested bbt position,
3134 * leaving the tempo map in a solved state.
3135 * @param ts the tempo section to be moved
3136 * @param bbt the requested new position for the tempo section
3137 * @return true if the tempo section can be moved to the position, otherwise false.
3140 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3143 TempoSection* tempo_copy = 0;
3146 Glib::Threads::RWLock::ReaderLock lm (lock);
3147 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3153 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3155 Metrics::const_iterator d = copy.begin();
3156 while (d != copy.end()) {
3165 * 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,
3166 * taking any possible reordering as a consequence of this into account.
3167 * @param section - the section to be altered
3168 * @param bbt - the BBT time where the altered tempo will fall
3169 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3171 pair<double, framepos_t>
3172 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3175 pair<double, framepos_t> ret = make_pair (0.0, 0);
3177 Glib::Threads::RWLock::ReaderLock lm (lock);
3179 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3181 const double beat = beat_at_bbt_locked (future_map, bbt);
3183 if (section->position_lock_style() == AudioTime) {
3184 tempo_copy->set_position_lock_style (MusicTime);
3187 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3188 ret.first = tempo_copy->pulse();
3189 ret.second = tempo_copy->frame();
3191 ret.first = section->pulse();
3192 ret.second = section->frame();
3195 Metrics::const_iterator d = future_map.begin();
3196 while (d != future_map.end()) {
3203 /** moves a TempoSection to a specified position.
3204 * @param ts - the section to be moved
3205 * @param frame - the new position in frames for the tempo
3206 * @param sub_num - the snap division to use if using musical time.
3208 * if sub_num is non-zero, the frame position is used to calculate an exact
3211 * -1 | snap to bars (meter-based)
3212 * 0 | no snap - use audio frame for musical position
3213 * 1 | snap to meter-based (BBT) beat
3214 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3216 * this follows the snap convention in the gui.
3217 * if sub_num is zero, the musical position will be taken from the supplied frame.
3220 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3224 if (ts->position_lock_style() == MusicTime) {
3226 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3227 Glib::Threads::RWLock::WriterLock lm (lock);
3228 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3230 tempo_copy->set_position_lock_style (AudioTime);
3232 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3233 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3234 const double pulse = pulse_at_beat_locked (future_map, beat);
3236 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3237 solve_map_pulse (_metrics, ts, pulse);
3238 recompute_meters (_metrics);
3246 Glib::Threads::RWLock::WriterLock lm (lock);
3247 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3249 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3251 /* We're moving the object that defines the grid while snapping to it...
3252 * Placing the ts at the beat corresponding to the requested frame may shift the
3253 * grid in such a way that the mouse is left hovering over a completerly different division,
3254 * causing jittering when the mouse next moves (esp. large tempo deltas).
3256 * This alters the snap behaviour slightly in that we snap to beat divisions
3257 * in the future map rather than the existing one.
3259 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3260 const framepos_t snapped_frame = frame_at_minute (minute_at_pulse_locked (future_map, qn / 4.0));
3262 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3263 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3264 ts->set_pulse (qn / 4.0);
3265 recompute_meters (_metrics);
3268 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3269 recompute_meters (_metrics);
3275 Metrics::const_iterator d = future_map.begin();
3276 while (d != future_map.end()) {
3281 MetricPositionChanged (PropertyChange ()); // Emit Signal
3284 /** moves a MeterSection to a specified position.
3285 * @param ms - the section to be moved
3286 * @param frame - the new position in frames for the meter
3288 * as a meter cannot snap to anything but bars,
3289 * the supplied frame is rounded to the nearest bar, possibly
3290 * leaving the meter position unchanged.
3293 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3297 if (ms->position_lock_style() == AudioTime) {
3300 Glib::Threads::RWLock::WriterLock lm (lock);
3301 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3303 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3304 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3305 recompute_tempi (_metrics);
3310 Glib::Threads::RWLock::WriterLock lm (lock);
3311 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3313 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3314 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3316 if (solve_map_bbt (future_map, copy, bbt)) {
3317 solve_map_bbt (_metrics, ms, bbt);
3318 recompute_tempi (_metrics);
3323 Metrics::const_iterator d = future_map.begin();
3324 while (d != future_map.end()) {
3329 MetricPositionChanged (PropertyChange ()); // Emit Signal
3333 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3336 bool can_solve = false;
3338 Glib::Threads::RWLock::WriterLock lm (lock);
3339 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3341 if (tempo_copy->type() == TempoSection::Constant) {
3342 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3343 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3345 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3346 tempo_copy->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3349 if (ts->clamped()) {
3350 TempoSection* prev = 0;
3351 if ((prev = previous_tempo_section_locked (future_map, tempo_copy)) != 0) {
3352 prev->set_end_note_types_per_minute (tempo_copy->note_types_per_minute());
3356 recompute_tempi (future_map);
3358 if (check_solved (future_map)) {
3359 if (ts->type() == TempoSection::Constant) {
3360 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3361 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3363 ts->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3364 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3367 if (ts->clamped()) {
3368 TempoSection* prev = 0;
3369 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3370 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3374 recompute_map (_metrics);
3379 Metrics::const_iterator d = future_map.begin();
3380 while (d != future_map.end()) {
3385 MetricPositionChanged (PropertyChange ()); // Emit Signal
3392 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3395 Ts (future prev_t) Tnext
3398 |----------|----------
3405 Glib::Threads::RWLock::WriterLock lm (lock);
3411 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3417 /* minimum allowed measurement distance in frames */
3418 framepos_t const min_dframe = 2;
3421 if (prev_t->clamped()) {
3422 TempoSection* next_t = next_tempo_section_locked (future_map, prev_t);
3423 TempoSection* prev_to_prev_t = previous_tempo_section_locked (future_map, prev_t);
3424 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3425 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3427 double contribution = 0.0;
3428 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3429 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3431 framepos_t const fr_off = (end_frame - frame);
3432 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3434 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3435 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3436 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3438 new_bpm = prev_t->note_types_per_minute();
3441 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3443 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
3444 / (double) (end_frame - prev_t->frame()));
3446 new_bpm = prev_t->note_types_per_minute();
3449 new_bpm = min (new_bpm, (double) 1000.0);
3451 /* don't clamp and proceed here.
3452 testing has revealed that this can go negative,
3453 which is an entirely different thing to just being too low.
3456 if (new_bpm < 0.5) {
3460 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3461 prev_t->set_note_types_per_minute (new_bpm);
3463 prev_t->set_end_note_types_per_minute (new_bpm);
3464 prev_t->set_note_types_per_minute (new_bpm);
3467 if (prev_t->clamped()) {
3468 TempoSection* prev = 0;
3469 if ((prev = previous_tempo_section_locked (future_map, prev_t)) != 0) {
3470 prev->set_end_note_types_per_minute (prev_t->note_types_per_minute());
3474 recompute_tempi (future_map);
3475 recompute_meters (future_map);
3477 if (check_solved (future_map)) {
3478 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3479 ts->set_note_types_per_minute (new_bpm);
3481 ts->set_end_note_types_per_minute (new_bpm);
3482 ts->set_note_types_per_minute (new_bpm);
3484 if (ts->clamped()) {
3485 TempoSection* prev = 0;
3486 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3487 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3490 recompute_tempi (_metrics);
3491 recompute_meters (_metrics);
3497 Metrics::const_iterator d = future_map.begin();
3498 while (d != future_map.end()) {
3502 MetricPositionChanged (PropertyChange ()); // Emit Signal
3507 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3510 Ts (future prev_t) Tnext
3513 |----------|----------
3520 Glib::Threads::RWLock::WriterLock lm (lock);
3526 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3532 /* minimum allowed measurement distance in frames */
3533 framepos_t const min_dframe = 2;
3536 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3537 new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->frame() - frame)
3538 / (double) (prev_t->frame() - end_frame));
3540 new_bpm = prev_t->end_note_types_per_minute();
3543 new_bpm = min (new_bpm, (double) 1000.0);
3545 if (new_bpm < 0.5) {
3549 prev_t->set_end_note_types_per_minute (new_bpm);
3551 TempoSection* next = 0;
3552 if ((next = next_tempo_section_locked (future_map, prev_t)) != 0) {
3553 if (next->clamped()) {
3554 next->set_note_types_per_minute (prev_t->end_note_types_per_minute());
3558 recompute_tempi (future_map);
3559 recompute_meters (future_map);
3561 if (check_solved (future_map)) {
3562 ts->set_end_note_types_per_minute (new_bpm);
3564 TempoSection* true_next = 0;
3565 if ((true_next = next_tempo_section_locked (_metrics, ts)) != 0) {
3566 if (true_next->clamped()) {
3567 true_next->set_note_types_per_minute (ts->end_note_types_per_minute());
3571 recompute_tempi (_metrics);
3572 recompute_meters (_metrics);
3578 Metrics::const_iterator d = future_map.begin();
3579 while (d != future_map.end()) {
3584 MetricPositionChanged (PropertyChange ()); // Emit Signal
3588 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
3590 TempoSection* next_t = 0;
3591 TempoSection* next_to_next_t = 0;
3593 bool can_solve = false;
3595 /* minimum allowed measurement distance in frames */
3596 framepos_t const min_dframe = 2;
3599 Glib::Threads::RWLock::WriterLock lm (lock);
3604 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3605 TempoSection* prev_to_prev_t = 0;
3606 const frameoffset_t fr_off = end_frame - frame;
3612 if (tempo_copy->pulse() > 0.0) {
3613 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
3616 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3617 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3618 next_t = static_cast<TempoSection*> (*i);
3627 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3628 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3629 next_to_next_t = static_cast<TempoSection*> (*i);
3634 if (!next_to_next_t) {
3638 double prev_contribution = 0.0;
3640 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3641 prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3644 const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
3647 framepos_t old_tc_minute = tempo_copy->minute();
3648 double old_next_minute = next_t->minute();
3649 double old_next_to_next_minute = next_to_next_t->minute();
3652 double new_next_bpm;
3653 double new_copy_end_bpm;
3655 if (frame > tempo_copy->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > tempo_copy->frame() + min_dframe) {
3656 new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
3657 / (double) (end_frame - tempo_copy->frame()));
3659 new_bpm = tempo_copy->note_types_per_minute();
3662 /* don't clamp and proceed here.
3663 testing has revealed that this can go negative,
3664 which is an entirely different thing to just being too low.
3666 if (new_bpm < 0.5) {
3670 new_bpm = min (new_bpm, (double) 1000.0);
3672 tempo_copy->set_note_types_per_minute (new_bpm);
3673 if (tempo_copy->type() == TempoSection::Constant) {
3674 tempo_copy->set_end_note_types_per_minute (new_bpm);
3677 recompute_tempi (future_map);
3679 if (check_solved (future_map)) {
3685 ts->set_note_types_per_minute (new_bpm);
3686 if (ts->type() == TempoSection::Constant) {
3687 ts->set_end_note_types_per_minute (new_bpm);
3690 recompute_map (_metrics);
3695 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3696 if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) {
3698 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3699 / (double) ((old_next_to_next_minute) - old_next_minute));
3702 new_next_bpm = next_t->note_types_per_minute();
3705 next_t->set_note_types_per_minute (new_next_bpm);
3706 recompute_tempi (future_map);
3708 if (check_solved (future_map)) {
3709 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3710 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3711 next_t = static_cast<TempoSection*> (*i);
3719 next_t->set_note_types_per_minute (new_next_bpm);
3720 recompute_map (_metrics);
3724 double next_frame_ratio = 1.0;
3725 double copy_frame_ratio = 1.0;
3727 if (next_to_next_t) {
3728 next_frame_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute - old_next_minute);
3730 copy_frame_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute));
3733 new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
3734 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
3736 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3738 if (next_t->clamped()) {
3739 next_t->set_note_types_per_minute (new_copy_end_bpm);
3741 next_t->set_note_types_per_minute (new_next_bpm);
3744 recompute_tempi (future_map);
3746 if (check_solved (future_map)) {
3747 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3748 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3749 next_t = static_cast<TempoSection*> (*i);
3758 if (next_t->clamped()) {
3759 next_t->set_note_types_per_minute (new_copy_end_bpm);
3761 next_t->set_note_types_per_minute (new_next_bpm);
3764 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3765 recompute_map (_metrics);
3771 Metrics::const_iterator d = future_map.begin();
3772 while (d != future_map.end()) {
3777 MetricPositionChanged (PropertyChange ()); // Emit Signal
3781 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3782 * the supplied frame, possibly returning a negative value.
3784 * @param frame The session frame position.
3785 * @param sub_num The subdivision to use when rounding the beat.
3786 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3787 * Positive integers indicate quarter note (non BBT) divisions.
3788 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3789 * @return The beat position of the supplied frame.
3791 * when working to a musical grid, the use of sub_nom indicates that
3792 * the position should be interpreted musically.
3794 * it effectively snaps to meter bars, meter beats or quarter note divisions
3795 * (as per current gui convention) and returns a musical position independent of frame rate.
3797 * If the supplied frame lies before the first meter, the return will be negative,
3798 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3799 * the continuation of the tempo curve (backwards).
3801 * This function is sensitive to tempo and meter.
3804 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3806 Glib::Threads::RWLock::ReaderLock lm (lock);
3808 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3812 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3814 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3817 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3818 * the supplied frame, possibly returning a negative value.
3820 * @param frame The session frame position.
3821 * @param sub_num The subdivision to use when rounding the quarter note.
3822 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3823 * Positive integers indicate quarter note (non BBT) divisions.
3824 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3825 * @return The quarter note position of the supplied frame.
3827 * When working to a musical grid, the use of sub_nom indicates that
3828 * the frame position should be interpreted musically.
3830 * it effectively snaps to meter bars, meter beats or quarter note divisions
3831 * (as per current gui convention) and returns a musical position independent of frame rate.
3833 * If the supplied frame lies before the first meter, the return will be negative,
3834 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3835 * the continuation of the tempo curve (backwards).
3837 * This function is tempo-sensitive.
3840 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3842 Glib::Threads::RWLock::ReaderLock lm (lock);
3844 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3848 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3850 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3853 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3854 } else if (sub_num == 1) {
3855 /* the gui requested exact musical (BBT) beat */
3856 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3857 } else if (sub_num == -1) {
3859 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3863 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3865 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3867 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3877 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3878 * @param pos the frame position in the tempo map.
3879 * @param bbt the distance in BBT time from pos to calculate.
3880 * @param dir the rounding direction..
3881 * @return the duration in frames between pos and bbt
3884 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3886 Glib::Threads::RWLock::ReaderLock lm (lock);
3888 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3890 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3893 pos_bbt.bars += bbt.bars;
3895 pos_bbt.ticks += bbt.ticks;
3896 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3898 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3901 pos_bbt.beats += bbt.beats;
3902 if ((double) pos_bbt.beats > divisions) {
3904 pos_bbt.beats -= divisions;
3906 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3908 return pos_bbt_frame - pos;
3912 if (pos_bbt.bars <= bbt.bars) {
3915 pos_bbt.bars -= bbt.bars;
3918 if (pos_bbt.ticks < bbt.ticks) {
3919 if (pos_bbt.bars > 1) {
3920 if (pos_bbt.beats == 1) {
3922 pos_bbt.beats = divisions;
3926 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3932 pos_bbt.ticks -= bbt.ticks;
3935 if (pos_bbt.beats <= bbt.beats) {
3936 if (pos_bbt.bars > 1) {
3938 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3943 pos_bbt.beats -= bbt.beats;
3946 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3953 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3955 return round_to_type (fr, dir, Bar);
3959 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3961 return round_to_type (fr, dir, Beat);
3965 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3967 Glib::Threads::RWLock::ReaderLock lm (lock);
3968 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);
3969 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3970 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3972 ticks -= beats * BBT_Time::ticks_per_beat;
3975 /* round to next (or same iff dir == RoundUpMaybe) */
3977 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3979 if (mod == 0 && dir == RoundUpMaybe) {
3980 /* right on the subdivision, which is fine, so do nothing */
3982 } else if (mod == 0) {
3983 /* right on the subdivision, so the difference is just the subdivision ticks */
3984 ticks += ticks_one_subdivisions_worth;
3987 /* not on subdivision, compute distance to next subdivision */
3989 ticks += ticks_one_subdivisions_worth - mod;
3992 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3993 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3994 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3995 // But I'm keeping it around, until we determine there are no terrible consequences.
3996 // if (ticks >= BBT_Time::ticks_per_beat) {
3997 // ticks -= BBT_Time::ticks_per_beat;
4000 } else if (dir < 0) {
4002 /* round to previous (or same iff dir == RoundDownMaybe) */
4004 uint32_t difference = ticks % ticks_one_subdivisions_worth;
4006 if (difference == 0 && dir == RoundDownAlways) {
4007 /* right on the subdivision, but force-rounding down,
4008 so the difference is just the subdivision ticks */
4009 difference = ticks_one_subdivisions_worth;
4012 if (ticks < difference) {
4013 ticks = BBT_Time::ticks_per_beat - ticks;
4015 ticks -= difference;
4019 /* round to nearest */
4022 /* compute the distance to the previous and next subdivision */
4024 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
4026 /* closer to the next subdivision, so shift forward */
4028 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
4030 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
4032 if (ticks > BBT_Time::ticks_per_beat) {
4034 ticks -= BBT_Time::ticks_per_beat;
4035 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
4038 } else if (rem > 0) {
4040 /* closer to previous subdivision, so shift backward */
4044 /* can't go backwards past zero, so ... */
4045 return MusicFrame (0, 0);
4047 /* step back to previous beat */
4049 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4050 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4052 ticks = lrint (ticks - rem);
4053 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4056 /* on the subdivision, do nothing */
4060 MusicFrame ret (0, 0);
4061 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4062 ret.division = sub_num;
4068 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
4070 Glib::Threads::RWLock::ReaderLock lm (lock);
4071 const double minute = minute_at_frame (frame);
4072 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4073 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
4074 MusicFrame ret (0, 0);
4081 /* find bar previous to 'frame' */
4087 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4091 } else if (dir > 0) {
4092 /* find bar following 'frame' */
4097 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4101 /* true rounding: find nearest bar */
4102 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4105 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4107 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4109 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4110 ret.frame = next_ft;
4115 ret.frame = prev_ft;
4127 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
4130 } else if (dir > 0) {
4131 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
4135 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
4142 return MusicFrame (0, 0);
4146 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4147 framepos_t lower, framepos_t upper, uint32_t bar_mod)
4149 Glib::Threads::RWLock::ReaderLock lm (lock);
4150 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
4152 /* although the map handles negative beats, bbt doesn't. */
4157 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
4161 while (pos >= 0 && pos < upper) {
4162 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
4163 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4164 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4165 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4167 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4171 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
4176 bbt.bars -= bbt.bars % bar_mod;
4180 while (pos >= 0 && pos < upper) {
4181 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4182 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4183 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4184 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4185 bbt.bars += bar_mod;
4191 TempoMap::tempo_section_at_frame (framepos_t frame) const
4193 Glib::Threads::RWLock::ReaderLock lm (lock);
4195 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4199 TempoMap::tempo_section_at_frame (framepos_t frame)
4201 Glib::Threads::RWLock::ReaderLock lm (lock);
4203 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4207 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4209 TempoSection* prev = 0;
4213 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4215 if ((*i)->is_tempo()) {
4216 t = static_cast<TempoSection*> (*i);
4220 if (prev && t->minute() > minute) {
4230 abort(); /*NOTREACHED*/
4236 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4238 TempoSection* prev = 0;
4242 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4244 if ((*i)->is_tempo()) {
4245 t = static_cast<TempoSection*> (*i);
4249 if (prev && t->minute() > minute) {
4259 abort(); /*NOTREACHED*/
4265 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4267 TempoSection* prev_t = 0;
4268 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4272 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4273 if ((*i)->is_tempo()) {
4274 t = static_cast<TempoSection*> (*i);
4280 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4291 TempoMap::previous_tempo_section (TempoSection* ts) const
4293 Glib::Threads::RWLock::ReaderLock lm (lock);
4295 return previous_tempo_section_locked (_metrics, ts);
4300 TempoMap::previous_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4306 TempoSection* prev = 0;
4308 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4310 if ((*i)->is_tempo()) {
4311 TempoSection* t = static_cast<TempoSection*> (*i);
4317 if (prev && t == ts) {
4328 abort(); /*NOTREACHED*/
4335 TempoMap::next_tempo_section (TempoSection* ts) const
4337 Glib::Threads::RWLock::ReaderLock lm (lock);
4339 return next_tempo_section_locked (_metrics, ts);
4343 TempoMap::next_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4349 TempoSection* prev = 0;
4351 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4353 if ((*i)->is_tempo()) {
4354 TempoSection* t = static_cast<TempoSection*> (*i);
4360 if (prev && prev == ts) {
4371 abort(); /*NOTREACHED*/
4376 /* don't use this to calculate length (the tempo is only correct for this frame).
4377 do that stuff based on the beat_at_frame and frame_at_beat api
4380 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4382 Glib::Threads::RWLock::ReaderLock lm (lock);
4384 const TempoSection* ts_at = 0;
4385 const TempoSection* ts_after = 0;
4386 Metrics::const_iterator i;
4389 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4391 if ((*i)->is_tempo()) {
4392 t = static_cast<TempoSection*> (*i);
4396 if (ts_at && (*i)->frame() > frame) {
4406 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4408 /* must be treated as constant tempo */
4409 return ts_at->frames_per_quarter_note (_frame_rate);
4413 TempoMap::meter_section_at_frame (framepos_t frame) const
4415 Glib::Threads::RWLock::ReaderLock lm (lock);
4416 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4420 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4422 Metrics::const_iterator i;
4423 MeterSection* prev = 0;
4427 for (i = metrics.begin(); i != metrics.end(); ++i) {
4429 if (!(*i)->is_tempo()) {
4430 m = static_cast<MeterSection*> (*i);
4432 if (prev && (*i)->minute() > minute) {
4442 abort(); /*NOTREACHED*/
4449 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4451 MeterSection* prev_m = 0;
4453 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4455 if (!(*i)->is_tempo()) {
4456 m = static_cast<MeterSection*> (*i);
4457 if (prev_m && m->beat() > beat) {
4468 TempoMap::meter_section_at_beat (double beat) const
4470 Glib::Threads::RWLock::ReaderLock lm (lock);
4471 return meter_section_at_beat_locked (_metrics, beat);
4475 TempoMap::meter_at_frame (framepos_t frame) const
4477 TempoMetric m (metric_at (frame));
4482 TempoMap::fix_legacy_session ()
4484 MeterSection* prev_m = 0;
4485 TempoSection* prev_t = 0;
4486 bool have_initial_t = false;
4488 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4492 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4494 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4497 m->set_minute (0.0);
4498 m->set_position_lock_style (AudioTime);
4503 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4504 + (m->bbt().beats - 1)
4505 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4507 m->set_beat (start);
4508 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4509 + (m->bbt().beats - 1)
4510 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4511 m->set_pulse (start_beat / prev_m->note_divisor());
4514 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4519 /* Ramp type never existed in the era of this tempo section */
4520 t->set_end_note_types_per_minute (t->note_types_per_minute());
4524 t->set_minute (0.0);
4525 t->set_position_lock_style (AudioTime);
4527 have_initial_t = true;
4532 /* some 4.x sessions have no initial (non-movable) tempo. */
4533 if (!have_initial_t) {
4534 prev_t->set_pulse (0.0);
4535 prev_t->set_minute (0.0);
4536 prev_t->set_position_lock_style (AudioTime);
4537 prev_t->set_initial (true);
4538 prev_t->set_locked_to_meter (true);
4539 have_initial_t = true;
4542 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4543 + (t->legacy_bbt().beats - 1)
4544 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4546 t->set_pulse (beat / prev_m->note_divisor());
4548 /* really shouldn't happen but.. */
4549 t->set_pulse (beat / 4.0);
4557 TempoMap::fix_legacy_end_session ()
4559 TempoSection* prev_t = 0;
4561 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4564 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4571 if (prev_t->type() != TempoSection::Constant) {
4572 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4582 TempoMap::get_state ()
4584 Metrics::const_iterator i;
4585 XMLNode *root = new XMLNode ("TempoMap");
4588 Glib::Threads::RWLock::ReaderLock lm (lock);
4589 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4590 root->add_child_nocopy ((*i)->get_state());
4598 TempoMap::set_state (const XMLNode& node, int /*version*/)
4601 Glib::Threads::RWLock::WriterLock lm (lock);
4604 XMLNodeConstIterator niter;
4605 Metrics old_metrics (_metrics);
4608 nlist = node.children();
4610 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4611 XMLNode* child = *niter;
4613 if (child->name() == TempoSection::xml_state_node_name) {
4616 TempoSection* ts = new TempoSection (*child, _frame_rate);
4617 _metrics.push_back (ts);
4620 catch (failed_constructor& err){
4621 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4622 _metrics = old_metrics;
4623 old_metrics.clear();
4627 } else if (child->name() == MeterSection::xml_state_node_name) {
4630 MeterSection* ms = new MeterSection (*child, _frame_rate);
4631 _metrics.push_back (ms);
4634 catch (failed_constructor& err) {
4635 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4636 _metrics = old_metrics;
4637 old_metrics.clear();
4643 if (niter == nlist.end()) {
4644 MetricSectionSorter cmp;
4645 _metrics.sort (cmp);
4648 /* check for legacy sessions where bbt was the base musical unit for tempo */
4649 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4651 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4652 if (t->legacy_bbt().bars != 0) {
4653 fix_legacy_session();
4657 if (t->legacy_end()) {
4658 fix_legacy_end_session();
4666 /* check for multiple tempo/meters at the same location, which
4667 ardour2 somehow allowed.
4670 Metrics::iterator prev = _metrics.end();
4671 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4672 if (prev != _metrics.end()) {
4674 MeterSection* prev_m;
4676 TempoSection* prev_t;
4677 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4678 if (prev_m->pulse() == ms->pulse()) {
4679 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4680 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4683 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4684 if (prev_t->pulse() == ts->pulse()) {
4685 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4686 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4694 recompute_map (_metrics);
4696 Metrics::const_iterator d = old_metrics.begin();
4697 while (d != old_metrics.end()) {
4701 old_metrics.clear ();
4704 PropertyChanged (PropertyChange ());
4710 TempoMap::dump (std::ostream& o) const
4712 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4713 const MeterSection* m;
4714 const TempoSection* t;
4715 const TempoSection* prev_t = 0;
4717 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4719 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4720 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4721 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4722 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4723 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4725 o << " current start : " << t->note_types_per_minute()
4726 << " current end : " << t->end_note_types_per_minute()
4727 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4728 o << " previous : " << prev_t->note_types_per_minute()
4729 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4730 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4731 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4732 << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4733 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4736 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4737 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4738 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4739 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4742 o << "------" << std::endl;
4746 TempoMap::n_tempos() const
4748 Glib::Threads::RWLock::ReaderLock lm (lock);
4751 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4752 if ((*i)->is_tempo()) {
4761 TempoMap::n_meters() const
4763 Glib::Threads::RWLock::ReaderLock lm (lock);
4766 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4767 if (!(*i)->is_tempo()) {
4776 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4778 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4779 if ((*i)->frame() >= where && !(*i)->initial ()) {
4783 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4784 gui_set_meter_position (ms, (*i)->frame() + amount);
4787 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4788 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4793 PropertyChanged (PropertyChange ());
4797 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4801 std::list<MetricSection*> metric_kill_list;
4803 TempoSection* last_tempo = NULL;
4804 MeterSection* last_meter = NULL;
4805 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4806 bool meter_after = false; // is there a meter marker likewise?
4808 Glib::Threads::RWLock::WriterLock lm (lock);
4809 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4810 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4811 metric_kill_list.push_back(*i);
4812 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4815 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4819 else if ((*i)->frame() >= where) {
4820 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4821 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4822 if ((*i)->frame() == where) {
4823 // marker was immediately after end of range
4824 tempo_after = dynamic_cast<TempoSection*> (*i);
4825 meter_after = dynamic_cast<MeterSection*> (*i);
4831 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4832 if (last_tempo && !tempo_after) {
4833 metric_kill_list.remove(last_tempo);
4834 last_tempo->set_minute (minute_at_frame (where));
4837 if (last_meter && !meter_after) {
4838 metric_kill_list.remove(last_meter);
4839 last_meter->set_minute (minute_at_frame (where));
4843 //remove all the remaining metrics
4844 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4845 _metrics.remove(*i);
4850 recompute_map (_metrics);
4853 PropertyChanged (PropertyChange ());
4857 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4858 * pos can be -ve, if required.
4861 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4863 Glib::Threads::RWLock::ReaderLock lm (lock);
4864 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4866 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4870 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4872 Glib::Threads::RWLock::ReaderLock lm (lock);
4874 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4875 pos_bbt.ticks += op.ticks;
4876 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4878 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4880 pos_bbt.beats += op.beats;
4881 /* the meter in effect will start on the bar */
4882 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();
4883 while (pos_bbt.beats >= divisions_per_bar + 1) {
4885 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4886 pos_bbt.beats -= divisions_per_bar;
4888 pos_bbt.bars += op.bars;
4890 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4893 /** Count the number of beats that are equivalent to distance when going forward,
4897 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4899 Glib::Threads::RWLock::ReaderLock lm (lock);
4901 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4905 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4911 operator<< (std::ostream& o, const Meter& m) {
4912 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4916 operator<< (std::ostream& o, const Tempo& t) {
4917 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4921 operator<< (std::ostream& o, const MetricSection& section) {
4923 o << "MetricSection @ " << section.frame() << ' ';
4925 const TempoSection* ts;
4926 const MeterSection* ms;
4928 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4929 o << *((const Tempo*) ts);
4930 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4931 o << *((const Meter*) ms);