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"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0, 4.0);
51 MetricSection::frame_at_minute (const double& time) const
53 return (framepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
57 MetricSection::minute_at_frame (const framepos_t& frame) const
59 return (frame / (double) _sample_rate) / 60.0;
62 /***********************************************************************/
65 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
67 /* This is tempo- and meter-sensitive. The number it returns
68 is based on the interval between any two lines in the
69 grid that is constructed from tempo and meter sections.
71 The return value IS NOT interpretable in terms of "beats".
74 return (60.0 * sr) / (tempo.note_types_per_minute() * (_note_type/tempo.note_type()));
78 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
80 return frames_per_grid (tempo, sr) * _divisions_per_bar;
83 /***********************************************************************/
85 const string TempoSection::xml_state_node_name = "Tempo";
87 TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate)
88 : MetricSection (0.0, 0, MusicTime, true, sample_rate)
89 , Tempo (TempoMap::default_tempo())
92 , _locked_to_meter (false)
94 XMLProperty const * prop;
100 _legacy_bbt = BBT_Time (0, 0, 0);
102 if ((prop = node.property ("start")) != 0) {
103 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
107 /* legacy session - start used to be in bbt*/
110 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
114 if ((prop = node.property ("pulse")) != 0) {
115 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
116 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
122 if ((prop = node.property ("frame")) != 0) {
123 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
124 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
125 throw failed_constructor();
127 set_minute (minute_at_frame (frame));
131 /* XX replace old beats-per-minute name with note-types-per-minute */
132 if ((prop = node.property ("beats-per-minute")) != 0) {
133 if (sscanf (prop->value().c_str(), "%lf", &_note_types_per_minute) != 1 || _note_types_per_minute < 0.0) {
134 error << _("TempoSection XML node has an illegal \"beats-per-minute\" value") << endmsg;
135 throw failed_constructor();
139 if ((prop = node.property ("note-type")) == 0) {
140 /* older session, make note type be quarter by default */
143 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
144 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
145 throw failed_constructor();
149 if ((prop = node.property ("movable")) == 0) {
150 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
151 throw failed_constructor();
154 set_initial (!string_is_affirmative (prop->value()));
156 if ((prop = node.property ("active")) == 0) {
157 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
160 set_active (string_is_affirmative (prop->value()));
163 if ((prop = node.property ("tempo-type")) == 0) {
166 _type = Type (string_2_enum (prop->value(), _type));
169 if ((prop = node.property ("lock-style")) == 0) {
171 set_position_lock_style (MusicTime);
173 set_position_lock_style (AudioTime);
176 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
179 if ((prop = node.property ("locked-to-meter")) == 0) {
181 set_locked_to_meter (true);
183 set_locked_to_meter (false);
186 set_locked_to_meter (string_is_affirmative (prop->value()));
189 /* 5.5 marked initial tempo as not locked to meter. this should always be true anyway */
191 set_locked_to_meter (true);
196 TempoSection::get_state() const
198 XMLNode *root = new XMLNode (xml_state_node_name);
202 snprintf (buf, sizeof (buf), "%lf", pulse());
203 root->add_property ("pulse", buf);
204 snprintf (buf, sizeof (buf), "%li", frame());
205 root->add_property ("frame", buf);
206 snprintf (buf, sizeof (buf), "%lf", _note_types_per_minute);
207 root->add_property ("beats-per-minute", buf);
208 snprintf (buf, sizeof (buf), "%lf", _note_type);
209 root->add_property ("note-type", buf);
210 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
211 root->add_property ("movable", buf);
212 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
213 root->add_property ("active", buf);
214 root->add_property ("tempo-type", enum_2_string (_type));
215 root->add_property ("lock-style", enum_2_string (position_lock_style()));
216 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
222 TempoSection::set_type (Type type)
227 /** returns the Tempo at the session-relative minute.
230 TempoSection::tempo_at_minute (const double& m) const
232 const bool constant = _type == Constant || _c == 0.0 || (initial() && m < minute());
234 return Tempo (note_types_per_minute(), note_type());
237 return Tempo (_tempo_at_time (m - minute()), _note_type);
240 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
241 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
242 * @param p the pulse used to calculate the returned minute for constant tempi
243 * @return the minute at the supplied tempo
245 * note that the note_type is currently ignored in this function. see below.
249 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
250 * there should be no ramp between the two even if we are ramped.
251 * in other words a ramp should only place a curve on note_types_per_minute.
252 * we should be able to use Tempo note type here, but the above
253 * complicates things a bit.
256 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
258 const bool constant = _type == Constant || _c == 0.0 || (initial() && p < pulse());
260 return ((p - pulse()) / pulses_per_minute()) + minute();
263 return _time_at_tempo (ntpm) + minute();
266 /** returns the Tempo at the supplied whole-note pulse.
269 TempoSection::tempo_at_pulse (const double& p) const
271 const bool constant = _type == Constant || _c == 0.0 || (initial() && p < pulse());
274 return Tempo (note_types_per_minute(), note_type());
277 return Tempo (_tempo_at_pulse (p - pulse()), _note_type);
280 /** returns the whole-note pulse where a tempo in note types per minute occurs.
281 * constant tempi require minute m.
282 * @param ntpm the note types per minute value used to calculate the returned pulse
283 * @param m the minute used to calculate the returned pulse if the tempo is constant
284 * @return the whole-note pulse at the supplied tempo
286 * note that note_type is currently ignored in this function. see minute_at_tempo().
288 * for constant tempi, this is anaologous to pulse_at_minute().
291 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
293 const bool constant = _type == Constant || _c == 0.0 || (initial() && m < minute());
295 return ((m - minute()) * pulses_per_minute()) + pulse();
298 return _pulse_at_tempo (ntpm) + pulse();
301 /** returns the whole-note pulse at the supplied session-relative minute.
304 TempoSection::pulse_at_minute (const double& m) const
306 const bool constant = _type == Constant || _c == 0.0 || (initial() && m < minute());
308 return ((m - minute()) * pulses_per_minute()) + pulse();
311 return _pulse_at_time (m - minute()) + pulse();
314 /** returns the session-relative minute at the supplied whole-note pulse.
317 TempoSection::minute_at_pulse (const double& p) const
319 const bool constant = _type == Constant || _c == 0.0 || (initial() && p < pulse());
321 return ((p - pulse()) / pulses_per_minute()) + minute();
324 return _time_at_pulse (p - pulse()) + minute();
327 /** returns thw whole-note pulse at session frame position f.
328 * @param f the frame position.
329 * @return the position in whole-note pulses corresponding to f
331 * for use with musical units whose granularity is coarser than frames (e.g. ticks)
334 TempoSection::pulse_at_frame (const framepos_t& f) const
336 const bool constant = _type == Constant || _c == 0.0 || (initial() && f < frame());
338 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
341 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
345 TempoSection::frame_at_pulse (const double& p) const
347 const bool constant = _type == Constant || _c == 0.0 || (initial() && p < pulse());
349 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
352 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
360 Tt----|-----------------*|
361 Ta----|--------------|* |
367 _______________|___|____
368 time a t (next tempo)
371 Duration in beats at time a is the integral of some Tempo function.
372 In our case, the Tempo function (Tempo at time t) is
375 with function constant
380 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
381 b(t) = T0(e^(ct) - 1) / c
383 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:
384 t(b) = log((c.b / T0) + 1) / c
386 The time t at which Tempo T occurs is a as above:
387 t(T) = log(T / T0) / c
389 The beat at which a Tempo T occurs is:
392 The Tempo at which beat b occurs is:
395 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
396 Our problem is that we usually don't know t.
397 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.
398 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
399 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
401 By substituting our expanded t as a in the c function above, our problem is reduced to:
402 c = T0 (e^(log (Ta / T0)) - 1) / b
404 Of course the word 'beat' has been left loosely defined above.
405 In music, a beat is defined by the musical pulse (which comes from the tempo)
406 and the meter in use at a particular time (how many pulse divisions there are in one bar).
407 It would be more accurate to substitute the work 'pulse' for 'beat' above.
411 We can now store c for future time calculations.
412 If the following tempo section (the one that defines c in conjunction with this one)
413 is changed or moved, c is no longer valid.
415 The public methods are session-relative.
417 Most of this stuff is taken from this paper:
420 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
423 Zurich University of Arts
424 Institute for Computer Music and Sound Technology
426 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
430 /** compute this ramp's function constant from some tempo-pulse point
431 * @param end_npm end tempo (in note types per minute)
432 * @param end_pulse duration (pulses into global start) of some other position.
433 * @return the calculated function constant
436 TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const
438 if (note_types_per_minute() == end_npm || _type == Constant) {
442 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
443 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
446 /** compute the function constant from some tempo-time point.
447 * @param end_npm tempo (note types/min.)
448 * @param end_minute distance (in minutes) from session origin
449 * @return the calculated function constant
452 TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const
454 if (note_types_per_minute() == end_npm || _type == Constant) {
458 return c_func (end_npm, end_minute - minute());
461 /* position function */
463 TempoSection::a_func (double end_npm, double c) const
465 return log (end_npm / note_types_per_minute()) / c;
468 /*function constant*/
470 TempoSection::c_func (double end_npm, double end_time) const
472 return log (end_npm / note_types_per_minute()) / end_time;
475 /* tempo in note types per minute at time in minutes */
477 TempoSection::_tempo_at_time (const double& time) const
479 return exp (_c * time) * note_types_per_minute();
482 /* time in minutes at tempo in note types per minute */
484 TempoSection::_time_at_tempo (const double& npm) const
486 return log (npm / note_types_per_minute()) / _c;
489 /* pulse at tempo in note types per minute */
491 TempoSection::_pulse_at_tempo (const double& npm) const
493 return ((npm - note_types_per_minute()) / _c) / _note_type;
496 /* tempo in note types per minute at pulse */
498 TempoSection::_tempo_at_pulse (const double& pulse) const
500 return (pulse * _note_type * _c) + note_types_per_minute();
503 /* pulse at time in minutes */
505 TempoSection::_pulse_at_time (const double& time) const
507 return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
510 /* time in minutes at pulse */
512 TempoSection::_time_at_pulse (const double& pulse) const
514 return log1p ((_c * pulse * _note_type) / note_types_per_minute()) / _c;
517 /***********************************************************************/
519 const string MeterSection::xml_state_node_name = "Meter";
521 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
522 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
524 XMLProperty const * prop;
529 framepos_t frame = 0;
530 pair<double, BBT_Time> start;
532 if ((prop = node.property ("start")) != 0) {
533 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
537 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
539 /* legacy session - start used to be in bbt*/
540 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
545 if ((prop = node.property ("pulse")) != 0) {
546 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
547 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
552 if ((prop = node.property ("beat")) != 0) {
553 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
554 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
560 if ((prop = node.property ("bbt")) == 0) {
561 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
562 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
566 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
567 throw failed_constructor();
573 if ((prop = node.property ("frame")) != 0) {
574 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
575 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
576 throw failed_constructor();
578 set_minute (minute_at_frame (frame));
582 /* beats-per-bar is old; divisions-per-bar is new */
584 if ((prop = node.property ("divisions-per-bar")) == 0) {
585 if ((prop = node.property ("beats-per-bar")) == 0) {
586 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
587 throw failed_constructor();
590 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
591 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
592 throw failed_constructor();
595 if ((prop = node.property ("note-type")) == 0) {
596 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
597 throw failed_constructor();
599 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
600 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
601 throw failed_constructor();
604 if ((prop = node.property ("movable")) == 0) {
605 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
606 throw failed_constructor();
609 set_initial (!string_is_affirmative (prop->value()));
611 if ((prop = node.property ("lock-style")) == 0) {
612 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
614 set_position_lock_style (MusicTime);
616 set_position_lock_style (AudioTime);
619 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
624 MeterSection::get_state() const
626 XMLNode *root = new XMLNode (xml_state_node_name);
630 snprintf (buf, sizeof (buf), "%lf", pulse());
631 root->add_property ("pulse", buf);
632 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
636 root->add_property ("bbt", buf);
637 snprintf (buf, sizeof (buf), "%lf", beat());
638 root->add_property ("beat", buf);
639 snprintf (buf, sizeof (buf), "%lf", _note_type);
640 root->add_property ("note-type", buf);
641 snprintf (buf, sizeof (buf), "%li", frame());
642 root->add_property ("frame", buf);
643 root->add_property ("lock-style", enum_2_string (position_lock_style()));
644 snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
645 root->add_property ("divisions-per-bar", buf);
646 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
647 root->add_property ("movable", buf);
652 /***********************************************************************/
656 Tempo determines the rate of musical pulse determined by its components
657 note types per minute - the rate per minute of the whole note divisor _note_type
658 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
659 Meter divides the musical pulse into measures and beats according to its components
663 TempoSection - translates between time, musical pulse and tempo.
664 has a musical location in whole notes (pulses).
665 has a time location in minutes.
666 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
667 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
669 MeterSection - translates between BBT, meter-based beat and musical pulse.
670 has a musical location in whole notes (pulses)
671 has a musical location in meter-based beats
672 has a musical location in BBT time
673 has a time location expressed in minutes.
675 TempoSection and MeterSection may be locked to either audio or music (position lock style).
676 The lock style determines the location type to be kept as a reference when location is recalculated.
678 The first tempo and meter are special. they must move together, and are locked to audio.
679 Audio locked tempi which lie before the first meter are made inactive.
681 Recomputing the map is the process where the 'missing' location types are calculated.
682 We construct the tempo map by first using the locked location type of each section
683 to determine non-locked location types (pulse or minute position).
684 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
686 Having done this, we can now traverse the Metrics list by pulse or minute
687 to query its relevant meter/tempo.
689 It is important to keep the _metrics in an order that makes sense.
690 Because ramped MusicTime and AudioTime tempos can interact with each other,
691 reordering is frequent. Care must be taken to keep _metrics in a solved state.
692 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
696 Music and audio-locked objects may seem interchangeable on the surface, but when translating
697 between audio samples and beat, remember that a sample is only a quantised approximation
698 of the actual time (in minutes) of a beat.
699 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
700 mean that this frame is the actual location in time of 1|3|0.
702 You cannot use a frame measurement to determine beat distance except under special circumstances
703 (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).
705 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
706 sample space the user is operating at to be translated correctly to the object.
708 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
709 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
710 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
712 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
714 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
715 The result is rounded to audio frames.
716 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
719 frame_at_beat (beat_at_frame (frame)) == frame
721 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
723 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
724 frames_between_quarter_notes () eliminats this effect when determining time duration
725 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
727 The above pointless example could instead do:
728 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
730 The Shaggs - Things I Wonder
731 https://www.youtube.com/watch?v=9wQK6zMJOoQ
734 struct MetricSectionSorter {
735 bool operator() (const MetricSection* a, const MetricSection* b) {
736 return a->pulse() < b->pulse();
740 struct MetricSectionFrameSorter {
741 bool operator() (const MetricSection* a, const MetricSection* b) {
742 return a->frame() < b->frame();
746 TempoMap::TempoMap (framecnt_t fr)
749 BBT_Time start (1, 1, 0);
751 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo.note_types_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime, fr);
752 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
754 t->set_initial (true);
755 t->set_locked_to_meter (true);
757 m->set_initial (true);
759 /* note: frame time is correct (zero) for both of these */
761 _metrics.push_back (t);
762 _metrics.push_back (m);
766 TempoMap::TempoMap (TempoMap const & other)
768 _frame_rate = other._frame_rate;
769 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
770 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
771 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
774 TempoSection* new_section = new TempoSection (*ts);
775 _metrics.push_back (new_section);
777 MeterSection* new_section = new MeterSection (*ms);
778 _metrics.push_back (new_section);
784 TempoMap::operator= (TempoMap const & other)
786 if (&other != this) {
787 _frame_rate = other._frame_rate;
789 Metrics::const_iterator d = _metrics.begin();
790 while (d != _metrics.end()) {
796 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
797 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
798 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
801 TempoSection* new_section = new TempoSection (*ts);
802 _metrics.push_back (new_section);
804 MeterSection* new_section = new MeterSection (*ms);
805 _metrics.push_back (new_section);
810 PropertyChanged (PropertyChange());
815 TempoMap::~TempoMap ()
817 Metrics::const_iterator d = _metrics.begin();
818 while (d != _metrics.end()) {
826 TempoMap::frame_at_minute (const double time) const
828 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
832 TempoMap::minute_at_frame (const framepos_t frame) const
834 return (frame / (double) _frame_rate) / 60.0;
838 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
840 bool removed = false;
843 Glib::Threads::RWLock::WriterLock lm (lock);
844 if ((removed = remove_tempo_locked (tempo))) {
845 if (complete_operation) {
846 recompute_map (_metrics);
851 if (removed && complete_operation) {
852 PropertyChanged (PropertyChange ());
857 TempoMap::remove_tempo_locked (const TempoSection& tempo)
861 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
862 if (dynamic_cast<TempoSection*> (*i) != 0) {
863 if (tempo.frame() == (*i)->frame()) {
864 if (!(*i)->initial()) {
877 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
879 bool removed = false;
882 Glib::Threads::RWLock::WriterLock lm (lock);
883 if ((removed = remove_meter_locked (tempo))) {
884 if (complete_operation) {
885 recompute_map (_metrics);
890 if (removed && complete_operation) {
891 PropertyChanged (PropertyChange ());
896 TempoMap::remove_meter_locked (const MeterSection& meter)
899 if (meter.position_lock_style() == AudioTime) {
900 /* remove meter-locked tempo */
901 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
903 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
904 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
913 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
914 if (dynamic_cast<MeterSection*> (*i) != 0) {
915 if (meter.frame() == (*i)->frame()) {
916 if (!(*i)->initial()) {
929 TempoMap::do_insert (MetricSection* section)
931 bool need_add = true;
932 /* we only allow new meters to be inserted on beat 1 of an existing
936 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
938 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
940 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
941 corrected.second.beats = 1;
942 corrected.second.ticks = 0;
943 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
944 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
945 m->bbt(), corrected.second) << endmsg;
946 //m->set_pulse (corrected);
950 /* Look for any existing MetricSection that is of the same type and
951 in the same bar as the new one, and remove it before adding
952 the new one. Note that this means that if we find a matching,
953 existing section, we can break out of the loop since we're
954 guaranteed that there is only one such match.
957 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
959 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
960 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
961 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
962 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
964 if (tempo && insert_tempo) {
967 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
968 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
970 if (tempo->initial()) {
972 /* can't (re)move this section, so overwrite
973 * its data content (but not its properties as
977 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
978 (*i)->set_position_lock_style (AudioTime);
980 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
981 t->set_type (insert_tempo->type());
991 } else if (meter && insert_meter) {
995 bool const ipm = insert_meter->position_lock_style() == MusicTime;
997 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
999 if (meter->initial()) {
1001 /* can't (re)move this section, so overwrite
1002 * its data content (but not its properties as
1006 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
1007 (*i)->set_position_lock_style (AudioTime);
1017 /* non-matching types, so we don't care */
1021 /* Add the given MetricSection, if we didn't just reset an existing
1026 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
1027 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
1028 Metrics::iterator i;
1031 TempoSection* prev_t = 0;
1033 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1034 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
1035 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1038 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
1042 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
1046 prev_t = dynamic_cast<TempoSection*> (*i);
1049 } else if (insert_tempo) {
1050 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1051 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1054 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1055 const bool lm = insert_tempo->locked_to_meter();
1056 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1057 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1064 _metrics.insert (i, section);
1068 /* user supplies the exact pulse if pls == MusicTime */
1070 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
1072 if (tempo.note_types_per_minute() <= 0.0) {
1073 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1077 TempoSection* ts = 0;
1079 Glib::Threads::RWLock::WriterLock lm (lock);
1080 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
1084 PropertyChanged (PropertyChange ());
1090 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
1092 if (tempo.note_types_per_minute() <= 0.0) {
1093 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1097 const bool locked_to_meter = ts.locked_to_meter();
1100 Glib::Threads::RWLock::WriterLock lm (lock);
1101 TempoSection& first (first_tempo());
1102 if (!ts.initial()) {
1103 if (locked_to_meter) {
1106 /* cannot move a meter-locked tempo section */
1107 *static_cast<Tempo*>(&ts) = tempo;
1108 recompute_map (_metrics);
1111 remove_tempo_locked (ts);
1112 add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter);
1115 first.set_type (type);
1116 first.set_pulse (0.0);
1117 first.set_minute (minute_at_frame (frame));
1118 first.set_position_lock_style (AudioTime);
1119 first.set_locked_to_meter (true);
1121 /* cannot move the first tempo section */
1122 *static_cast<Tempo*>(&first) = tempo;
1123 recompute_map (_metrics);
1128 PropertyChanged (PropertyChange ());
1132 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1133 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1135 TempoSection* t = new TempoSection (pulse, minute, tempo.note_types_per_minute(), tempo.note_type(), type, pls, _frame_rate);
1136 t->set_locked_to_meter (locked_to_meter);
1137 bool solved = false;
1142 if (pls == AudioTime) {
1143 solved = solve_map_minute (_metrics, t, t->minute());
1145 solved = solve_map_pulse (_metrics, t, t->pulse());
1147 recompute_meters (_metrics);
1150 if (!solved && recompute) {
1151 recompute_map (_metrics);
1158 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1160 MeterSection* m = 0;
1162 Glib::Threads::RWLock::WriterLock lm (lock);
1163 m = add_meter_locked (meter, beat, where, frame, pls, true);
1168 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1173 PropertyChanged (PropertyChange ());
1178 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1181 Glib::Threads::RWLock::WriterLock lm (lock);
1182 const double beat = beat_at_bbt_locked (_metrics, where);
1184 if (!ms.initial()) {
1185 remove_meter_locked (ms);
1186 add_meter_locked (meter, beat, where, frame, pls, true);
1188 MeterSection& first (first_meter());
1189 TempoSection& first_t (first_tempo());
1190 /* cannot move the first meter section */
1191 *static_cast<Meter*>(&first) = meter;
1192 first.set_position_lock_style (AudioTime);
1193 first.set_pulse (0.0);
1194 first.set_minute (minute_at_frame (frame));
1195 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1196 first.set_beat (beat);
1197 first_t.set_minute (first.minute());
1198 first_t.set_locked_to_meter (true);
1199 first_t.set_pulse (0.0);
1200 first_t.set_position_lock_style (AudioTime);
1201 recompute_map (_metrics);
1205 PropertyChanged (PropertyChange ());
1209 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1211 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1212 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1213 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1214 TempoSection* mlt = 0;
1216 if (pls == AudioTime) {
1217 /* add meter-locked tempo */
1218 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), TempoSection::Ramp, AudioTime, true, true);
1226 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1228 bool solved = false;
1230 do_insert (new_meter);
1234 if (pls == AudioTime) {
1235 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1236 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1237 fudge frame so that the meter ends up at its BBT position instead.
1240 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1243 solved = solve_map_bbt (_metrics, new_meter, where);
1244 /* required due to resetting the pulse of meter-locked tempi above.
1245 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1246 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1248 recompute_map (_metrics);
1252 if (!solved && recompute) {
1253 /* if this has failed to solve, there is little we can do other than to ensure that
1254 the new map is recalculated.
1256 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1257 recompute_map (_metrics);
1264 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type)
1266 Tempo newtempo (note_types_per_minute, note_type);
1269 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1270 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1275 Glib::Threads::RWLock::WriterLock lm (lock);
1276 *((Tempo*) t) = newtempo;
1277 recompute_map (_metrics);
1279 PropertyChanged (PropertyChange ());
1286 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type)
1288 Tempo newtempo (note_types_per_minute, note_type);
1291 TempoSection* first;
1292 Metrics::iterator i;
1294 /* find the TempoSection immediately preceding "where"
1297 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1299 if ((*i)->frame() > where) {
1305 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1318 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1328 Glib::Threads::RWLock::WriterLock lm (lock);
1329 /* cannot move the first tempo section */
1330 *((Tempo*)prev) = newtempo;
1331 recompute_map (_metrics);
1334 PropertyChanged (PropertyChange ());
1338 TempoMap::first_meter () const
1340 const MeterSection *m = 0;
1342 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1343 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1348 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1349 abort(); /*NOTREACHED*/
1354 TempoMap::first_meter ()
1356 MeterSection *m = 0;
1358 /* CALLER MUST HOLD LOCK */
1360 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1361 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1366 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1367 abort(); /*NOTREACHED*/
1372 TempoMap::first_tempo () const
1374 const TempoSection *t = 0;
1376 /* CALLER MUST HOLD LOCK */
1378 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1379 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1389 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1390 abort(); /*NOTREACHED*/
1395 TempoMap::first_tempo ()
1397 TempoSection *t = 0;
1399 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1400 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1410 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1411 abort(); /*NOTREACHED*/
1415 TempoMap::recompute_tempi (Metrics& metrics)
1417 TempoSection* prev_t = 0;
1419 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1422 if ((*i)->is_tempo()) {
1423 t = static_cast<TempoSection*> (*i);
1435 if (t->position_lock_style() == AudioTime) {
1436 prev_t->set_c (prev_t->compute_c_minute (t->note_types_per_minute(), t->minute()));
1437 if (!t->locked_to_meter()) {
1438 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
1442 prev_t->set_c (prev_t->compute_c_pulse (t->note_types_per_minute(), t->pulse()));
1443 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
1451 prev_t->set_c (0.0);
1454 /* tempos must be positioned correctly.
1455 the current approach is to use a meter's bbt time as its base position unit.
1456 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1457 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1460 TempoMap::recompute_meters (Metrics& metrics)
1462 MeterSection* meter = 0;
1463 MeterSection* prev_m = 0;
1465 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1466 if (!(*mi)->is_tempo()) {
1467 meter = static_cast<MeterSection*> (*mi);
1468 if (meter->position_lock_style() == AudioTime) {
1470 pair<double, BBT_Time> b_bbt;
1471 TempoSection* meter_locked_tempo = 0;
1472 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1474 if ((*ii)->is_tempo()) {
1475 t = static_cast<TempoSection*> (*ii);
1476 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1477 meter_locked_tempo = t;
1484 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1485 if (beats + prev_m->beat() != meter->beat()) {
1486 /* reordering caused a bbt change */
1488 beats = meter->beat() - prev_m->beat();
1489 b_bbt = make_pair (beats + prev_m->beat()
1490 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1491 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1493 } else if (!meter->initial()) {
1494 b_bbt = make_pair (meter->beat(), meter->bbt());
1495 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1498 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1500 if (meter_locked_tempo) {
1501 meter_locked_tempo->set_pulse (pulse);
1503 meter->set_beat (b_bbt);
1504 meter->set_pulse (pulse);
1509 pair<double, BBT_Time> b_bbt;
1511 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1512 if (beats + prev_m->beat() != meter->beat()) {
1513 /* reordering caused a bbt change */
1514 b_bbt = make_pair (beats + prev_m->beat()
1515 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1517 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1519 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1521 /* shouldn't happen - the first is audio-locked */
1522 pulse = pulse_at_beat_locked (metrics, meter->beat());
1523 b_bbt = make_pair (meter->beat(), meter->bbt());
1526 meter->set_beat (b_bbt);
1527 meter->set_pulse (pulse);
1528 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1537 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1539 /* CALLER MUST HOLD WRITE LOCK */
1541 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1544 /* silly call from Session::process() during startup
1549 recompute_tempi (metrics);
1550 recompute_meters (metrics);
1554 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1556 Glib::Threads::RWLock::ReaderLock lm (lock);
1557 TempoMetric m (first_meter(), first_tempo());
1559 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1560 at something, because we insert the default tempo and meter during
1561 TempoMap construction.
1563 now see if we can find better candidates.
1566 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1568 if ((*i)->frame() > frame) {
1582 /* XX meters only */
1584 TempoMap::metric_at (BBT_Time bbt) const
1586 Glib::Threads::RWLock::ReaderLock lm (lock);
1587 TempoMetric m (first_meter(), first_tempo());
1589 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1590 at something, because we insert the default tempo and meter during
1591 TempoMap construction.
1593 now see if we can find better candidates.
1596 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1598 if (!(*i)->is_tempo()) {
1599 mw = static_cast<MeterSection*> (*i);
1600 BBT_Time section_start (mw->bbt());
1602 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1613 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1614 * @param frame The session frame position.
1615 * @return The beat duration according to the tempo map at the supplied frame.
1617 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1618 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1620 * This function uses both tempo and meter.
1623 TempoMap::beat_at_frame (const framecnt_t& frame) const
1625 Glib::Threads::RWLock::ReaderLock lm (lock);
1627 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1630 /* This function uses both tempo and meter.*/
1632 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1634 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1635 MeterSection* prev_m = 0;
1636 MeterSection* next_m = 0;
1638 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1639 if (!(*i)->is_tempo()) {
1640 if (prev_m && (*i)->minute() > minute) {
1641 next_m = static_cast<MeterSection*> (*i);
1644 prev_m = static_cast<MeterSection*> (*i);
1648 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1650 /* audio locked meters fake their beat */
1651 if (next_m && next_m->beat() < beat) {
1652 return next_m->beat();
1658 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1659 * @param beat The BBT (meter-based) beat.
1660 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1662 * This function uses both tempo and meter.
1665 TempoMap::frame_at_beat (const double& beat) const
1667 Glib::Threads::RWLock::ReaderLock lm (lock);
1669 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1672 /* meter & tempo section based */
1674 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1676 MeterSection* prev_m = 0;
1677 TempoSection* prev_t = 0;
1681 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1682 if (!(*i)->is_tempo()) {
1683 m = static_cast<MeterSection*> (*i);
1684 if (prev_m && m->beat() > beat) {
1694 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1695 if ((*i)->is_tempo()) {
1696 t = static_cast<TempoSection*> (*i);
1702 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1711 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1714 /** Returns a Tempo corresponding to the supplied frame position.
1715 * @param frame The audio frame.
1716 * @return a Tempo according to the tempo map at the supplied frame.
1720 TempoMap::tempo_at_frame (const framepos_t& frame) const
1722 Glib::Threads::RWLock::ReaderLock lm (lock);
1724 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1728 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1730 TempoSection* prev_t = 0;
1734 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1735 if ((*i)->is_tempo()) {
1736 t = static_cast<TempoSection*> (*i);
1740 if ((prev_t) && t->minute() > minute) {
1741 /* t is the section past frame */
1742 return prev_t->tempo_at_minute (minute);
1748 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1751 /** returns the frame at which the supplied tempo occurs, or
1752 * the frame of the last tempo section (search exhausted)
1753 * only the position of the first occurence will be returned
1757 TempoMap::frame_at_tempo (const Tempo& tempo) const
1759 Glib::Threads::RWLock::ReaderLock lm (lock);
1761 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1765 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1767 TempoSection* prev_t = 0;
1768 const double tempo_bpm = tempo.note_types_per_minute();
1770 Metrics::const_iterator i;
1772 for (i = metrics.begin(); i != metrics.end(); ++i) {
1774 if ((*i)->is_tempo()) {
1775 t = static_cast<TempoSection*> (*i);
1781 const double t_bpm = t->note_types_per_minute();
1783 if (t_bpm == tempo_bpm) {
1788 const double prev_t_bpm = prev_t->note_types_per_minute();
1790 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1791 return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse());
1798 return prev_t->minute();
1802 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1804 TempoSection* prev_t = 0;
1808 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1809 if ((*i)->is_tempo()) {
1810 t = static_cast<TempoSection*> (*i);
1814 if ((prev_t) && t->pulse() > pulse) {
1815 /* t is the section past frame */
1816 return prev_t->tempo_at_pulse (pulse);
1822 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1826 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1828 TempoSection* prev_t = 0;
1829 const double tempo_bpm = tempo.note_types_per_minute();
1831 Metrics::const_iterator i;
1833 for (i = metrics.begin(); i != metrics.end(); ++i) {
1835 if ((*i)->is_tempo()) {
1836 t = static_cast<TempoSection*> (*i);
1842 const double t_bpm = t->note_types_per_minute();
1844 if (t_bpm == tempo_bpm) {
1849 const double prev_t_bpm = prev_t->note_types_per_minute();
1851 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1852 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1859 return prev_t->pulse();
1862 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1863 * @param qn the position in quarter note beats.
1864 * @return the Tempo at the supplied quarter-note.
1867 TempoMap::tempo_at_quarter_note (const double& qn) const
1869 Glib::Threads::RWLock::ReaderLock lm (lock);
1871 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1874 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1875 * @param tempo the tempo.
1876 * @return the position in quarter-note beats where the map bpm
1877 * is equal to that of the Tempo. currently ignores note_type.
1880 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1882 Glib::Threads::RWLock::ReaderLock lm (lock);
1884 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1887 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1888 * @param metrics the list of metric sections used to calculate the pulse.
1889 * @param beat The BBT (meter-based) beat.
1890 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1892 * a pulse or whole note is the base musical position of a MetricSection.
1893 * it is equivalent to four quarter notes.
1897 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1899 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1901 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1904 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1905 * @param metrics the list of metric sections used to calculate the beat.
1906 * @param pulse the whole-note pulse.
1907 * @return the meter-based beat at the supplied whole-note pulse.
1909 * a pulse or whole note is the base musical position of a MetricSection.
1910 * it is equivalent to four quarter notes.
1913 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1915 MeterSection* prev_m = 0;
1917 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1919 if (!(*i)->is_tempo()) {
1920 m = static_cast<MeterSection*> (*i);
1921 if (prev_m && m->pulse() > pulse) {
1929 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1933 /* tempo section based */
1935 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1937 /* HOLD (at least) THE READER LOCK */
1938 TempoSection* prev_t = 0;
1940 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1942 if ((*i)->is_tempo()) {
1943 t = static_cast<TempoSection*> (*i);
1947 if (prev_t && t->minute() > minute) {
1948 /*the previous ts is the one containing the frame */
1949 const double ret = prev_t->pulse_at_minute (minute);
1950 /* audio locked section in new meter*/
1951 if (t->pulse() < ret) {
1960 /* treated as constant for this ts */
1961 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1963 return pulses_in_section + prev_t->pulse();
1966 /* tempo section based */
1968 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1970 /* HOLD THE READER LOCK */
1972 const TempoSection* prev_t = 0;
1974 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1977 if ((*i)->is_tempo()) {
1978 t = static_cast<TempoSection*> (*i);
1982 if (prev_t && t->pulse() > pulse) {
1983 return prev_t->minute_at_pulse (pulse);
1989 /* must be treated as constant, irrespective of _type */
1990 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1992 return dtime + prev_t->minute();
1995 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1996 * @param bbt The BBT time (meter-based).
1997 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
2001 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
2003 Glib::Threads::RWLock::ReaderLock lm (lock);
2004 return beat_at_bbt_locked (_metrics, bbt);
2009 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2011 /* CALLER HOLDS READ LOCK */
2013 MeterSection* prev_m = 0;
2015 /* because audio-locked meters have 'fake' integral beats,
2016 there is no pulse offset here.
2020 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2021 if (!(*i)->is_tempo()) {
2022 m = static_cast<MeterSection*> (*i);
2024 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2025 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2033 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2034 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2035 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2040 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2041 * @param beat The BBT (meter-based) beat.
2042 * @return The BBT time (meter-based) at the supplied meter-based beat.
2046 TempoMap::bbt_at_beat (const double& beat)
2048 Glib::Threads::RWLock::ReaderLock lm (lock);
2049 return bbt_at_beat_locked (_metrics, beat);
2053 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2055 /* CALLER HOLDS READ LOCK */
2056 MeterSection* prev_m = 0;
2057 const double beats = max (0.0, b);
2059 MeterSection* m = 0;
2061 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2062 if (!(*i)->is_tempo()) {
2063 m = static_cast<MeterSection*> (*i);
2065 if (m->beat() > beats) {
2066 /* this is the meter after the one our beat is on*/
2076 const double beats_in_ms = beats - prev_m->beat();
2077 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2078 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2079 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2080 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2084 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2085 ret.beats = (uint32_t) floor (remaining_beats);
2086 ret.bars = total_bars;
2088 /* 0 0 0 to 1 1 0 - based mapping*/
2092 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2094 ret.ticks -= BBT_Time::ticks_per_beat;
2097 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2105 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2106 * @param bbt The BBT time (meter-based).
2107 * @return the quarter note beat at the supplied BBT time
2109 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2111 * while the input uses meter, the output does not.
2114 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2116 Glib::Threads::RWLock::ReaderLock lm (lock);
2118 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2122 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2124 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2127 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2130 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2134 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2136 /* CALLER HOLDS READ LOCK */
2138 MeterSection* prev_m = 0;
2140 /* because audio-locked meters have 'fake' integral beats,
2141 there is no pulse offset here.
2145 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2146 if (!(*i)->is_tempo()) {
2147 m = static_cast<MeterSection*> (*i);
2149 if (m->bbt().bars > bbt.bars) {
2157 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2158 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2159 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2164 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2165 * @param qn the quarter-note beat.
2166 * @return The BBT time (meter-based) at the supplied meter-based beat.
2168 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2172 TempoMap::bbt_at_quarter_note (const double& qn)
2174 Glib::Threads::RWLock::ReaderLock lm (lock);
2176 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2179 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2180 * @param metrics The list of metric sections used to determine the result.
2181 * @param pulse The whole-note pulse.
2182 * @return The BBT time at the supplied whole-note pulse.
2184 * a pulse or whole note is the basic musical position of a MetricSection.
2185 * it is equivalent to four quarter notes.
2186 * while the output uses meter, the input does not.
2189 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2191 MeterSection* prev_m = 0;
2193 MeterSection* m = 0;
2195 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2197 if (!(*i)->is_tempo()) {
2198 m = static_cast<MeterSection*> (*i);
2201 double const pulses_to_m = m->pulse() - prev_m->pulse();
2202 if (prev_m->pulse() + pulses_to_m > pulse) {
2203 /* this is the meter after the one our beat is on*/
2214 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2215 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2216 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2217 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2218 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2222 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2223 ret.beats = (uint32_t) floor (remaining_beats);
2224 ret.bars = total_bars;
2226 /* 0 0 0 to 1 1 0 mapping*/
2230 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2232 ret.ticks -= BBT_Time::ticks_per_beat;
2235 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2243 /** Returns the BBT time corresponding to the supplied frame position.
2244 * @param frame the position in audio samples.
2245 * @return the BBT time at the frame position .
2249 TempoMap::bbt_at_frame (framepos_t frame)
2257 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2262 const double minute = minute_at_frame (frame);
2264 Glib::Threads::RWLock::ReaderLock lm (lock);
2266 return bbt_at_minute_locked (_metrics, minute);
2270 TempoMap::bbt_at_frame_rt (framepos_t frame)
2272 const double minute = minute_at_frame (frame);
2274 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2277 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2280 return bbt_at_minute_locked (_metrics, minute);
2284 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2294 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2295 MeterSection* prev_m = 0;
2296 MeterSection* next_m = 0;
2300 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2301 if (!(*i)->is_tempo()) {
2302 m = static_cast<MeterSection*> (*i);
2303 if (prev_m && m->minute() > minute) {
2311 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2313 /* handle frame before first meter */
2314 if (minute < prev_m->minute()) {
2317 /* audio locked meters fake their beat */
2318 if (next_m && next_m->beat() < beat) {
2319 beat = next_m->beat();
2322 beat = max (0.0, beat);
2324 const double beats_in_ms = beat - prev_m->beat();
2325 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2326 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2327 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2328 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2332 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2333 ret.beats = (uint32_t) floor (remaining_beats);
2334 ret.bars = total_bars;
2336 /* 0 0 0 to 1 1 0 - based mapping*/
2340 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2342 ret.ticks -= BBT_Time::ticks_per_beat;
2345 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2353 /** Returns the frame position corresponding to the supplied BBT time.
2354 * @param bbt the position in BBT time.
2355 * @return the frame position at bbt.
2359 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2363 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2368 if (bbt.beats < 1) {
2369 throw std::logic_error ("beats are counted from one");
2374 Glib::Threads::RWLock::ReaderLock lm (lock);
2375 minute = minute_at_bbt_locked (_metrics, bbt);
2378 return frame_at_minute (minute);
2381 /* meter & tempo section based */
2383 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2385 /* HOLD THE READER LOCK */
2387 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2392 * Returns the quarter-note beat position corresponding to the supplied frame.
2394 * @param frame the position in frames.
2395 * @return The quarter-note position of the supplied frame. Ignores meter.
2399 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2401 const double minute = minute_at_frame (frame);
2403 Glib::Threads::RWLock::ReaderLock lm (lock);
2405 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2409 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2411 const double minute = minute_at_frame (frame);
2413 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2416 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2419 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2423 * Returns the frame position corresponding to the supplied quarter-note beat.
2425 * @param quarter_note the quarter-note position.
2426 * @return the frame position of the supplied quarter-note. Ignores meter.
2431 TempoMap::frame_at_quarter_note (const double quarter_note) const
2435 Glib::Threads::RWLock::ReaderLock lm (lock);
2437 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2440 return frame_at_minute (minute);
2443 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2444 * @param beat The BBT (meter-based) beat.
2445 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2447 * a quarter-note may be compared with and assigned to Evoral::Beats.
2451 TempoMap::quarter_note_at_beat (const double beat) const
2453 Glib::Threads::RWLock::ReaderLock lm (lock);
2455 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2458 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2459 * @param quarter_note The position in quarter-note beats.
2460 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2462 * a quarter-note is the musical unit of Evoral::Beats.
2466 TempoMap::beat_at_quarter_note (const double quarter_note) const
2468 Glib::Threads::RWLock::ReaderLock lm (lock);
2470 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2473 /** Returns the duration in frames between two supplied quarter-note beat positions.
2474 * @param start the first position in quarter-note beats.
2475 * @param end the end position in quarter-note beats.
2476 * @return the frame distance ober the quarter-note beats duration.
2478 * use this rather than e.g.
2479 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2480 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2484 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2489 Glib::Threads::RWLock::ReaderLock lm (lock);
2490 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2493 return frame_at_minute (minutes);
2497 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2500 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2504 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2506 Glib::Threads::RWLock::ReaderLock lm (lock);
2508 return quarter_notes_between_frames_locked (_metrics, start, end);
2512 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2514 const TempoSection* prev_t = 0;
2516 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2519 if ((*i)->is_tempo()) {
2520 t = static_cast<TempoSection*> (*i);
2524 if (prev_t && t->frame() > start) {
2531 const double start_qn = prev_t->pulse_at_frame (start);
2533 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2536 if ((*i)->is_tempo()) {
2537 t = static_cast<TempoSection*> (*i);
2541 if (prev_t && t->frame() > end) {
2547 const double end_qn = prev_t->pulse_at_frame (end);
2549 return (end_qn - start_qn) * 4.0;
2553 TempoMap::check_solved (const Metrics& metrics) const
2555 TempoSection* prev_t = 0;
2556 MeterSection* prev_m = 0;
2558 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2561 if ((*i)->is_tempo()) {
2562 t = static_cast<TempoSection*> (*i);
2567 /* check ordering */
2568 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2572 /* precision check ensures tempo and frames align.*/
2573 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))) {
2574 if (!t->locked_to_meter()) {
2579 /* gradient limit - who knows what it should be?
2580 things are also ok (if a little chaotic) without this
2582 if (fabs (prev_t->c()) > 1000.0) {
2583 //std::cout << "c : " << prev_t->c() << std::endl;
2590 if (!(*i)->is_tempo()) {
2591 m = static_cast<MeterSection*> (*i);
2592 if (prev_m && m->position_lock_style() == AudioTime) {
2593 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2594 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2595 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2597 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2611 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2613 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2615 if ((*i)->is_tempo()) {
2616 t = static_cast<TempoSection*> (*i);
2617 if (t->locked_to_meter()) {
2618 t->set_active (true);
2619 } else if (t->position_lock_style() == AudioTime) {
2620 if (t->frame() < frame) {
2621 t->set_active (false);
2622 t->set_pulse (-1.0);
2623 } else if (t->frame() > frame) {
2624 t->set_active (true);
2625 } else if (t->frame() == frame) {
2635 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2637 TempoSection* prev_t = 0;
2638 TempoSection* section_prev = 0;
2639 double first_m_minute = 0.0;
2640 const bool sml = section->locked_to_meter();
2642 /* can't move a tempo before the first meter */
2643 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2645 if (!(*i)->is_tempo()) {
2646 m = static_cast<MeterSection*> (*i);
2648 first_m_minute = m->minute();
2653 if (!section->initial() && minute <= first_m_minute) {
2657 section->set_active (true);
2658 section->set_minute (minute);
2660 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2662 if ((*i)->is_tempo()) {
2663 t = static_cast<TempoSection*> (*i);
2675 if (t->frame() == frame_at_minute (minute)) {
2679 const bool tlm = t->position_lock_style() == MusicTime;
2681 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2682 section_prev = prev_t;
2684 section_prev->set_c (section_prev->compute_c_minute (section->note_types_per_minute(), minute));
2685 if (!section->locked_to_meter()) {
2686 section->set_pulse (section_prev->pulse_at_ntpm (section->note_types_per_minute(), minute));
2691 if (t->position_lock_style() == MusicTime) {
2692 prev_t->set_c (prev_t->compute_c_pulse (t->note_types_per_minute(), t->pulse()));
2693 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2695 prev_t->set_c (prev_t->compute_c_minute (t->note_types_per_minute(), t->minute()));
2696 if (!t->locked_to_meter()) {
2697 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2706 recompute_tempi (imaginary);
2708 if (check_solved (imaginary)) {
2711 dunp (imaginary, std::cout);
2715 MetricSectionFrameSorter fcmp;
2716 imaginary.sort (fcmp);
2718 recompute_tempi (imaginary);
2720 if (check_solved (imaginary)) {
2728 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2730 TempoSection* prev_t = 0;
2731 TempoSection* section_prev = 0;
2733 section->set_pulse (pulse);
2735 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2737 if ((*i)->is_tempo()) {
2738 t = static_cast<TempoSection*> (*i);
2749 section_prev = prev_t;
2753 if (t->position_lock_style() == MusicTime) {
2754 prev_t->set_c (prev_t->compute_c_pulse (t->note_types_per_minute(), t->pulse()));
2755 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2757 prev_t->set_c (prev_t->compute_c_minute (t->note_types_per_minute(), t->minute()));
2758 if (!t->locked_to_meter()) {
2759 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2768 section_prev->set_c (section_prev->compute_c_pulse (section->note_types_per_minute(), pulse));
2769 section->set_minute (section_prev->minute_at_ntpm (section->note_types_per_minute(), pulse));
2773 recompute_tempi (imaginary);
2775 if (check_solved (imaginary)) {
2778 dunp (imaginary, std::cout);
2782 MetricSectionSorter cmp;
2783 imaginary.sort (cmp);
2785 recompute_tempi (imaginary);
2787 * XX need a restriction here, but only for this case,
2788 * as audio locked tempos don't interact in the same way.
2790 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2792 * |50 bpm |250 bpm |60 bpm
2793 * drag 250 to the pulse after 60->
2794 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2796 if (check_solved (imaginary)) {
2804 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2806 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2807 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2808 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2812 if (section->initial()) {
2813 /* lock the first tempo to our first meter */
2814 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2819 TempoSection* meter_locked_tempo = 0;
2821 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2823 if ((*ii)->is_tempo()) {
2824 t = static_cast<TempoSection*> (*ii);
2825 if (t->locked_to_meter() && t->frame() == section->frame()) {
2826 meter_locked_tempo = t;
2832 if (!meter_locked_tempo) {
2836 MeterSection* prev_m = 0;
2838 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2839 bool solved = false;
2841 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2843 if (!(*i)->is_tempo()) {
2844 m = static_cast<MeterSection*> (*i);
2846 if (prev_m && !section->initial()) {
2847 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2848 if (beats + prev_m->beat() < section->beat()) {
2849 /* set the section pulse according to its musical position,
2850 * as an earlier time than this has been requested.
2852 const double new_pulse = ((section->beat() - prev_m->beat())
2853 / prev_m->note_divisor()) + prev_m->pulse();
2855 tempo_copy->set_position_lock_style (MusicTime);
2856 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2857 meter_locked_tempo->set_position_lock_style (MusicTime);
2858 section->set_position_lock_style (MusicTime);
2859 section->set_pulse (new_pulse);
2860 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2861 meter_locked_tempo->set_position_lock_style (AudioTime);
2862 section->set_position_lock_style (AudioTime);
2863 section->set_minute (meter_locked_tempo->minute());
2869 Metrics::const_iterator d = future_map.begin();
2870 while (d != future_map.end()) {
2879 /* all is ok. set section's locked tempo if allowed.
2880 possibly disallow if there is an adjacent audio-locked tempo.
2881 XX this check could possibly go. its never actually happened here.
2883 MeterSection* meter_copy = const_cast<MeterSection*>
2884 (&meter_section_at_minute_locked (future_map, section->minute()));
2886 meter_copy->set_minute (minute);
2888 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2889 section->set_minute (minute);
2890 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2891 / prev_m->note_divisor()) + prev_m->pulse());
2892 solve_map_minute (imaginary, meter_locked_tempo, minute);
2897 Metrics::const_iterator d = future_map.begin();
2898 while (d != future_map.end()) {
2908 /* initial (first meter atm) */
2910 tempo_copy->set_minute (minute);
2911 tempo_copy->set_pulse (0.0);
2913 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2914 section->set_minute (minute);
2915 meter_locked_tempo->set_minute (minute);
2916 meter_locked_tempo->set_pulse (0.0);
2917 solve_map_minute (imaginary, meter_locked_tempo, minute);
2922 Metrics::const_iterator d = future_map.begin();
2923 while (d != future_map.end()) {
2932 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2933 section->set_beat (b_bbt);
2934 section->set_pulse (0.0);
2944 MetricSectionFrameSorter fcmp;
2945 imaginary.sort (fcmp);
2947 recompute_meters (imaginary);
2953 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2955 /* disallow setting section to an existing meter's bbt */
2956 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2958 if (!(*i)->is_tempo()) {
2959 m = static_cast<MeterSection*> (*i);
2960 if (m != section && m->bbt().bars == when.bars) {
2966 MeterSection* prev_m = 0;
2967 MeterSection* section_prev = 0;
2969 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2971 if (!(*i)->is_tempo()) {
2972 m = static_cast<MeterSection*> (*i);
2978 pair<double, BBT_Time> b_bbt;
2979 double new_pulse = 0.0;
2981 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2982 section_prev = prev_m;
2984 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2985 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2986 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2988 section->set_beat (b_bbt);
2989 section->set_pulse (pulse);
2990 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2994 if (m->position_lock_style() == AudioTime) {
2995 TempoSection* meter_locked_tempo = 0;
2997 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2999 if ((*ii)->is_tempo()) {
3000 t = static_cast<TempoSection*> (*ii);
3001 if (t->locked_to_meter() && t->frame() == m->frame()) {
3002 meter_locked_tempo = t;
3008 if (!meter_locked_tempo) {
3013 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3015 if (beats + prev_m->beat() != m->beat()) {
3016 /* tempo/ meter change caused a change in beat (bar). */
3018 /* the user has requested that the previous section of music overlaps this one.
3019 we have no choice but to change the bar number here, as being locked to audio means
3020 we must stay where we are on the timeline.
3022 beats = m->beat() - prev_m->beat();
3023 b_bbt = make_pair (beats + prev_m->beat()
3024 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3025 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3027 } else if (!m->initial()) {
3028 b_bbt = make_pair (m->beat(), m->bbt());
3029 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3032 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3035 meter_locked_tempo->set_pulse (new_pulse);
3036 m->set_beat (b_bbt);
3037 m->set_pulse (new_pulse);
3041 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3042 if (beats + prev_m->beat() != m->beat()) {
3043 /* tempo/ meter change caused a change in beat (bar). */
3044 b_bbt = make_pair (beats + prev_m->beat()
3045 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3047 b_bbt = make_pair (beats + prev_m->beat()
3050 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3051 m->set_beat (b_bbt);
3052 m->set_pulse (new_pulse);
3053 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3060 if (!section_prev) {
3062 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3063 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3064 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3066 section->set_beat (b_bbt);
3067 section->set_pulse (pulse);
3068 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3071 MetricSectionSorter cmp;
3072 imaginary.sort (cmp);
3074 recompute_meters (imaginary);
3079 /** places a copy of _metrics into copy and returns a pointer
3080 * to section's equivalent in copy.
3083 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
3085 TempoSection* ret = 0;
3087 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3090 if ((*i)->is_tempo()) {
3091 t = static_cast<TempoSection*> (*i);
3093 ret = new TempoSection (*t);
3094 copy.push_back (ret);
3098 TempoSection* cp = new TempoSection (*t);
3099 copy.push_back (cp);
3101 if (!(*i)->is_tempo()) {
3102 m = static_cast<MeterSection *> (*i);
3103 MeterSection* cp = new MeterSection (*m);
3104 copy.push_back (cp);
3112 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3114 MeterSection* ret = 0;
3116 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3119 if ((*i)->is_tempo()) {
3120 t = static_cast<TempoSection*> (*i);
3121 TempoSection* cp = new TempoSection (*t);
3122 copy.push_back (cp);
3125 if (!(*i)->is_tempo()) {
3126 m = static_cast<MeterSection *> (*i);
3128 ret = new MeterSection (*m);
3129 copy.push_back (ret);
3132 MeterSection* cp = new MeterSection (*m);
3133 copy.push_back (cp);
3140 /** answers the question "is this a valid beat position for this tempo section?".
3141 * it returns true if the tempo section can be moved to the requested bbt position,
3142 * leaving the tempo map in a solved state.
3143 * @param ts the tempo section to be moved
3144 * @param bbt the requested new position for the tempo section
3145 * @return true if the tempo section can be moved to the position, otherwise false.
3148 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3151 TempoSection* tempo_copy = 0;
3154 Glib::Threads::RWLock::ReaderLock lm (lock);
3155 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3161 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3163 Metrics::const_iterator d = copy.begin();
3164 while (d != copy.end()) {
3173 * 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,
3174 * taking any possible reordering as a consequence of this into account.
3175 * @param section - the section to be altered
3176 * @param bbt - the BBT time where the altered tempo will fall
3177 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3179 pair<double, framepos_t>
3180 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3183 pair<double, framepos_t> ret = make_pair (0.0, 0);
3185 Glib::Threads::RWLock::ReaderLock lm (lock);
3187 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3189 const double beat = beat_at_bbt_locked (future_map, bbt);
3191 if (section->position_lock_style() == AudioTime) {
3192 tempo_copy->set_position_lock_style (MusicTime);
3195 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3196 ret.first = tempo_copy->pulse();
3197 ret.second = tempo_copy->frame();
3199 ret.first = section->pulse();
3200 ret.second = section->frame();
3203 Metrics::const_iterator d = future_map.begin();
3204 while (d != future_map.end()) {
3211 /** moves a TempoSection to a specified position.
3212 * @param ts - the section to be moved
3213 * @param frame - the new position in frames for the tempo
3214 * @param sub_num - the snap division to use if using musical time.
3216 * if sub_num is non-zero, the frame position is used to calculate an exact
3219 * -1 | snap to bars (meter-based)
3220 * 0 | no snap - use audio frame for musical position
3221 * 1 | snap to meter-based (BBT) beat
3222 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3224 * this follows the snap convention in the gui.
3225 * if sub_num is zero, the musical position will be taken from the supplied frame.
3228 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3232 if (ts->position_lock_style() == MusicTime) {
3234 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3235 Glib::Threads::RWLock::WriterLock lm (lock);
3236 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3238 tempo_copy->set_position_lock_style (AudioTime);
3240 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3241 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3242 const double pulse = pulse_at_beat_locked (future_map, beat);
3244 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3245 solve_map_pulse (_metrics, ts, pulse);
3246 recompute_meters (_metrics);
3254 Glib::Threads::RWLock::WriterLock lm (lock);
3255 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3257 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3259 /* We're moving the object that defines the grid while snapping to it...
3260 * Placing the ts at the beat corresponding to the requested frame may shift the
3261 * grid in such a way that the mouse is left hovering over a completerly different division,
3262 * causing jittering when the mouse next moves (esp. large tempo deltas).
3264 * This alters the snap behaviour slightly in that we snap to beat divisions
3265 * in the future map rather than the existing one.
3267 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3268 const framepos_t snapped_frame = frame_at_minute (minute_at_pulse_locked (future_map, qn / 4.0));
3270 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3271 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3272 ts->set_pulse (qn / 4.0);
3273 recompute_meters (_metrics);
3276 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3277 recompute_meters (_metrics);
3283 Metrics::const_iterator d = future_map.begin();
3284 while (d != future_map.end()) {
3289 MetricPositionChanged (PropertyChange ()); // Emit Signal
3292 /** moves a MeterSection to a specified position.
3293 * @param ms - the section to be moved
3294 * @param frame - the new position in frames for the meter
3296 * as a meter cannot snap to anything but bars,
3297 * the supplied frame is rounded to the nearest bar, possibly
3298 * leaving the meter position unchanged.
3301 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3305 if (ms->position_lock_style() == AudioTime) {
3308 Glib::Threads::RWLock::WriterLock lm (lock);
3309 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3311 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3312 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3313 recompute_tempi (_metrics);
3318 Glib::Threads::RWLock::WriterLock lm (lock);
3319 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3321 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3322 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3324 if (solve_map_bbt (future_map, copy, bbt)) {
3325 solve_map_bbt (_metrics, ms, bbt);
3326 recompute_tempi (_metrics);
3331 Metrics::const_iterator d = future_map.begin();
3332 while (d != future_map.end()) {
3337 MetricPositionChanged (PropertyChange ()); // Emit Signal
3341 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3344 bool can_solve = false;
3346 Glib::Threads::RWLock::WriterLock lm (lock);
3347 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3348 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3349 recompute_tempi (future_map);
3351 if (check_solved (future_map)) {
3352 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3353 recompute_map (_metrics);
3358 Metrics::const_iterator d = future_map.begin();
3359 while (d != future_map.end()) {
3364 MetricPositionChanged (PropertyChange ()); // Emit Signal
3370 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
3373 Ts (future prev_t) Tnext
3376 |----------|----------
3383 Glib::Threads::RWLock::WriterLock lm (lock);
3389 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3390 TempoSection* prev_to_prev_t = 0;
3391 const frameoffset_t fr_off = end_frame - frame;
3395 if (prev_t->pulse() > 0.0) {
3396 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
3399 TempoSection* next_t = 0;
3400 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
3401 TempoSection* t = 0;
3402 if ((*i)->is_tempo()) {
3403 t = static_cast<TempoSection*> (*i);
3404 if (t->frame() > ts->frame()) {
3410 /* minimum allowed measurement distance in frames */
3411 const framepos_t min_dframe = 2;
3413 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3414 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3416 double contribution = 0.0;
3418 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3419 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3422 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3424 const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame));
3425 const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame));
3429 if (prev_t->type() == TempoSection::Constant || prev_t->c() == 0.0) {
3431 if (prev_t->position_lock_style() == MusicTime) {
3432 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3433 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 /* prev to prev is irrelevant */
3443 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3444 new_bpm = prev_t->note_types_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3446 new_bpm = prev_t->note_types_per_minute();
3451 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3452 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3454 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3455 / (double) ((end_frame) - prev_to_prev_t->frame()));
3457 new_bpm = prev_t->note_types_per_minute();
3460 /* prev_to_prev_t is irrelevant */
3462 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3463 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3465 new_bpm = prev_t->note_types_per_minute();
3471 double frame_ratio = 1.0;
3472 double pulse_ratio = 1.0;
3473 const double pulse_pos = frame;
3475 if (prev_to_prev_t) {
3476 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3477 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3479 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3480 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3483 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3484 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3486 pulse_ratio = (start_pulse / end_pulse);
3488 new_bpm = prev_t->note_types_per_minute() * (pulse_ratio * frame_ratio);
3491 /* don't clamp and proceed here.
3492 testing has revealed that this can go negative,
3493 which is an entirely different thing to just being too low.
3495 if (new_bpm < 0.5) {
3498 new_bpm = min (new_bpm, (double) 1000.0);
3499 prev_t->set_note_types_per_minute (new_bpm);
3500 recompute_tempi (future_map);
3501 recompute_meters (future_map);
3503 if (check_solved (future_map)) {
3504 ts->set_note_types_per_minute (new_bpm);
3505 recompute_tempi (_metrics);
3506 recompute_meters (_metrics);
3510 Metrics::const_iterator d = future_map.begin();
3511 while (d != future_map.end()) {
3516 MetricPositionChanged (PropertyChange ()); // Emit Signal
3519 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3520 * the supplied frame, possibly returning a negative value.
3522 * @param frame The session frame position.
3523 * @param sub_num The subdivision to use when rounding the beat.
3524 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3525 * Positive integers indicate quarter note (non BBT) divisions.
3526 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3527 * @return The beat position of the supplied frame.
3529 * when working to a musical grid, the use of sub_nom indicates that
3530 * the position should be interpreted musically.
3532 * it effectively snaps to meter bars, meter beats or quarter note divisions
3533 * (as per current gui convention) and returns a musical position independent of frame rate.
3535 * If the supplied frame lies before the first meter, the return will be negative,
3536 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3537 * the continuation of the tempo curve (backwards).
3539 * This function is sensitive to tempo and meter.
3542 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3544 Glib::Threads::RWLock::ReaderLock lm (lock);
3546 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3550 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3552 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3555 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3556 * the supplied frame, possibly returning a negative value.
3558 * @param frame The session frame position.
3559 * @param sub_num The subdivision to use when rounding the quarter note.
3560 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3561 * Positive integers indicate quarter note (non BBT) divisions.
3562 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3563 * @return The quarter note position of the supplied frame.
3565 * When working to a musical grid, the use of sub_nom indicates that
3566 * the frame position should be interpreted musically.
3568 * it effectively snaps to meter bars, meter beats or quarter note divisions
3569 * (as per current gui convention) and returns a musical position independent of frame rate.
3571 * If the supplied frame lies before the first meter, the return will be negative,
3572 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3573 * the continuation of the tempo curve (backwards).
3575 * This function is tempo-sensitive.
3578 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3580 Glib::Threads::RWLock::ReaderLock lm (lock);
3582 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3586 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3588 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3591 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3592 } else if (sub_num == 1) {
3593 /* the gui requested exact musical (BBT) beat */
3594 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3595 } else if (sub_num == -1) {
3597 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3601 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3603 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3605 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3615 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3616 * @param pos the frame position in the tempo map.
3617 * @param bbt the distance in BBT time from pos to calculate.
3618 * @param dir the rounding direction..
3619 * @return the duration in frames between pos and bbt
3622 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3624 Glib::Threads::RWLock::ReaderLock lm (lock);
3626 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3628 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3631 pos_bbt.bars += bbt.bars;
3633 pos_bbt.ticks += bbt.ticks;
3634 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3636 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3639 pos_bbt.beats += bbt.beats;
3640 if ((double) pos_bbt.beats > divisions) {
3642 pos_bbt.beats -= divisions;
3644 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3646 return pos_bbt_frame - pos;
3650 if (pos_bbt.bars <= bbt.bars) {
3653 pos_bbt.bars -= bbt.bars;
3656 if (pos_bbt.ticks < bbt.ticks) {
3657 if (pos_bbt.bars > 1) {
3658 if (pos_bbt.beats == 1) {
3660 pos_bbt.beats = divisions;
3664 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3670 pos_bbt.ticks -= bbt.ticks;
3673 if (pos_bbt.beats <= bbt.beats) {
3674 if (pos_bbt.bars > 1) {
3676 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3681 pos_bbt.beats -= bbt.beats;
3684 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3691 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3693 return round_to_type (fr, dir, Bar);
3697 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3699 return round_to_type (fr, dir, Beat);
3703 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3705 Glib::Threads::RWLock::ReaderLock lm (lock);
3706 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);
3707 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3708 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3710 ticks -= beats * BBT_Time::ticks_per_beat;
3713 /* round to next (or same iff dir == RoundUpMaybe) */
3715 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3717 if (mod == 0 && dir == RoundUpMaybe) {
3718 /* right on the subdivision, which is fine, so do nothing */
3720 } else if (mod == 0) {
3721 /* right on the subdivision, so the difference is just the subdivision ticks */
3722 ticks += ticks_one_subdivisions_worth;
3725 /* not on subdivision, compute distance to next subdivision */
3727 ticks += ticks_one_subdivisions_worth - mod;
3730 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3731 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3732 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3733 // But I'm keeping it around, until we determine there are no terrible consequences.
3734 // if (ticks >= BBT_Time::ticks_per_beat) {
3735 // ticks -= BBT_Time::ticks_per_beat;
3738 } else if (dir < 0) {
3740 /* round to previous (or same iff dir == RoundDownMaybe) */
3742 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3744 if (difference == 0 && dir == RoundDownAlways) {
3745 /* right on the subdivision, but force-rounding down,
3746 so the difference is just the subdivision ticks */
3747 difference = ticks_one_subdivisions_worth;
3750 if (ticks < difference) {
3751 ticks = BBT_Time::ticks_per_beat - ticks;
3753 ticks -= difference;
3757 /* round to nearest */
3760 /* compute the distance to the previous and next subdivision */
3762 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3764 /* closer to the next subdivision, so shift forward */
3766 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3768 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3770 if (ticks > BBT_Time::ticks_per_beat) {
3772 ticks -= BBT_Time::ticks_per_beat;
3773 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3776 } else if (rem > 0) {
3778 /* closer to previous subdivision, so shift backward */
3782 /* can't go backwards past zero, so ... */
3783 return MusicFrame (0, 0);
3785 /* step back to previous beat */
3787 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3788 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3790 ticks = lrint (ticks - rem);
3791 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3794 /* on the subdivision, do nothing */
3798 MusicFrame ret (0, 0);
3799 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
3800 ret.division = sub_num;
3806 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3808 Glib::Threads::RWLock::ReaderLock lm (lock);
3809 const double minute = minute_at_frame (frame);
3810 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
3811 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3812 MusicFrame ret (0, 0);
3819 /* find bar previous to 'frame' */
3825 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3829 } else if (dir > 0) {
3830 /* find bar following 'frame' */
3835 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3839 /* true rounding: find nearest bar */
3840 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3843 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3845 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3847 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3848 ret.frame = next_ft;
3853 ret.frame = prev_ft;
3865 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
3868 } else if (dir > 0) {
3869 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
3873 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
3880 return MusicFrame (0, 0);
3884 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3885 framepos_t lower, framepos_t upper, uint32_t bar_mod)
3887 Glib::Threads::RWLock::ReaderLock lm (lock);
3888 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
3890 /* although the map handles negative beats, bbt doesn't. */
3895 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
3899 while (pos >= 0 && pos < upper) {
3900 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
3901 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3902 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3903 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3905 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
3909 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
3914 bbt.bars -= bbt.bars % bar_mod;
3918 while (pos >= 0 && pos < upper) {
3919 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3920 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3921 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3922 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
3923 bbt.bars += bar_mod;
3929 TempoMap::tempo_section_at_frame (framepos_t frame) const
3931 Glib::Threads::RWLock::ReaderLock lm (lock);
3933 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
3937 TempoMap::tempo_section_at_frame (framepos_t frame)
3939 Glib::Threads::RWLock::ReaderLock lm (lock);
3941 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
3945 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
3947 TempoSection* prev = 0;
3951 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3953 if ((*i)->is_tempo()) {
3954 t = static_cast<TempoSection*> (*i);
3958 if (prev && t->minute() > minute) {
3968 abort(); /*NOTREACHED*/
3974 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
3976 TempoSection* prev = 0;
3980 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3982 if ((*i)->is_tempo()) {
3983 t = static_cast<TempoSection*> (*i);
3987 if (prev && t->minute() > minute) {
3997 abort(); /*NOTREACHED*/
4003 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4005 TempoSection* prev_t = 0;
4006 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4010 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4011 if ((*i)->is_tempo()) {
4012 t = static_cast<TempoSection*> (*i);
4018 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4028 /* don't use this to calculate length (the tempo is only correct for this frame).
4029 do that stuff based on the beat_at_frame and frame_at_beat api
4032 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4034 Glib::Threads::RWLock::ReaderLock lm (lock);
4036 const TempoSection* ts_at = 0;
4037 const TempoSection* ts_after = 0;
4038 Metrics::const_iterator i;
4041 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4043 if ((*i)->is_tempo()) {
4044 t = static_cast<TempoSection*> (*i);
4048 if (ts_at && (*i)->frame() > frame) {
4058 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4060 /* must be treated as constant tempo */
4061 return ts_at->frames_per_quarter_note (_frame_rate);
4065 TempoMap::meter_section_at_frame (framepos_t frame) const
4067 Glib::Threads::RWLock::ReaderLock lm (lock);
4068 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4072 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4074 Metrics::const_iterator i;
4075 MeterSection* prev = 0;
4079 for (i = metrics.begin(); i != metrics.end(); ++i) {
4081 if (!(*i)->is_tempo()) {
4082 m = static_cast<MeterSection*> (*i);
4084 if (prev && (*i)->minute() > minute) {
4094 abort(); /*NOTREACHED*/
4101 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4103 MeterSection* prev_m = 0;
4105 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4107 if (!(*i)->is_tempo()) {
4108 m = static_cast<MeterSection*> (*i);
4109 if (prev_m && m->beat() > beat) {
4120 TempoMap::meter_section_at_beat (double beat) const
4122 Glib::Threads::RWLock::ReaderLock lm (lock);
4123 return meter_section_at_beat_locked (_metrics, beat);
4127 TempoMap::meter_at_frame (framepos_t frame) const
4129 TempoMetric m (metric_at (frame));
4134 TempoMap::fix_legacy_session ()
4136 MeterSection* prev_m = 0;
4137 TempoSection* prev_t = 0;
4138 bool have_initial_t = false;
4140 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4144 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4146 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4149 m->set_minute (0.0);
4150 m->set_position_lock_style (AudioTime);
4155 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4156 + (m->bbt().beats - 1)
4157 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4159 m->set_beat (start);
4160 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4161 + (m->bbt().beats - 1)
4162 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4163 m->set_pulse (start_beat / prev_m->note_divisor());
4166 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4174 t->set_minute (0.0);
4175 t->set_position_lock_style (AudioTime);
4177 have_initial_t = true;
4182 /* some 4.x sessions have no initial (non-movable) tempo. */
4183 if (!have_initial_t) {
4184 prev_t->set_pulse (0.0);
4185 prev_t->set_minute (0.0);
4186 prev_t->set_position_lock_style (AudioTime);
4187 t->set_initial (true);
4188 have_initial_t = true;
4191 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4192 + (t->legacy_bbt().beats - 1)
4193 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4195 t->set_pulse (beat / prev_m->note_divisor());
4197 /* really shouldn't happen but.. */
4198 t->set_pulse (beat / 4.0);
4207 TempoMap::get_state ()
4209 Metrics::const_iterator i;
4210 XMLNode *root = new XMLNode ("TempoMap");
4213 Glib::Threads::RWLock::ReaderLock lm (lock);
4214 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4215 root->add_child_nocopy ((*i)->get_state());
4223 TempoMap::set_state (const XMLNode& node, int /*version*/)
4226 Glib::Threads::RWLock::WriterLock lm (lock);
4229 XMLNodeConstIterator niter;
4230 Metrics old_metrics (_metrics);
4233 nlist = node.children();
4235 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4236 XMLNode* child = *niter;
4238 if (child->name() == TempoSection::xml_state_node_name) {
4241 TempoSection* ts = new TempoSection (*child, _frame_rate);
4242 _metrics.push_back (ts);
4245 catch (failed_constructor& err){
4246 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4247 _metrics = old_metrics;
4248 old_metrics.clear();
4252 } else if (child->name() == MeterSection::xml_state_node_name) {
4255 MeterSection* ms = new MeterSection (*child, _frame_rate);
4256 _metrics.push_back (ms);
4259 catch (failed_constructor& err) {
4260 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4261 _metrics = old_metrics;
4262 old_metrics.clear();
4268 if (niter == nlist.end()) {
4269 MetricSectionSorter cmp;
4270 _metrics.sort (cmp);
4273 /* check for legacy sessions where bbt was the base musical unit for tempo */
4274 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4276 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4277 if (t->legacy_bbt().bars != 0) {
4278 fix_legacy_session();
4285 /* check for multiple tempo/meters at the same location, which
4286 ardour2 somehow allowed.
4289 Metrics::iterator prev = _metrics.end();
4290 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4291 if (prev != _metrics.end()) {
4293 MeterSection* prev_m;
4295 TempoSection* prev_t;
4296 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4297 if (prev_m->pulse() == ms->pulse()) {
4298 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4299 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4302 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4303 if (prev_t->pulse() == ts->pulse()) {
4304 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4305 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4313 recompute_map (_metrics);
4315 Metrics::const_iterator d = old_metrics.begin();
4316 while (d != old_metrics.end()) {
4320 old_metrics.clear ();
4323 PropertyChanged (PropertyChange ());
4329 TempoMap::dump (std::ostream& o) const
4331 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4332 const MeterSection* m;
4333 const TempoSection* t;
4334 const TempoSection* prev_t = 0;
4336 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4338 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4339 o << "Tempo @ " << *i << t->note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4340 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4341 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4342 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4344 o << " current : " << t->note_types_per_minute()
4345 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4346 o << " previous : " << prev_t->note_types_per_minute()
4347 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4348 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4349 << " | " << prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())
4350 << " | " << frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))
4351 << " | " << prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()) << std::endl;
4354 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4355 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4356 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4357 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4360 o << "------" << std::endl;
4364 TempoMap::n_tempos() const
4366 Glib::Threads::RWLock::ReaderLock lm (lock);
4369 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4370 if ((*i)->is_tempo()) {
4379 TempoMap::n_meters() const
4381 Glib::Threads::RWLock::ReaderLock lm (lock);
4384 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4385 if (!(*i)->is_tempo()) {
4394 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4396 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4397 if ((*i)->frame() >= where && !(*i)->initial ()) {
4401 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4402 gui_set_meter_position (ms, (*i)->frame() + amount);
4405 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4406 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4411 PropertyChanged (PropertyChange ());
4415 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4419 std::list<MetricSection*> metric_kill_list;
4421 TempoSection* last_tempo = NULL;
4422 MeterSection* last_meter = NULL;
4423 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4424 bool meter_after = false; // is there a meter marker likewise?
4426 Glib::Threads::RWLock::WriterLock lm (lock);
4427 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4428 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4429 metric_kill_list.push_back(*i);
4430 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4433 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4437 else if ((*i)->frame() >= where) {
4438 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4439 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4440 if ((*i)->frame() == where) {
4441 // marker was immediately after end of range
4442 tempo_after = dynamic_cast<TempoSection*> (*i);
4443 meter_after = dynamic_cast<MeterSection*> (*i);
4449 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4450 if (last_tempo && !tempo_after) {
4451 metric_kill_list.remove(last_tempo);
4452 last_tempo->set_minute (minute_at_frame (where));
4455 if (last_meter && !meter_after) {
4456 metric_kill_list.remove(last_meter);
4457 last_meter->set_minute (minute_at_frame (where));
4461 //remove all the remaining metrics
4462 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4463 _metrics.remove(*i);
4468 recompute_map (_metrics);
4471 PropertyChanged (PropertyChange ());
4475 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4476 * pos can be -ve, if required.
4479 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4481 Glib::Threads::RWLock::ReaderLock lm (lock);
4482 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4484 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4488 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4490 Glib::Threads::RWLock::ReaderLock lm (lock);
4492 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4493 pos_bbt.ticks += op.ticks;
4494 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4496 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4498 pos_bbt.beats += op.beats;
4499 /* the meter in effect will start on the bar */
4500 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();
4501 while (pos_bbt.beats >= divisions_per_bar + 1) {
4503 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4504 pos_bbt.beats -= divisions_per_bar;
4506 pos_bbt.bars += op.bars;
4508 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4511 /** Count the number of beats that are equivalent to distance when going forward,
4515 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4517 Glib::Threads::RWLock::ReaderLock lm (lock);
4519 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4523 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4529 operator<< (std::ostream& o, const Meter& m) {
4530 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4534 operator<< (std::ostream& o, const Tempo& t) {
4535 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4539 operator<< (std::ostream& o, const MetricSection& section) {
4541 o << "MetricSection @ " << section.frame() << ' ';
4543 const TempoSection* ts;
4544 const MeterSection* ms;
4546 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4547 o << *((const Tempo*) ts);
4548 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4549 o << *((const Meter*) ms);