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, 120.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)
96 XMLProperty const * prop;
102 _legacy_bbt = BBT_Time (0, 0, 0);
104 if ((prop = node.property ("start")) != 0) {
105 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
109 /* legacy session - start used to be in bbt*/
112 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
116 if ((prop = node.property ("pulse")) != 0) {
117 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
118 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
124 if ((prop = node.property ("frame")) != 0) {
125 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
126 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
127 throw failed_constructor();
129 set_minute (minute_at_frame (frame));
133 /* XX replace old beats-per-minute name with note-types-per-minute */
134 if ((prop = node.property ("beats-per-minute")) != 0) {
135 if (sscanf (prop->value().c_str(), "%lf", &_note_types_per_minute) != 1 || _note_types_per_minute < 0.0) {
136 error << _("TempoSection XML node has an illegal \"beats-per-minute\" value") << endmsg;
137 throw failed_constructor();
141 if ((prop = node.property ("note-type")) == 0) {
142 /* older session, make note type be quarter by default */
145 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
146 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
147 throw failed_constructor();
151 if ((prop = node.property ("clamped")) == 0) {
152 warning << _("TempoSection XML node has no \"clamped\" property") << endmsg;
155 set_clamped (string_is_affirmative (prop->value()));
158 /* XX replace old end-beats-per-minute name with note-types-per-minute */
159 if ((prop = node.property ("end-beats-per-minute")) != 0) {
160 if (sscanf (prop->value().c_str(), "%lf", &_end_note_types_per_minute) != 1 || _end_note_types_per_minute < 0.0) {
161 info << _("TempoSection XML node has an illegal \"in-beats-per-minute\" value") << endmsg;
162 //throw failed_constructor();
163 _end_note_types_per_minute = _note_types_per_minute;
170 if ((prop = node.property ("tempo-type")) != 0) {
171 TempoSection::Type old_type;
173 old_type = Type (string_2_enum (prop->value(), old_type));
174 if (old_type == TempoSection::Constant) {
175 _end_note_types_per_minute = _note_types_per_minute;
179 if ((prop = node.property ("movable")) == 0) {
180 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
181 throw failed_constructor();
184 set_initial (!string_is_affirmative (prop->value()));
186 if ((prop = node.property ("active")) == 0) {
187 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
190 set_active (string_is_affirmative (prop->value()));
193 if ((prop = node.property ("lock-style")) == 0) {
195 set_position_lock_style (MusicTime);
197 set_position_lock_style (AudioTime);
200 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
203 if ((prop = node.property ("locked-to-meter")) == 0) {
205 set_locked_to_meter (true);
207 set_locked_to_meter (false);
210 set_locked_to_meter (string_is_affirmative (prop->value()));
213 /* 5.5 marked initial tempo as not locked to meter. this should always be true anyway */
215 set_locked_to_meter (true);
220 TempoSection::get_state() const
222 XMLNode *root = new XMLNode (xml_state_node_name);
226 snprintf (buf, sizeof (buf), "%lf", pulse());
227 root->add_property ("pulse", buf);
228 snprintf (buf, sizeof (buf), "%li", frame());
229 root->add_property ("frame", buf);
230 snprintf (buf, sizeof (buf), "%lf", _note_types_per_minute);
231 root->add_property ("beats-per-minute", buf);
232 snprintf (buf, sizeof (buf), "%lf", _note_type);
233 root->add_property ("note-type", buf);
234 snprintf (buf, sizeof (buf), "%s", _clamped?"yes":"no");
235 root->add_property ("clamped", buf);
236 snprintf (buf, sizeof (buf), "%lf", _end_note_types_per_minute);
237 root->add_property ("end-beats-per-minute", buf);
238 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
239 root->add_property ("movable", buf);
240 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
241 root->add_property ("active", buf);
242 root->add_property ("lock-style", enum_2_string (position_lock_style()));
243 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
248 /** returns the Tempo at the session-relative minute.
251 TempoSection::tempo_at_minute (const double& m) const
253 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
255 return Tempo (note_types_per_minute(), note_type());
258 return Tempo (_tempo_at_time (m - minute()), _note_type, _end_note_types_per_minute);
261 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
262 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
263 * @param p the pulse used to calculate the returned minute for constant tempi
264 * @return the minute at the supplied tempo
266 * note that the note_type is currently ignored in this function. see below.
270 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
271 * there should be no ramp between the two even if we are ramped.
272 * in other words a ramp should only place a curve on note_types_per_minute.
273 * we should be able to use Tempo note type here, but the above
274 * complicates things a bit.
277 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
279 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
281 return ((p - pulse()) / pulses_per_minute()) + minute();
284 return _time_at_tempo (ntpm) + minute();
287 /** returns the Tempo at the supplied whole-note pulse.
290 TempoSection::tempo_at_pulse (const double& p) const
292 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
295 return Tempo (note_types_per_minute(), note_type());
298 return Tempo (_tempo_at_pulse (p - pulse()), _note_type, _end_note_types_per_minute);
301 /** returns the whole-note pulse where a tempo in note types per minute occurs.
302 * constant tempi require minute m.
303 * @param ntpm the note types per minute value used to calculate the returned pulse
304 * @param m the minute used to calculate the returned pulse if the tempo is constant
305 * @return the whole-note pulse at the supplied tempo
307 * note that note_type is currently ignored in this function. see minute_at_tempo().
309 * for constant tempi, this is anaologous to pulse_at_minute().
312 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
314 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
316 return ((m - minute()) * pulses_per_minute()) + pulse();
319 return _pulse_at_tempo (ntpm) + pulse();
322 /** returns the whole-note pulse at the supplied session-relative minute.
325 TempoSection::pulse_at_minute (const double& m) const
327 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
329 return ((m - minute()) * pulses_per_minute()) + pulse();
332 return _pulse_at_time (m - minute()) + pulse();
335 /** returns the session-relative minute at the supplied whole-note pulse.
338 TempoSection::minute_at_pulse (const double& p) const
340 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
342 return ((p - pulse()) / pulses_per_minute()) + minute();
345 return _time_at_pulse (p - pulse()) + minute();
348 /** returns thw whole-note pulse at session frame position f.
349 * @param f the frame position.
350 * @return the position in whole-note pulses corresponding to f
352 * for use with musical units whose granularity is coarser than frames (e.g. ticks)
355 TempoSection::pulse_at_frame (const framepos_t& f) const
357 const bool constant = type() == Constant || _c == 0.0 || (initial() && f < frame());
359 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
362 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
366 TempoSection::frame_at_pulse (const double& p) const
368 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
370 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
373 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
381 Tt----|-----------------*|
382 Ta----|--------------|* |
388 _______________|___|____
389 time a t (next tempo)
392 Duration in beats at time a is the integral of some Tempo function.
393 In our case, the Tempo function (Tempo at time t) is
396 with function constant
401 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
402 b(t) = T0(e^(ct) - 1) / c
404 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:
405 t(b) = log((c.b / T0) + 1) / c
407 The time t at which Tempo T occurs is a as above:
408 t(T) = log(T / T0) / c
410 The beat at which a Tempo T occurs is:
413 The Tempo at which beat b occurs is:
416 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
417 Our problem is that we usually don't know t.
418 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.
419 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
420 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
422 By substituting our expanded t as a in the c function above, our problem is reduced to:
423 c = T0 (e^(log (Ta / T0)) - 1) / b
425 Of course the word 'beat' has been left loosely defined above.
426 In music, a beat is defined by the musical pulse (which comes from the tempo)
427 and the meter in use at a particular time (how many pulse divisions there are in one bar).
428 It would be more accurate to substitute the work 'pulse' for 'beat' above.
432 We can now store c for future time calculations.
433 If the following tempo section (the one that defines c in conjunction with this one)
434 is changed or moved, c is no longer valid.
436 The public methods are session-relative.
438 Most of this stuff is taken from this paper:
441 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
444 Zurich University of Arts
445 Institute for Computer Music and Sound Technology
447 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
451 /** compute this ramp's function constant from some tempo-pulse point
452 * @param end_npm end tempo (in note types per minute)
453 * @param end_pulse duration (pulses into global start) of some other position.
454 * @return the calculated function constant
457 TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const
459 if (note_types_per_minute() == end_npm || type() == Constant) {
463 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
464 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
467 /** compute the function constant from some tempo-time point.
468 * @param end_npm tempo (note types/min.)
469 * @param end_minute distance (in minutes) from session origin
470 * @return the calculated function constant
473 TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const
475 if (note_types_per_minute() == end_npm || type() == Constant) {
479 return c_func (end_npm, end_minute - minute());
482 /* position function */
484 TempoSection::a_func (double end_npm, double c) const
486 return log (end_npm / note_types_per_minute()) / c;
489 /*function constant*/
491 TempoSection::c_func (double end_npm, double end_time) const
493 return log (end_npm / note_types_per_minute()) / end_time;
496 /* tempo in note types per minute at time in minutes */
498 TempoSection::_tempo_at_time (const double& time) const
500 return exp (_c * time) * note_types_per_minute();
503 /* time in minutes at tempo in note types per minute */
505 TempoSection::_time_at_tempo (const double& npm) const
507 return log (npm / note_types_per_minute()) / _c;
510 /* pulse at tempo in note types per minute */
512 TempoSection::_pulse_at_tempo (const double& npm) const
514 return ((npm - note_types_per_minute()) / _c) / _note_type;
517 /* tempo in note types per minute at pulse */
519 TempoSection::_tempo_at_pulse (const double& pulse) const
521 return (pulse * _note_type * _c) + note_types_per_minute();
524 /* pulse at time in minutes */
526 TempoSection::_pulse_at_time (const double& time) const
528 return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
531 /* time in minutes at pulse */
533 TempoSection::_time_at_pulse (const double& pulse) const
535 return log1p ((_c * pulse * _note_type) / note_types_per_minute()) / _c;
538 /***********************************************************************/
540 const string MeterSection::xml_state_node_name = "Meter";
542 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
543 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
545 XMLProperty const * prop;
550 framepos_t frame = 0;
551 pair<double, BBT_Time> start;
553 if ((prop = node.property ("start")) != 0) {
554 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
558 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
560 /* legacy session - start used to be in bbt*/
561 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
566 if ((prop = node.property ("pulse")) != 0) {
567 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
568 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
573 if ((prop = node.property ("beat")) != 0) {
574 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
575 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
581 if ((prop = node.property ("bbt")) == 0) {
582 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
583 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
587 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
588 throw failed_constructor();
594 if ((prop = node.property ("frame")) != 0) {
595 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
596 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
597 throw failed_constructor();
599 set_minute (minute_at_frame (frame));
603 /* beats-per-bar is old; divisions-per-bar is new */
605 if ((prop = node.property ("divisions-per-bar")) == 0) {
606 if ((prop = node.property ("beats-per-bar")) == 0) {
607 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
608 throw failed_constructor();
611 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
612 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
613 throw failed_constructor();
616 if ((prop = node.property ("note-type")) == 0) {
617 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
618 throw failed_constructor();
620 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
621 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
622 throw failed_constructor();
625 if ((prop = node.property ("movable")) == 0) {
626 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
627 throw failed_constructor();
630 set_initial (!string_is_affirmative (prop->value()));
632 if ((prop = node.property ("lock-style")) == 0) {
633 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
635 set_position_lock_style (MusicTime);
637 set_position_lock_style (AudioTime);
640 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
645 MeterSection::get_state() const
647 XMLNode *root = new XMLNode (xml_state_node_name);
651 snprintf (buf, sizeof (buf), "%lf", pulse());
652 root->add_property ("pulse", buf);
653 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
657 root->add_property ("bbt", buf);
658 snprintf (buf, sizeof (buf), "%lf", beat());
659 root->add_property ("beat", buf);
660 snprintf (buf, sizeof (buf), "%lf", _note_type);
661 root->add_property ("note-type", buf);
662 snprintf (buf, sizeof (buf), "%li", frame());
663 root->add_property ("frame", buf);
664 root->add_property ("lock-style", enum_2_string (position_lock_style()));
665 snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
666 root->add_property ("divisions-per-bar", buf);
667 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
668 root->add_property ("movable", buf);
673 /***********************************************************************/
677 Tempo determines the rate of musical pulse determined by its components
678 note types per minute - the rate per minute of the whole note divisor _note_type
679 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
680 Meter divides the musical pulse into measures and beats according to its components
684 TempoSection - translates between time, musical pulse and tempo.
685 has a musical location in whole notes (pulses).
686 has a time location in minutes.
687 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
688 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
690 MeterSection - translates between BBT, meter-based beat and musical pulse.
691 has a musical location in whole notes (pulses)
692 has a musical location in meter-based beats
693 has a musical location in BBT time
694 has a time location expressed in minutes.
696 TempoSection and MeterSection may be locked to either audio or music (position lock style).
697 The lock style determines the location type to be kept as a reference when location is recalculated.
699 The first tempo and meter are special. they must move together, and are locked to audio.
700 Audio locked tempi which lie before the first meter are made inactive.
702 Recomputing the map is the process where the 'missing' location types are calculated.
703 We construct the tempo map by first using the locked location type of each section
704 to determine non-locked location types (pulse or minute position).
705 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
707 Having done this, we can now traverse the Metrics list by pulse or minute
708 to query its relevant meter/tempo.
710 It is important to keep the _metrics in an order that makes sense.
711 Because ramped MusicTime and AudioTime tempos can interact with each other,
712 reordering is frequent. Care must be taken to keep _metrics in a solved state.
713 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
717 Music and audio-locked objects may seem interchangeable on the surface, but when translating
718 between audio samples and beat, remember that a sample is only a quantised approximation
719 of the actual time (in minutes) of a beat.
720 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
721 mean that this frame is the actual location in time of 1|3|0.
723 You cannot use a frame measurement to determine beat distance except under special circumstances
724 (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).
726 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
727 sample space the user is operating at to be translated correctly to the object.
729 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
730 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
731 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
733 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
735 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
736 The result is rounded to audio frames.
737 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
740 frame_at_beat (beat_at_frame (frame)) == frame
742 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
744 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
745 frames_between_quarter_notes () eliminats this effect when determining time duration
746 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
748 The above pointless example could instead do:
749 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
751 The Shaggs - Things I Wonder
752 https://www.youtube.com/watch?v=9wQK6zMJOoQ
755 struct MetricSectionSorter {
756 bool operator() (const MetricSection* a, const MetricSection* b) {
757 return a->pulse() < b->pulse();
761 struct MetricSectionFrameSorter {
762 bool operator() (const MetricSection* a, const MetricSection* b) {
763 return a->frame() < b->frame();
767 TempoMap::TempoMap (framecnt_t fr)
770 BBT_Time start (1, 1, 0);
772 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo, AudioTime, fr);
773 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
775 t->set_initial (true);
776 t->set_locked_to_meter (true);
778 m->set_initial (true);
780 /* note: frame time is correct (zero) for both of these */
782 _metrics.push_back (t);
783 _metrics.push_back (m);
787 TempoMap::TempoMap (TempoMap const & other)
789 _frame_rate = other._frame_rate;
790 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
791 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
792 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
795 TempoSection* new_section = new TempoSection (*ts);
796 _metrics.push_back (new_section);
798 MeterSection* new_section = new MeterSection (*ms);
799 _metrics.push_back (new_section);
805 TempoMap::operator= (TempoMap const & other)
807 if (&other != this) {
808 _frame_rate = other._frame_rate;
810 Metrics::const_iterator d = _metrics.begin();
811 while (d != _metrics.end()) {
817 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
818 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
819 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
822 TempoSection* new_section = new TempoSection (*ts);
823 _metrics.push_back (new_section);
825 MeterSection* new_section = new MeterSection (*ms);
826 _metrics.push_back (new_section);
831 PropertyChanged (PropertyChange());
836 TempoMap::~TempoMap ()
838 Metrics::const_iterator d = _metrics.begin();
839 while (d != _metrics.end()) {
847 TempoMap::frame_at_minute (const double time) const
849 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
853 TempoMap::minute_at_frame (const framepos_t frame) const
855 return (frame / (double) _frame_rate) / 60.0;
859 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
861 bool removed = false;
864 Glib::Threads::RWLock::WriterLock lm (lock);
865 if ((removed = remove_tempo_locked (tempo))) {
866 if (complete_operation) {
867 recompute_map (_metrics);
872 if (removed && complete_operation) {
873 PropertyChanged (PropertyChange ());
878 TempoMap::remove_tempo_locked (const TempoSection& tempo)
882 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
883 if (dynamic_cast<TempoSection*> (*i) != 0) {
884 if (tempo.frame() == (*i)->frame()) {
885 if (!(*i)->initial()) {
898 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
900 bool removed = false;
903 Glib::Threads::RWLock::WriterLock lm (lock);
904 if ((removed = remove_meter_locked (tempo))) {
905 if (complete_operation) {
906 recompute_map (_metrics);
911 if (removed && complete_operation) {
912 PropertyChanged (PropertyChange ());
917 TempoMap::remove_meter_locked (const MeterSection& meter)
920 if (meter.position_lock_style() == AudioTime) {
921 /* remove meter-locked tempo */
922 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
924 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
925 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
934 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
935 if (dynamic_cast<MeterSection*> (*i) != 0) {
936 if (meter.frame() == (*i)->frame()) {
937 if (!(*i)->initial()) {
950 TempoMap::do_insert (MetricSection* section)
952 bool need_add = true;
953 /* we only allow new meters to be inserted on beat 1 of an existing
957 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
959 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
961 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
962 corrected.second.beats = 1;
963 corrected.second.ticks = 0;
964 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
965 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
966 m->bbt(), corrected.second) << endmsg;
967 //m->set_pulse (corrected);
971 /* Look for any existing MetricSection that is of the same type and
972 in the same bar as the new one, and remove it before adding
973 the new one. Note that this means that if we find a matching,
974 existing section, we can break out of the loop since we're
975 guaranteed that there is only one such match.
978 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
980 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
981 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
982 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
983 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
985 if (tempo && insert_tempo) {
988 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
989 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
991 if (tempo->initial()) {
993 /* can't (re)move this section, so overwrite
994 * its data content (but not its properties as
998 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
999 (*i)->set_position_lock_style (AudioTime);
1008 } else if (meter && insert_meter) {
1010 /* Meter Sections */
1012 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1014 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
1016 if (meter->initial()) {
1018 /* can't (re)move this section, so overwrite
1019 * its data content (but not its properties as
1023 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
1024 (*i)->set_position_lock_style (AudioTime);
1034 /* non-matching types, so we don't care */
1038 /* Add the given MetricSection, if we didn't just reset an existing
1043 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
1044 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
1045 Metrics::iterator i;
1048 TempoSection* prev_t = 0;
1050 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1051 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
1052 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1055 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
1059 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
1063 prev_t = dynamic_cast<TempoSection*> (*i);
1066 } else if (insert_tempo) {
1067 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1068 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1071 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1072 const bool lm = insert_tempo->locked_to_meter();
1073 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1074 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1081 _metrics.insert (i, section);
1085 /* user supplies the exact pulse if pls == MusicTime */
1087 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1089 if (tempo.note_types_per_minute() <= 0.0) {
1090 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1094 TempoSection* ts = 0;
1095 TempoSection* prev_tempo = 0;
1097 Glib::Threads::RWLock::WriterLock lm (lock);
1098 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true);
1099 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1101 if ((*i)->is_tempo()) {
1102 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1104 bool const ipm = ts->position_lock_style() == MusicTime;
1105 bool const lm = ts->locked_to_meter();
1106 if ((ipm && this_t->pulse() == ts->pulse()) || (!ipm && this_t->frame() == ts->frame())
1107 || (lm && this_t->pulse() == ts->pulse())) {
1108 if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1109 prev_tempo->set_end_note_types_per_minute (ts->note_types_per_minute());
1113 prev_tempo = this_t;
1116 recompute_map (_metrics);
1119 PropertyChanged (PropertyChange ());
1125 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1127 if (tempo.note_types_per_minute() <= 0.0) {
1128 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1132 bool const locked_to_meter = ts.locked_to_meter();
1133 bool const ts_clamped = ts.clamped();
1134 TempoSection* new_ts = 0;
1137 Glib::Threads::RWLock::WriterLock lm (lock);
1138 TempoSection& first (first_tempo());
1139 if (!ts.initial()) {
1140 if (locked_to_meter) {
1142 /* cannot move a meter-locked tempo section */
1143 *static_cast<Tempo*>(&ts) = tempo;
1144 recompute_map (_metrics);
1147 remove_tempo_locked (ts);
1148 new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter);
1149 new_ts->set_clamped (ts_clamped);
1151 if (new_ts && new_ts->type() == TempoSection::Constant) {
1152 new_ts->set_end_note_types_per_minute (new_ts->note_types_per_minute());
1154 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1156 if ((*i)->is_tempo()) {
1157 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1159 bool const ipm = new_ts->position_lock_style() == MusicTime;
1160 bool const lm = new_ts->locked_to_meter();
1161 if ((ipm && this_t->pulse() > new_ts->pulse()) || (!ipm && this_t->frame() > new_ts->frame())
1162 || (lm && this_t->pulse() > new_ts->pulse())) {
1163 new_ts->set_end_note_types_per_minute (tempo.end_note_types_per_minute());
1173 first.set_pulse (0.0);
1174 first.set_minute (minute_at_frame (frame));
1175 first.set_position_lock_style (AudioTime);
1176 first.set_locked_to_meter (true);
1177 first.set_clamped (ts_clamped);
1179 /* cannot move the first tempo section */
1180 *static_cast<Tempo*>(&first) = tempo;
1183 recompute_map (_metrics);
1186 PropertyChanged (PropertyChange ());
1190 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1191 , PositionLockStyle pls, bool recompute, bool locked_to_meter)
1193 TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _frame_rate);
1194 t->set_locked_to_meter (locked_to_meter);
1199 if (pls == AudioTime) {
1200 solve_map_minute (_metrics, t, t->minute());
1202 solve_map_pulse (_metrics, t, t->pulse());
1204 recompute_meters (_metrics);
1211 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1213 MeterSection* m = 0;
1215 Glib::Threads::RWLock::WriterLock lm (lock);
1216 m = add_meter_locked (meter, beat, where, frame, pls, true);
1221 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1226 PropertyChanged (PropertyChange ());
1231 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1234 Glib::Threads::RWLock::WriterLock lm (lock);
1235 const double beat = beat_at_bbt_locked (_metrics, where);
1237 if (!ms.initial()) {
1238 remove_meter_locked (ms);
1239 add_meter_locked (meter, beat, where, frame, pls, true);
1241 MeterSection& first (first_meter());
1242 TempoSection& first_t (first_tempo());
1243 /* cannot move the first meter section */
1244 *static_cast<Meter*>(&first) = meter;
1245 first.set_position_lock_style (AudioTime);
1246 first.set_pulse (0.0);
1247 first.set_minute (minute_at_frame (frame));
1248 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1249 first.set_beat (beat);
1250 first_t.set_minute (first.minute());
1251 first_t.set_locked_to_meter (true);
1252 first_t.set_pulse (0.0);
1253 first_t.set_position_lock_style (AudioTime);
1254 recompute_map (_metrics);
1258 PropertyChanged (PropertyChange ());
1262 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1264 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1265 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1266 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1267 TempoSection* mlt = 0;
1269 if (pls == AudioTime) {
1270 /* add meter-locked tempo */
1271 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), AudioTime, true, true);
1279 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1281 bool solved = false;
1283 do_insert (new_meter);
1287 if (pls == AudioTime) {
1288 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1289 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1290 fudge frame so that the meter ends up at its BBT position instead.
1293 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1296 solved = solve_map_bbt (_metrics, new_meter, where);
1297 /* required due to resetting the pulse of meter-locked tempi above.
1298 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1299 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1301 recompute_map (_metrics);
1305 if (!solved && recompute) {
1306 /* if this has failed to solve, there is little we can do other than to ensure that
1307 the new map is recalculated.
1309 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1310 recompute_map (_metrics);
1317 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1319 Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1322 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1323 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1328 Glib::Threads::RWLock::WriterLock lm (lock);
1329 *((Tempo*) t) = newtempo;
1330 recompute_map (_metrics);
1332 PropertyChanged (PropertyChange ());
1339 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1341 Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1344 TempoSection* first;
1345 Metrics::iterator i;
1347 /* find the TempoSection immediately preceding "where"
1350 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1352 if ((*i)->frame() > where) {
1358 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1371 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1381 Glib::Threads::RWLock::WriterLock lm (lock);
1382 /* cannot move the first tempo section */
1383 *((Tempo*)prev) = newtempo;
1384 recompute_map (_metrics);
1387 PropertyChanged (PropertyChange ());
1391 TempoMap::first_meter () const
1393 const MeterSection *m = 0;
1395 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1396 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1401 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1402 abort(); /*NOTREACHED*/
1407 TempoMap::first_meter ()
1409 MeterSection *m = 0;
1411 /* CALLER MUST HOLD LOCK */
1413 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1414 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1419 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1420 abort(); /*NOTREACHED*/
1425 TempoMap::first_tempo () const
1427 const TempoSection *t = 0;
1429 /* CALLER MUST HOLD LOCK */
1431 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1432 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1442 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1443 abort(); /*NOTREACHED*/
1448 TempoMap::first_tempo ()
1450 TempoSection *t = 0;
1452 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1453 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1463 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1464 abort(); /*NOTREACHED*/
1468 TempoMap::recompute_tempi (Metrics& metrics)
1470 TempoSection* prev_t = 0;
1472 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1475 if ((*i)->is_tempo()) {
1476 t = static_cast<TempoSection*> (*i);
1488 if (t->position_lock_style() == AudioTime) {
1489 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1490 if (!t->locked_to_meter()) {
1491 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1495 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1496 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1504 prev_t->set_c (0.0);
1507 /* tempos must be positioned correctly.
1508 the current approach is to use a meter's bbt time as its base position unit.
1509 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1510 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1513 TempoMap::recompute_meters (Metrics& metrics)
1515 MeterSection* meter = 0;
1516 MeterSection* prev_m = 0;
1518 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1519 if (!(*mi)->is_tempo()) {
1520 meter = static_cast<MeterSection*> (*mi);
1521 if (meter->position_lock_style() == AudioTime) {
1523 pair<double, BBT_Time> b_bbt;
1524 TempoSection* meter_locked_tempo = 0;
1525 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1527 if ((*ii)->is_tempo()) {
1528 t = static_cast<TempoSection*> (*ii);
1529 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1530 meter_locked_tempo = t;
1537 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1538 if (beats + prev_m->beat() != meter->beat()) {
1539 /* reordering caused a bbt change */
1541 beats = meter->beat() - prev_m->beat();
1542 b_bbt = make_pair (beats + prev_m->beat()
1543 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1544 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1546 } else if (!meter->initial()) {
1547 b_bbt = make_pair (meter->beat(), meter->bbt());
1548 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1551 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1553 if (meter_locked_tempo) {
1554 meter_locked_tempo->set_pulse (pulse);
1556 meter->set_beat (b_bbt);
1557 meter->set_pulse (pulse);
1562 pair<double, BBT_Time> b_bbt;
1564 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1565 if (beats + prev_m->beat() != meter->beat()) {
1566 /* reordering caused a bbt change */
1567 b_bbt = make_pair (beats + prev_m->beat()
1568 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1570 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1572 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1574 /* shouldn't happen - the first is audio-locked */
1575 pulse = pulse_at_beat_locked (metrics, meter->beat());
1576 b_bbt = make_pair (meter->beat(), meter->bbt());
1579 meter->set_beat (b_bbt);
1580 meter->set_pulse (pulse);
1581 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1590 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1592 /* CALLER MUST HOLD WRITE LOCK */
1594 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1597 /* silly call from Session::process() during startup
1602 recompute_tempi (metrics);
1603 recompute_meters (metrics);
1607 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1609 Glib::Threads::RWLock::ReaderLock lm (lock);
1610 TempoMetric m (first_meter(), first_tempo());
1612 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1613 at something, because we insert the default tempo and meter during
1614 TempoMap construction.
1616 now see if we can find better candidates.
1619 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1621 if ((*i)->frame() > frame) {
1635 /* XX meters only */
1637 TempoMap::metric_at (BBT_Time bbt) const
1639 Glib::Threads::RWLock::ReaderLock lm (lock);
1640 TempoMetric m (first_meter(), first_tempo());
1642 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1643 at something, because we insert the default tempo and meter during
1644 TempoMap construction.
1646 now see if we can find better candidates.
1649 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1651 if (!(*i)->is_tempo()) {
1652 mw = static_cast<MeterSection*> (*i);
1653 BBT_Time section_start (mw->bbt());
1655 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1666 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1667 * @param frame The session frame position.
1668 * @return The beat duration according to the tempo map at the supplied frame.
1670 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1671 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1673 * This function uses both tempo and meter.
1676 TempoMap::beat_at_frame (const framecnt_t& frame) const
1678 Glib::Threads::RWLock::ReaderLock lm (lock);
1680 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1683 /* This function uses both tempo and meter.*/
1685 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1687 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1688 MeterSection* prev_m = 0;
1689 MeterSection* next_m = 0;
1691 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1692 if (!(*i)->is_tempo()) {
1693 if (prev_m && (*i)->minute() > minute) {
1694 next_m = static_cast<MeterSection*> (*i);
1697 prev_m = static_cast<MeterSection*> (*i);
1701 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1703 /* audio locked meters fake their beat */
1704 if (next_m && next_m->beat() < beat) {
1705 return next_m->beat();
1711 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1712 * @param beat The BBT (meter-based) beat.
1713 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1715 * This function uses both tempo and meter.
1718 TempoMap::frame_at_beat (const double& beat) const
1720 Glib::Threads::RWLock::ReaderLock lm (lock);
1722 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1725 /* meter & tempo section based */
1727 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1729 MeterSection* prev_m = 0;
1730 TempoSection* prev_t = 0;
1734 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1735 if (!(*i)->is_tempo()) {
1736 m = static_cast<MeterSection*> (*i);
1737 if (prev_m && m->beat() > beat) {
1747 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1748 if ((*i)->is_tempo()) {
1749 t = static_cast<TempoSection*> (*i);
1755 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1764 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1767 /** Returns a Tempo corresponding to the supplied frame position.
1768 * @param frame The audio frame.
1769 * @return a Tempo according to the tempo map at the supplied frame.
1773 TempoMap::tempo_at_frame (const framepos_t& frame) const
1775 Glib::Threads::RWLock::ReaderLock lm (lock);
1777 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1781 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1783 TempoSection* prev_t = 0;
1787 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1788 if ((*i)->is_tempo()) {
1789 t = static_cast<TempoSection*> (*i);
1793 if ((prev_t) && t->minute() > minute) {
1794 /* t is the section past frame */
1795 return prev_t->tempo_at_minute (minute);
1801 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1804 /** returns the frame at which the supplied tempo occurs, or
1805 * the frame of the last tempo section (search exhausted)
1806 * only the position of the first occurence will be returned
1810 TempoMap::frame_at_tempo (const Tempo& tempo) const
1812 Glib::Threads::RWLock::ReaderLock lm (lock);
1814 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1818 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1820 TempoSection* prev_t = 0;
1821 const double tempo_bpm = tempo.note_types_per_minute();
1823 Metrics::const_iterator i;
1825 for (i = metrics.begin(); i != metrics.end(); ++i) {
1827 if ((*i)->is_tempo()) {
1828 t = static_cast<TempoSection*> (*i);
1836 if (t->note_types_per_minute() == tempo_bpm) {
1841 const double prev_t_bpm = prev_t->note_types_per_minute();
1842 const double prev_t_end_bpm = prev_t->end_note_types_per_minute();
1843 if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm)
1844 || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm)
1845 || (prev_t_end_bpm == tempo_bpm)) {
1847 return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
1854 return prev_t->minute();
1858 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1860 TempoSection* prev_t = 0;
1864 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1865 if ((*i)->is_tempo()) {
1866 t = static_cast<TempoSection*> (*i);
1870 if ((prev_t) && t->pulse() > pulse) {
1871 /* t is the section past frame */
1872 return prev_t->tempo_at_pulse (pulse);
1878 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1882 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1884 TempoSection* prev_t = 0;
1885 const double tempo_bpm = tempo.note_types_per_minute();
1887 Metrics::const_iterator i;
1889 for (i = metrics.begin(); i != metrics.end(); ++i) {
1891 if ((*i)->is_tempo()) {
1892 t = static_cast<TempoSection*> (*i);
1898 const double t_bpm = t->note_types_per_minute();
1900 if (t_bpm == tempo_bpm) {
1905 const double prev_t_bpm = prev_t->note_types_per_minute();
1907 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1908 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1915 return prev_t->pulse();
1918 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1919 * @param qn the position in quarter note beats.
1920 * @return the Tempo at the supplied quarter-note.
1923 TempoMap::tempo_at_quarter_note (const double& qn) const
1925 Glib::Threads::RWLock::ReaderLock lm (lock);
1927 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1930 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1931 * @param tempo the tempo.
1932 * @return the position in quarter-note beats where the map bpm
1933 * is equal to that of the Tempo. currently ignores note_type.
1936 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1938 Glib::Threads::RWLock::ReaderLock lm (lock);
1940 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1943 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1944 * @param metrics the list of metric sections used to calculate the pulse.
1945 * @param beat The BBT (meter-based) beat.
1946 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1948 * a pulse or whole note is the base musical position of a MetricSection.
1949 * it is equivalent to four quarter notes.
1953 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1955 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1957 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1960 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1961 * @param metrics the list of metric sections used to calculate the beat.
1962 * @param pulse the whole-note pulse.
1963 * @return the meter-based beat at the supplied whole-note pulse.
1965 * a pulse or whole note is the base musical position of a MetricSection.
1966 * it is equivalent to four quarter notes.
1969 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1971 MeterSection* prev_m = 0;
1973 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1975 if (!(*i)->is_tempo()) {
1976 m = static_cast<MeterSection*> (*i);
1977 if (prev_m && m->pulse() > pulse) {
1985 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1989 /* tempo section based */
1991 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1993 /* HOLD (at least) THE READER LOCK */
1994 TempoSection* prev_t = 0;
1996 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1998 if ((*i)->is_tempo()) {
1999 t = static_cast<TempoSection*> (*i);
2003 if (prev_t && t->minute() > minute) {
2004 /*the previous ts is the one containing the frame */
2005 const double ret = prev_t->pulse_at_minute (minute);
2006 /* audio locked section in new meter*/
2007 if (t->pulse() < ret) {
2016 /* treated as constant for this ts */
2017 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
2019 return pulses_in_section + prev_t->pulse();
2022 /* tempo section based */
2024 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2026 /* HOLD THE READER LOCK */
2028 const TempoSection* prev_t = 0;
2030 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2033 if ((*i)->is_tempo()) {
2034 t = static_cast<TempoSection*> (*i);
2038 if (prev_t && t->pulse() > pulse) {
2039 return prev_t->minute_at_pulse (pulse);
2045 /* must be treated as constant, irrespective of _type */
2046 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
2048 return dtime + prev_t->minute();
2051 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
2052 * @param bbt The BBT time (meter-based).
2053 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
2057 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
2059 Glib::Threads::RWLock::ReaderLock lm (lock);
2060 return beat_at_bbt_locked (_metrics, bbt);
2065 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2067 /* CALLER HOLDS READ LOCK */
2069 MeterSection* prev_m = 0;
2071 /* because audio-locked meters have 'fake' integral beats,
2072 there is no pulse offset here.
2076 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2077 if (!(*i)->is_tempo()) {
2078 m = static_cast<MeterSection*> (*i);
2080 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2081 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2089 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2090 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2091 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2096 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2097 * @param beat The BBT (meter-based) beat.
2098 * @return The BBT time (meter-based) at the supplied meter-based beat.
2102 TempoMap::bbt_at_beat (const double& beat)
2104 Glib::Threads::RWLock::ReaderLock lm (lock);
2105 return bbt_at_beat_locked (_metrics, beat);
2109 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2111 /* CALLER HOLDS READ LOCK */
2112 MeterSection* prev_m = 0;
2113 const double beats = max (0.0, b);
2115 MeterSection* m = 0;
2117 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2118 if (!(*i)->is_tempo()) {
2119 m = static_cast<MeterSection*> (*i);
2121 if (m->beat() > beats) {
2122 /* this is the meter after the one our beat is on*/
2132 const double beats_in_ms = beats - prev_m->beat();
2133 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2134 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2135 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2136 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2140 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2141 ret.beats = (uint32_t) floor (remaining_beats);
2142 ret.bars = total_bars;
2144 /* 0 0 0 to 1 1 0 - based mapping*/
2148 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2150 ret.ticks -= BBT_Time::ticks_per_beat;
2153 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2161 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2162 * @param bbt The BBT time (meter-based).
2163 * @return the quarter note beat at the supplied BBT time
2165 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2167 * while the input uses meter, the output does not.
2170 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2172 Glib::Threads::RWLock::ReaderLock lm (lock);
2174 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2178 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2180 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2183 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2186 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2190 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2192 /* CALLER HOLDS READ LOCK */
2194 MeterSection* prev_m = 0;
2196 /* because audio-locked meters have 'fake' integral beats,
2197 there is no pulse offset here.
2201 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2202 if (!(*i)->is_tempo()) {
2203 m = static_cast<MeterSection*> (*i);
2205 if (m->bbt().bars > bbt.bars) {
2213 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2214 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2215 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2220 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2221 * @param qn the quarter-note beat.
2222 * @return The BBT time (meter-based) at the supplied meter-based beat.
2224 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2228 TempoMap::bbt_at_quarter_note (const double& qn)
2230 Glib::Threads::RWLock::ReaderLock lm (lock);
2232 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2235 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2236 * @param metrics The list of metric sections used to determine the result.
2237 * @param pulse The whole-note pulse.
2238 * @return The BBT time at the supplied whole-note pulse.
2240 * a pulse or whole note is the basic musical position of a MetricSection.
2241 * it is equivalent to four quarter notes.
2242 * while the output uses meter, the input does not.
2245 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2247 MeterSection* prev_m = 0;
2249 MeterSection* m = 0;
2251 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2253 if (!(*i)->is_tempo()) {
2254 m = static_cast<MeterSection*> (*i);
2257 double const pulses_to_m = m->pulse() - prev_m->pulse();
2258 if (prev_m->pulse() + pulses_to_m > pulse) {
2259 /* this is the meter after the one our beat is on*/
2270 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2271 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2272 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2273 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2274 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2278 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2279 ret.beats = (uint32_t) floor (remaining_beats);
2280 ret.bars = total_bars;
2282 /* 0 0 0 to 1 1 0 mapping*/
2286 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2288 ret.ticks -= BBT_Time::ticks_per_beat;
2291 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2299 /** Returns the BBT time corresponding to the supplied frame position.
2300 * @param frame the position in audio samples.
2301 * @return the BBT time at the frame position .
2305 TempoMap::bbt_at_frame (framepos_t frame)
2313 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2318 const double minute = minute_at_frame (frame);
2320 Glib::Threads::RWLock::ReaderLock lm (lock);
2322 return bbt_at_minute_locked (_metrics, minute);
2326 TempoMap::bbt_at_frame_rt (framepos_t frame)
2328 const double minute = minute_at_frame (frame);
2330 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2333 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2336 return bbt_at_minute_locked (_metrics, minute);
2340 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2350 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2351 MeterSection* prev_m = 0;
2352 MeterSection* next_m = 0;
2356 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2357 if (!(*i)->is_tempo()) {
2358 m = static_cast<MeterSection*> (*i);
2359 if (prev_m && m->minute() > minute) {
2367 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2369 /* handle frame before first meter */
2370 if (minute < prev_m->minute()) {
2373 /* audio locked meters fake their beat */
2374 if (next_m && next_m->beat() < beat) {
2375 beat = next_m->beat();
2378 beat = max (0.0, beat);
2380 const double beats_in_ms = beat - prev_m->beat();
2381 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2382 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2383 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2384 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2388 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2389 ret.beats = (uint32_t) floor (remaining_beats);
2390 ret.bars = total_bars;
2392 /* 0 0 0 to 1 1 0 - based mapping*/
2396 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2398 ret.ticks -= BBT_Time::ticks_per_beat;
2401 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2409 /** Returns the frame position corresponding to the supplied BBT time.
2410 * @param bbt the position in BBT time.
2411 * @return the frame position at bbt.
2415 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2419 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2424 if (bbt.beats < 1) {
2425 throw std::logic_error ("beats are counted from one");
2430 Glib::Threads::RWLock::ReaderLock lm (lock);
2431 minute = minute_at_bbt_locked (_metrics, bbt);
2434 return frame_at_minute (minute);
2437 /* meter & tempo section based */
2439 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2441 /* HOLD THE READER LOCK */
2443 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2448 * Returns the quarter-note beat position corresponding to the supplied frame.
2450 * @param frame the position in frames.
2451 * @return The quarter-note position of the supplied frame. Ignores meter.
2455 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2457 const double minute = minute_at_frame (frame);
2459 Glib::Threads::RWLock::ReaderLock lm (lock);
2461 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2465 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2467 const double minute = minute_at_frame (frame);
2469 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2472 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2475 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2479 * Returns the frame position corresponding to the supplied quarter-note beat.
2481 * @param quarter_note the quarter-note position.
2482 * @return the frame position of the supplied quarter-note. Ignores meter.
2487 TempoMap::frame_at_quarter_note (const double quarter_note) const
2491 Glib::Threads::RWLock::ReaderLock lm (lock);
2493 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2496 return frame_at_minute (minute);
2499 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2500 * @param beat The BBT (meter-based) beat.
2501 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2503 * a quarter-note may be compared with and assigned to Evoral::Beats.
2507 TempoMap::quarter_note_at_beat (const double beat) const
2509 Glib::Threads::RWLock::ReaderLock lm (lock);
2511 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2514 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2515 * @param quarter_note The position in quarter-note beats.
2516 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2518 * a quarter-note is the musical unit of Evoral::Beats.
2522 TempoMap::beat_at_quarter_note (const double quarter_note) const
2524 Glib::Threads::RWLock::ReaderLock lm (lock);
2526 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2529 /** Returns the duration in frames between two supplied quarter-note beat positions.
2530 * @param start the first position in quarter-note beats.
2531 * @param end the end position in quarter-note beats.
2532 * @return the frame distance ober the quarter-note beats duration.
2534 * use this rather than e.g.
2535 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2536 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2540 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2545 Glib::Threads::RWLock::ReaderLock lm (lock);
2546 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2549 return frame_at_minute (minutes);
2553 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2556 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2560 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2562 Glib::Threads::RWLock::ReaderLock lm (lock);
2564 return quarter_notes_between_frames_locked (_metrics, start, end);
2568 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2570 const TempoSection* prev_t = 0;
2572 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2575 if ((*i)->is_tempo()) {
2576 t = static_cast<TempoSection*> (*i);
2580 if (prev_t && t->frame() > start) {
2587 const double start_qn = prev_t->pulse_at_frame (start);
2589 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2592 if ((*i)->is_tempo()) {
2593 t = static_cast<TempoSection*> (*i);
2597 if (prev_t && t->frame() > end) {
2603 const double end_qn = prev_t->pulse_at_frame (end);
2605 return (end_qn - start_qn) * 4.0;
2609 TempoMap::check_solved (const Metrics& metrics) const
2611 TempoSection* prev_t = 0;
2612 MeterSection* prev_m = 0;
2614 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2617 if ((*i)->is_tempo()) {
2618 t = static_cast<TempoSection*> (*i);
2623 /* check ordering */
2624 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2628 /* precision check ensures tempo and frames align.*/
2629 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2630 if (!t->locked_to_meter()) {
2635 /* gradient limit - who knows what it should be?
2636 things are also ok (if a little chaotic) without this
2638 if (fabs (prev_t->c()) > 1000.0) {
2639 //std::cout << "c : " << prev_t->c() << std::endl;
2646 if (!(*i)->is_tempo()) {
2647 m = static_cast<MeterSection*> (*i);
2648 if (prev_m && m->position_lock_style() == AudioTime) {
2649 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2650 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2651 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2653 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2667 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2669 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2671 if ((*i)->is_tempo()) {
2672 t = static_cast<TempoSection*> (*i);
2673 if (t->locked_to_meter()) {
2674 t->set_active (true);
2675 } else if (t->position_lock_style() == AudioTime) {
2676 if (t->frame() < frame) {
2677 t->set_active (false);
2678 t->set_pulse (-1.0);
2679 } else if (t->frame() > frame) {
2680 t->set_active (true);
2681 } else if (t->frame() == frame) {
2691 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2693 TempoSection* prev_t = 0;
2694 TempoSection* section_prev = 0;
2695 double first_m_minute = 0.0;
2696 const bool sml = section->locked_to_meter();
2698 /* can't move a tempo before the first meter */
2699 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2701 if (!(*i)->is_tempo()) {
2702 m = static_cast<MeterSection*> (*i);
2704 first_m_minute = m->minute();
2709 if (!section->initial() && minute <= first_m_minute) {
2713 section->set_active (true);
2714 section->set_minute (minute);
2716 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2718 if ((*i)->is_tempo()) {
2719 t = static_cast<TempoSection*> (*i);
2731 if (t->frame() == frame_at_minute (minute)) {
2735 const bool tlm = t->position_lock_style() == MusicTime;
2737 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2738 section_prev = prev_t;
2740 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2741 if (!section->locked_to_meter()) {
2742 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2747 if (t->position_lock_style() == MusicTime) {
2748 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2749 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2751 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2752 if (!t->locked_to_meter()) {
2753 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2762 recompute_tempi (imaginary);
2764 if (check_solved (imaginary)) {
2767 dunp (imaginary, std::cout);
2771 MetricSectionFrameSorter fcmp;
2772 imaginary.sort (fcmp);
2774 recompute_tempi (imaginary);
2776 if (check_solved (imaginary)) {
2784 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2786 TempoSection* prev_t = 0;
2787 TempoSection* section_prev = 0;
2789 section->set_pulse (pulse);
2791 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2793 if ((*i)->is_tempo()) {
2794 t = static_cast<TempoSection*> (*i);
2805 section_prev = prev_t;
2809 if (t->position_lock_style() == MusicTime) {
2810 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2811 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2813 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2814 if (!t->locked_to_meter()) {
2815 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2824 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2825 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2829 recompute_tempi (imaginary);
2831 if (check_solved (imaginary)) {
2834 dunp (imaginary, std::cout);
2838 MetricSectionSorter cmp;
2839 imaginary.sort (cmp);
2841 recompute_tempi (imaginary);
2843 * XX need a restriction here, but only for this case,
2844 * as audio locked tempos don't interact in the same way.
2846 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2848 * |50 bpm |250 bpm |60 bpm
2849 * drag 250 to the pulse after 60->
2850 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2852 if (check_solved (imaginary)) {
2860 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2862 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2863 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2864 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2868 if (section->initial()) {
2869 /* lock the first tempo to our first meter */
2870 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2875 TempoSection* meter_locked_tempo = 0;
2877 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2879 if ((*ii)->is_tempo()) {
2880 t = static_cast<TempoSection*> (*ii);
2881 if (t->locked_to_meter() && t->frame() == section->frame()) {
2882 meter_locked_tempo = t;
2888 if (!meter_locked_tempo) {
2892 MeterSection* prev_m = 0;
2894 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2895 bool solved = false;
2897 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2899 if (!(*i)->is_tempo()) {
2900 m = static_cast<MeterSection*> (*i);
2902 if (prev_m && !section->initial()) {
2903 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2904 if (beats + prev_m->beat() < section->beat()) {
2905 /* set the section pulse according to its musical position,
2906 * as an earlier time than this has been requested.
2908 const double new_pulse = ((section->beat() - prev_m->beat())
2909 / prev_m->note_divisor()) + prev_m->pulse();
2911 tempo_copy->set_position_lock_style (MusicTime);
2912 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2913 meter_locked_tempo->set_position_lock_style (MusicTime);
2914 section->set_position_lock_style (MusicTime);
2915 section->set_pulse (new_pulse);
2916 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2917 meter_locked_tempo->set_position_lock_style (AudioTime);
2918 section->set_position_lock_style (AudioTime);
2919 section->set_minute (meter_locked_tempo->minute());
2925 Metrics::const_iterator d = future_map.begin();
2926 while (d != future_map.end()) {
2935 /* all is ok. set section's locked tempo if allowed.
2936 possibly disallow if there is an adjacent audio-locked tempo.
2937 XX this check could possibly go. its never actually happened here.
2939 MeterSection* meter_copy = const_cast<MeterSection*>
2940 (&meter_section_at_minute_locked (future_map, section->minute()));
2942 meter_copy->set_minute (minute);
2944 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2945 section->set_minute (minute);
2946 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2947 / prev_m->note_divisor()) + prev_m->pulse());
2948 solve_map_minute (imaginary, meter_locked_tempo, minute);
2953 Metrics::const_iterator d = future_map.begin();
2954 while (d != future_map.end()) {
2964 /* initial (first meter atm) */
2966 tempo_copy->set_minute (minute);
2967 tempo_copy->set_pulse (0.0);
2969 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2970 section->set_minute (minute);
2971 meter_locked_tempo->set_minute (minute);
2972 meter_locked_tempo->set_pulse (0.0);
2973 solve_map_minute (imaginary, meter_locked_tempo, minute);
2978 Metrics::const_iterator d = future_map.begin();
2979 while (d != future_map.end()) {
2988 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2989 section->set_beat (b_bbt);
2990 section->set_pulse (0.0);
3000 MetricSectionFrameSorter fcmp;
3001 imaginary.sort (fcmp);
3003 recompute_meters (imaginary);
3009 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
3011 /* disallow setting section to an existing meter's bbt */
3012 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
3014 if (!(*i)->is_tempo()) {
3015 m = static_cast<MeterSection*> (*i);
3016 if (m != section && m->bbt().bars == when.bars) {
3022 MeterSection* prev_m = 0;
3023 MeterSection* section_prev = 0;
3025 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
3027 if (!(*i)->is_tempo()) {
3028 m = static_cast<MeterSection*> (*i);
3034 pair<double, BBT_Time> b_bbt;
3035 double new_pulse = 0.0;
3037 if (prev_m && m->bbt().bars > when.bars && !section_prev){
3038 section_prev = prev_m;
3040 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
3041 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
3042 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
3044 section->set_beat (b_bbt);
3045 section->set_pulse (pulse);
3046 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3050 if (m->position_lock_style() == AudioTime) {
3051 TempoSection* meter_locked_tempo = 0;
3053 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
3055 if ((*ii)->is_tempo()) {
3056 t = static_cast<TempoSection*> (*ii);
3057 if (t->locked_to_meter() && t->frame() == m->frame()) {
3058 meter_locked_tempo = t;
3064 if (!meter_locked_tempo) {
3069 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3071 if (beats + prev_m->beat() != m->beat()) {
3072 /* tempo/ meter change caused a change in beat (bar). */
3074 /* the user has requested that the previous section of music overlaps this one.
3075 we have no choice but to change the bar number here, as being locked to audio means
3076 we must stay where we are on the timeline.
3078 beats = m->beat() - prev_m->beat();
3079 b_bbt = make_pair (beats + prev_m->beat()
3080 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3081 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3083 } else if (!m->initial()) {
3084 b_bbt = make_pair (m->beat(), m->bbt());
3085 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3088 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3091 meter_locked_tempo->set_pulse (new_pulse);
3092 m->set_beat (b_bbt);
3093 m->set_pulse (new_pulse);
3097 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3098 if (beats + prev_m->beat() != m->beat()) {
3099 /* tempo/ meter change caused a change in beat (bar). */
3100 b_bbt = make_pair (beats + prev_m->beat()
3101 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3103 b_bbt = make_pair (beats + prev_m->beat()
3106 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3107 m->set_beat (b_bbt);
3108 m->set_pulse (new_pulse);
3109 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3116 if (!section_prev) {
3118 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3119 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3120 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3122 section->set_beat (b_bbt);
3123 section->set_pulse (pulse);
3124 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3127 MetricSectionSorter cmp;
3128 imaginary.sort (cmp);
3130 recompute_meters (imaginary);
3135 /** places a copy of _metrics into copy and returns a pointer
3136 * to section's equivalent in copy.
3139 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
3141 TempoSection* ret = 0;
3143 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3146 if ((*i)->is_tempo()) {
3147 t = static_cast<TempoSection*> (*i);
3149 ret = new TempoSection (*t);
3150 copy.push_back (ret);
3154 TempoSection* cp = new TempoSection (*t);
3155 copy.push_back (cp);
3157 if (!(*i)->is_tempo()) {
3158 m = static_cast<MeterSection *> (*i);
3159 MeterSection* cp = new MeterSection (*m);
3160 copy.push_back (cp);
3168 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3170 MeterSection* ret = 0;
3172 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3175 if ((*i)->is_tempo()) {
3176 t = static_cast<TempoSection*> (*i);
3177 TempoSection* cp = new TempoSection (*t);
3178 copy.push_back (cp);
3181 if (!(*i)->is_tempo()) {
3182 m = static_cast<MeterSection *> (*i);
3184 ret = new MeterSection (*m);
3185 copy.push_back (ret);
3188 MeterSection* cp = new MeterSection (*m);
3189 copy.push_back (cp);
3196 /** answers the question "is this a valid beat position for this tempo section?".
3197 * it returns true if the tempo section can be moved to the requested bbt position,
3198 * leaving the tempo map in a solved state.
3199 * @param ts the tempo section to be moved
3200 * @param bbt the requested new position for the tempo section
3201 * @return true if the tempo section can be moved to the position, otherwise false.
3204 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3207 TempoSection* tempo_copy = 0;
3210 Glib::Threads::RWLock::ReaderLock lm (lock);
3211 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3217 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3219 Metrics::const_iterator d = copy.begin();
3220 while (d != copy.end()) {
3229 * 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,
3230 * taking any possible reordering as a consequence of this into account.
3231 * @param section - the section to be altered
3232 * @param bbt - the BBT time where the altered tempo will fall
3233 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3235 pair<double, framepos_t>
3236 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3239 pair<double, framepos_t> ret = make_pair (0.0, 0);
3241 Glib::Threads::RWLock::ReaderLock lm (lock);
3243 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3245 const double beat = beat_at_bbt_locked (future_map, bbt);
3247 if (section->position_lock_style() == AudioTime) {
3248 tempo_copy->set_position_lock_style (MusicTime);
3251 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3252 ret.first = tempo_copy->pulse();
3253 ret.second = tempo_copy->frame();
3255 ret.first = section->pulse();
3256 ret.second = section->frame();
3259 Metrics::const_iterator d = future_map.begin();
3260 while (d != future_map.end()) {
3267 /** moves a TempoSection to a specified position.
3268 * @param ts - the section to be moved
3269 * @param frame - the new position in frames for the tempo
3270 * @param sub_num - the snap division to use if using musical time.
3272 * if sub_num is non-zero, the frame position is used to calculate an exact
3275 * -1 | snap to bars (meter-based)
3276 * 0 | no snap - use audio frame for musical position
3277 * 1 | snap to meter-based (BBT) beat
3278 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3280 * this follows the snap convention in the gui.
3281 * if sub_num is zero, the musical position will be taken from the supplied frame.
3284 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3288 if (ts->position_lock_style() == MusicTime) {
3290 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3291 Glib::Threads::RWLock::WriterLock lm (lock);
3292 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3294 tempo_copy->set_position_lock_style (AudioTime);
3296 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3297 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3298 const double pulse = pulse_at_beat_locked (future_map, beat);
3300 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3301 solve_map_pulse (_metrics, ts, pulse);
3302 recompute_meters (_metrics);
3310 Glib::Threads::RWLock::WriterLock lm (lock);
3311 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3313 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3315 /* We're moving the object that defines the grid while snapping to it...
3316 * Placing the ts at the beat corresponding to the requested frame may shift the
3317 * grid in such a way that the mouse is left hovering over a completerly different division,
3318 * causing jittering when the mouse next moves (esp. large tempo deltas).
3320 * This alters the snap behaviour slightly in that we snap to beat divisions
3321 * in the future map rather than the existing one.
3323 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3324 const framepos_t snapped_frame = frame_at_minute (minute_at_pulse_locked (future_map, qn / 4.0));
3326 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3327 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3328 ts->set_pulse (qn / 4.0);
3329 recompute_meters (_metrics);
3332 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3333 recompute_meters (_metrics);
3339 Metrics::const_iterator d = future_map.begin();
3340 while (d != future_map.end()) {
3345 MetricPositionChanged (PropertyChange ()); // Emit Signal
3348 /** moves a MeterSection to a specified position.
3349 * @param ms - the section to be moved
3350 * @param frame - the new position in frames for the meter
3352 * as a meter cannot snap to anything but bars,
3353 * the supplied frame is rounded to the nearest bar, possibly
3354 * leaving the meter position unchanged.
3357 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3361 if (ms->position_lock_style() == AudioTime) {
3364 Glib::Threads::RWLock::WriterLock lm (lock);
3365 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3367 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3368 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3369 recompute_tempi (_metrics);
3374 Glib::Threads::RWLock::WriterLock lm (lock);
3375 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3377 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3378 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3380 if (solve_map_bbt (future_map, copy, bbt)) {
3381 solve_map_bbt (_metrics, ms, bbt);
3382 recompute_tempi (_metrics);
3387 Metrics::const_iterator d = future_map.begin();
3388 while (d != future_map.end()) {
3393 MetricPositionChanged (PropertyChange ()); // Emit Signal
3397 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3400 bool can_solve = false;
3402 Glib::Threads::RWLock::WriterLock lm (lock);
3403 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3405 if (tempo_copy->type() == TempoSection::Constant) {
3406 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3407 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3409 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3410 tempo_copy->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3413 if (ts->clamped()) {
3414 TempoSection* prev = 0;
3415 if ((prev = previous_tempo_section_locked (future_map, tempo_copy)) != 0) {
3416 prev->set_end_note_types_per_minute (tempo_copy->note_types_per_minute());
3420 recompute_tempi (future_map);
3422 if (check_solved (future_map)) {
3423 if (ts->type() == TempoSection::Constant) {
3424 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3425 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3427 ts->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3428 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3431 if (ts->clamped()) {
3432 TempoSection* prev = 0;
3433 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3434 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3438 recompute_map (_metrics);
3443 Metrics::const_iterator d = future_map.begin();
3444 while (d != future_map.end()) {
3449 MetricPositionChanged (PropertyChange ()); // Emit Signal
3456 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3459 Ts (future prev_t) Tnext
3462 |----------|----------
3469 Glib::Threads::RWLock::WriterLock lm (lock);
3475 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3481 /* minimum allowed measurement distance in frames */
3482 framepos_t const min_dframe = 2;
3485 if (prev_t->clamped()) {
3486 TempoSection* next_t = next_tempo_section_locked (future_map, prev_t);
3487 TempoSection* prev_to_prev_t = previous_tempo_section_locked (future_map, prev_t);
3488 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3489 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3491 double contribution = 0.0;
3492 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3493 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3495 framepos_t const fr_off = (end_frame - frame);
3496 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3498 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3499 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3500 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3502 new_bpm = prev_t->note_types_per_minute();
3505 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3507 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
3508 / (double) (end_frame - prev_t->frame()));
3510 new_bpm = prev_t->note_types_per_minute();
3513 new_bpm = min (new_bpm, (double) 1000.0);
3515 /* don't clamp and proceed here.
3516 testing has revealed that this can go negative,
3517 which is an entirely different thing to just being too low.
3520 if (new_bpm < 0.5) {
3524 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3525 prev_t->set_note_types_per_minute (new_bpm);
3527 prev_t->set_end_note_types_per_minute (new_bpm);
3528 prev_t->set_note_types_per_minute (new_bpm);
3531 if (prev_t->clamped()) {
3532 TempoSection* prev = 0;
3533 if ((prev = previous_tempo_section_locked (future_map, prev_t)) != 0) {
3534 prev->set_end_note_types_per_minute (prev_t->note_types_per_minute());
3538 recompute_tempi (future_map);
3539 recompute_meters (future_map);
3541 if (check_solved (future_map)) {
3542 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3543 ts->set_note_types_per_minute (new_bpm);
3545 ts->set_end_note_types_per_minute (new_bpm);
3546 ts->set_note_types_per_minute (new_bpm);
3548 if (ts->clamped()) {
3549 TempoSection* prev = 0;
3550 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3551 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3554 recompute_tempi (_metrics);
3555 recompute_meters (_metrics);
3561 Metrics::const_iterator d = future_map.begin();
3562 while (d != future_map.end()) {
3566 MetricPositionChanged (PropertyChange ()); // Emit Signal
3571 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3574 Ts (future prev_t) Tnext
3577 |----------|----------
3584 Glib::Threads::RWLock::WriterLock lm (lock);
3590 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3596 /* minimum allowed measurement distance in frames */
3597 framepos_t const min_dframe = 2;
3600 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3601 new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->frame() - frame)
3602 / (double) (prev_t->frame() - end_frame));
3604 new_bpm = prev_t->end_note_types_per_minute();
3607 new_bpm = min (new_bpm, (double) 1000.0);
3609 if (new_bpm < 0.5) {
3613 prev_t->set_end_note_types_per_minute (new_bpm);
3615 TempoSection* next = 0;
3616 if ((next = next_tempo_section_locked (future_map, prev_t)) != 0) {
3617 if (next->clamped()) {
3618 next->set_note_types_per_minute (prev_t->end_note_types_per_minute());
3622 recompute_tempi (future_map);
3623 recompute_meters (future_map);
3625 if (check_solved (future_map)) {
3626 ts->set_end_note_types_per_minute (new_bpm);
3628 TempoSection* true_next = 0;
3629 if ((true_next = next_tempo_section_locked (_metrics, ts)) != 0) {
3630 if (true_next->clamped()) {
3631 true_next->set_note_types_per_minute (ts->end_note_types_per_minute());
3635 recompute_tempi (_metrics);
3636 recompute_meters (_metrics);
3642 Metrics::const_iterator d = future_map.begin();
3643 while (d != future_map.end()) {
3648 MetricPositionChanged (PropertyChange ()); // Emit Signal
3652 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
3654 TempoSection* next_t = 0;
3655 TempoSection* next_to_next_t = 0;
3657 bool can_solve = false;
3659 /* minimum allowed measurement distance in frames */
3660 framepos_t const min_dframe = 2;
3663 Glib::Threads::RWLock::WriterLock lm (lock);
3668 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3669 TempoSection* prev_to_prev_t = 0;
3670 const frameoffset_t fr_off = end_frame - frame;
3676 if (tempo_copy->pulse() > 0.0) {
3677 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
3680 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3681 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3682 next_t = static_cast<TempoSection*> (*i);
3691 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3692 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3693 next_to_next_t = static_cast<TempoSection*> (*i);
3698 if (!next_to_next_t) {
3702 double prev_contribution = 0.0;
3704 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3705 prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3708 const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
3711 framepos_t old_tc_minute = tempo_copy->minute();
3712 double old_next_minute = next_t->minute();
3713 double old_next_to_next_minute = next_to_next_t->minute();
3716 double new_next_bpm;
3717 double new_copy_end_bpm;
3719 if (frame > tempo_copy->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > tempo_copy->frame() + min_dframe) {
3720 new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
3721 / (double) (end_frame - tempo_copy->frame()));
3723 new_bpm = tempo_copy->note_types_per_minute();
3726 /* don't clamp and proceed here.
3727 testing has revealed that this can go negative,
3728 which is an entirely different thing to just being too low.
3730 if (new_bpm < 0.5) {
3734 new_bpm = min (new_bpm, (double) 1000.0);
3736 tempo_copy->set_note_types_per_minute (new_bpm);
3737 if (tempo_copy->type() == TempoSection::Constant) {
3738 tempo_copy->set_end_note_types_per_minute (new_bpm);
3741 recompute_tempi (future_map);
3743 if (check_solved (future_map)) {
3749 ts->set_note_types_per_minute (new_bpm);
3750 if (ts->type() == TempoSection::Constant) {
3751 ts->set_end_note_types_per_minute (new_bpm);
3754 recompute_map (_metrics);
3759 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3760 if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) {
3762 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3763 / (double) ((old_next_to_next_minute) - old_next_minute));
3766 new_next_bpm = next_t->note_types_per_minute();
3769 next_t->set_note_types_per_minute (new_next_bpm);
3770 recompute_tempi (future_map);
3772 if (check_solved (future_map)) {
3773 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3774 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3775 next_t = static_cast<TempoSection*> (*i);
3783 next_t->set_note_types_per_minute (new_next_bpm);
3784 recompute_map (_metrics);
3788 double next_frame_ratio = 1.0;
3789 double copy_frame_ratio = 1.0;
3791 if (next_to_next_t) {
3792 next_frame_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute - old_next_minute);
3794 copy_frame_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute));
3797 new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
3798 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
3800 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3802 if (next_t->clamped()) {
3803 next_t->set_note_types_per_minute (new_copy_end_bpm);
3805 next_t->set_note_types_per_minute (new_next_bpm);
3808 recompute_tempi (future_map);
3810 if (check_solved (future_map)) {
3811 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3812 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3813 next_t = static_cast<TempoSection*> (*i);
3822 if (next_t->clamped()) {
3823 next_t->set_note_types_per_minute (new_copy_end_bpm);
3825 next_t->set_note_types_per_minute (new_next_bpm);
3828 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3829 recompute_map (_metrics);
3835 Metrics::const_iterator d = future_map.begin();
3836 while (d != future_map.end()) {
3841 MetricPositionChanged (PropertyChange ()); // Emit Signal
3845 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3846 * the supplied frame, possibly returning a negative value.
3848 * @param frame The session frame position.
3849 * @param sub_num The subdivision to use when rounding the beat.
3850 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3851 * Positive integers indicate quarter note (non BBT) divisions.
3852 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3853 * @return The beat position of the supplied frame.
3855 * when working to a musical grid, the use of sub_nom indicates that
3856 * the position should be interpreted musically.
3858 * it effectively snaps to meter bars, meter beats or quarter note divisions
3859 * (as per current gui convention) and returns a musical position independent of frame rate.
3861 * If the supplied frame lies before the first meter, the return will be negative,
3862 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3863 * the continuation of the tempo curve (backwards).
3865 * This function is sensitive to tempo and meter.
3868 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3870 Glib::Threads::RWLock::ReaderLock lm (lock);
3872 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3876 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3878 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3881 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3882 * the supplied frame, possibly returning a negative value.
3884 * @param frame The session frame position.
3885 * @param sub_num The subdivision to use when rounding the quarter note.
3886 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3887 * Positive integers indicate quarter note (non BBT) divisions.
3888 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3889 * @return The quarter note position of the supplied frame.
3891 * When working to a musical grid, the use of sub_nom indicates that
3892 * the frame position should be interpreted musically.
3894 * it effectively snaps to meter bars, meter beats or quarter note divisions
3895 * (as per current gui convention) and returns a musical position independent of frame rate.
3897 * If the supplied frame lies before the first meter, the return will be negative,
3898 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3899 * the continuation of the tempo curve (backwards).
3901 * This function is tempo-sensitive.
3904 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3906 Glib::Threads::RWLock::ReaderLock lm (lock);
3908 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3912 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3914 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3917 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3918 } else if (sub_num == 1) {
3919 /* the gui requested exact musical (BBT) beat */
3920 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3921 } else if (sub_num == -1) {
3923 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3927 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3929 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3931 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3941 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3942 * @param pos the frame position in the tempo map.
3943 * @param bbt the distance in BBT time from pos to calculate.
3944 * @param dir the rounding direction..
3945 * @return the duration in frames between pos and bbt
3948 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3950 Glib::Threads::RWLock::ReaderLock lm (lock);
3952 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3954 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3957 pos_bbt.bars += bbt.bars;
3959 pos_bbt.ticks += bbt.ticks;
3960 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3962 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3965 pos_bbt.beats += bbt.beats;
3966 if ((double) pos_bbt.beats > divisions) {
3968 pos_bbt.beats -= divisions;
3970 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3972 return pos_bbt_frame - pos;
3976 if (pos_bbt.bars <= bbt.bars) {
3979 pos_bbt.bars -= bbt.bars;
3982 if (pos_bbt.ticks < bbt.ticks) {
3983 if (pos_bbt.bars > 1) {
3984 if (pos_bbt.beats == 1) {
3986 pos_bbt.beats = divisions;
3990 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3996 pos_bbt.ticks -= bbt.ticks;
3999 if (pos_bbt.beats <= bbt.beats) {
4000 if (pos_bbt.bars > 1) {
4002 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
4007 pos_bbt.beats -= bbt.beats;
4010 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4017 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
4019 return round_to_type (fr, dir, Bar);
4023 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
4025 return round_to_type (fr, dir, Beat);
4029 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
4031 Glib::Threads::RWLock::ReaderLock lm (lock);
4032 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);
4033 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
4034 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
4036 ticks -= beats * BBT_Time::ticks_per_beat;
4039 /* round to next (or same iff dir == RoundUpMaybe) */
4041 uint32_t mod = ticks % ticks_one_subdivisions_worth;
4043 if (mod == 0 && dir == RoundUpMaybe) {
4044 /* right on the subdivision, which is fine, so do nothing */
4046 } else if (mod == 0) {
4047 /* right on the subdivision, so the difference is just the subdivision ticks */
4048 ticks += ticks_one_subdivisions_worth;
4051 /* not on subdivision, compute distance to next subdivision */
4053 ticks += ticks_one_subdivisions_worth - mod;
4056 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
4057 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
4058 // And since the "prev" direction DOES move beats, I assume this code is unintended.
4059 // But I'm keeping it around, until we determine there are no terrible consequences.
4060 // if (ticks >= BBT_Time::ticks_per_beat) {
4061 // ticks -= BBT_Time::ticks_per_beat;
4064 } else if (dir < 0) {
4066 /* round to previous (or same iff dir == RoundDownMaybe) */
4068 uint32_t difference = ticks % ticks_one_subdivisions_worth;
4070 if (difference == 0 && dir == RoundDownAlways) {
4071 /* right on the subdivision, but force-rounding down,
4072 so the difference is just the subdivision ticks */
4073 difference = ticks_one_subdivisions_worth;
4076 if (ticks < difference) {
4077 ticks = BBT_Time::ticks_per_beat - ticks;
4079 ticks -= difference;
4083 /* round to nearest */
4086 /* compute the distance to the previous and next subdivision */
4088 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
4090 /* closer to the next subdivision, so shift forward */
4092 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
4094 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
4096 if (ticks > BBT_Time::ticks_per_beat) {
4098 ticks -= BBT_Time::ticks_per_beat;
4099 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
4102 } else if (rem > 0) {
4104 /* closer to previous subdivision, so shift backward */
4108 /* can't go backwards past zero, so ... */
4109 return MusicFrame (0, 0);
4111 /* step back to previous beat */
4113 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4114 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4116 ticks = lrint (ticks - rem);
4117 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4120 /* on the subdivision, do nothing */
4124 MusicFrame ret (0, 0);
4125 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4126 ret.division = sub_num;
4132 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
4134 Glib::Threads::RWLock::ReaderLock lm (lock);
4135 const double minute = minute_at_frame (frame);
4136 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4137 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
4138 MusicFrame ret (0, 0);
4145 /* find bar previous to 'frame' */
4151 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4155 } else if (dir > 0) {
4156 /* find bar following 'frame' */
4161 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4165 /* true rounding: find nearest bar */
4166 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4169 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4171 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4173 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4174 ret.frame = next_ft;
4179 ret.frame = prev_ft;
4191 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
4194 } else if (dir > 0) {
4195 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
4199 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
4206 return MusicFrame (0, 0);
4210 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4211 framepos_t lower, framepos_t upper, uint32_t bar_mod)
4213 Glib::Threads::RWLock::ReaderLock lm (lock);
4214 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
4216 /* although the map handles negative beats, bbt doesn't. */
4221 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
4225 while (pos >= 0 && pos < upper) {
4226 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
4227 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4228 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4229 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4231 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4235 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
4240 bbt.bars -= bbt.bars % bar_mod;
4244 while (pos >= 0 && pos < upper) {
4245 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4246 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4247 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4248 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4249 bbt.bars += bar_mod;
4255 TempoMap::tempo_section_at_frame (framepos_t frame) const
4257 Glib::Threads::RWLock::ReaderLock lm (lock);
4259 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4263 TempoMap::tempo_section_at_frame (framepos_t frame)
4265 Glib::Threads::RWLock::ReaderLock lm (lock);
4267 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4271 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4273 TempoSection* prev = 0;
4277 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4279 if ((*i)->is_tempo()) {
4280 t = static_cast<TempoSection*> (*i);
4284 if (prev && t->minute() > minute) {
4294 abort(); /*NOTREACHED*/
4300 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4302 TempoSection* prev = 0;
4306 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4308 if ((*i)->is_tempo()) {
4309 t = static_cast<TempoSection*> (*i);
4313 if (prev && t->minute() > minute) {
4323 abort(); /*NOTREACHED*/
4329 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4331 TempoSection* prev_t = 0;
4332 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4336 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4337 if ((*i)->is_tempo()) {
4338 t = static_cast<TempoSection*> (*i);
4344 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4355 TempoMap::previous_tempo_section (TempoSection* ts) const
4357 Glib::Threads::RWLock::ReaderLock lm (lock);
4359 return previous_tempo_section_locked (_metrics, ts);
4364 TempoMap::previous_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4370 TempoSection* prev = 0;
4372 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4374 if ((*i)->is_tempo()) {
4375 TempoSection* t = static_cast<TempoSection*> (*i);
4381 if (prev && t == ts) {
4392 abort(); /*NOTREACHED*/
4399 TempoMap::next_tempo_section (TempoSection* ts) const
4401 Glib::Threads::RWLock::ReaderLock lm (lock);
4403 return next_tempo_section_locked (_metrics, ts);
4407 TempoMap::next_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4413 TempoSection* prev = 0;
4415 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4417 if ((*i)->is_tempo()) {
4418 TempoSection* t = static_cast<TempoSection*> (*i);
4424 if (prev && prev == ts) {
4435 abort(); /*NOTREACHED*/
4440 /* don't use this to calculate length (the tempo is only correct for this frame).
4441 do that stuff based on the beat_at_frame and frame_at_beat api
4444 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4446 Glib::Threads::RWLock::ReaderLock lm (lock);
4448 const TempoSection* ts_at = 0;
4449 const TempoSection* ts_after = 0;
4450 Metrics::const_iterator i;
4453 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4455 if ((*i)->is_tempo()) {
4456 t = static_cast<TempoSection*> (*i);
4460 if (ts_at && (*i)->frame() > frame) {
4470 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4472 /* must be treated as constant tempo */
4473 return ts_at->frames_per_quarter_note (_frame_rate);
4477 TempoMap::meter_section_at_frame (framepos_t frame) const
4479 Glib::Threads::RWLock::ReaderLock lm (lock);
4480 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4484 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4486 Metrics::const_iterator i;
4487 MeterSection* prev = 0;
4491 for (i = metrics.begin(); i != metrics.end(); ++i) {
4493 if (!(*i)->is_tempo()) {
4494 m = static_cast<MeterSection*> (*i);
4496 if (prev && (*i)->minute() > minute) {
4506 abort(); /*NOTREACHED*/
4513 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4515 MeterSection* prev_m = 0;
4517 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4519 if (!(*i)->is_tempo()) {
4520 m = static_cast<MeterSection*> (*i);
4521 if (prev_m && m->beat() > beat) {
4532 TempoMap::meter_section_at_beat (double beat) const
4534 Glib::Threads::RWLock::ReaderLock lm (lock);
4535 return meter_section_at_beat_locked (_metrics, beat);
4539 TempoMap::meter_at_frame (framepos_t frame) const
4541 TempoMetric m (metric_at (frame));
4546 TempoMap::fix_legacy_session ()
4548 MeterSection* prev_m = 0;
4549 TempoSection* prev_t = 0;
4550 bool have_initial_t = false;
4552 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4556 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4558 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4561 m->set_minute (0.0);
4562 m->set_position_lock_style (AudioTime);
4567 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4568 + (m->bbt().beats - 1)
4569 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4571 m->set_beat (start);
4572 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4573 + (m->bbt().beats - 1)
4574 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4575 m->set_pulse (start_beat / prev_m->note_divisor());
4578 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4586 t->set_minute (0.0);
4587 t->set_position_lock_style (AudioTime);
4589 have_initial_t = true;
4594 /* some 4.x sessions have no initial (non-movable) tempo. */
4595 if (!have_initial_t) {
4596 prev_t->set_pulse (0.0);
4597 prev_t->set_minute (0.0);
4598 prev_t->set_position_lock_style (AudioTime);
4599 prev_t->set_initial (true);
4600 prev_t->set_locked_to_meter (true);
4601 have_initial_t = true;
4604 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4605 + (t->legacy_bbt().beats - 1)
4606 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4608 t->set_pulse (beat / prev_m->note_divisor());
4610 /* really shouldn't happen but.. */
4611 t->set_pulse (beat / 4.0);
4619 TempoMap::fix_legacy_end_session ()
4621 TempoSection* prev_t = 0;
4623 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4626 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4633 if (prev_t->type() != TempoSection::Constant) {
4634 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4644 TempoMap::get_state ()
4646 Metrics::const_iterator i;
4647 XMLNode *root = new XMLNode ("TempoMap");
4650 Glib::Threads::RWLock::ReaderLock lm (lock);
4651 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4652 root->add_child_nocopy ((*i)->get_state());
4660 TempoMap::set_state (const XMLNode& node, int /*version*/)
4663 Glib::Threads::RWLock::WriterLock lm (lock);
4666 XMLNodeConstIterator niter;
4667 Metrics old_metrics (_metrics);
4670 nlist = node.children();
4672 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4673 XMLNode* child = *niter;
4675 if (child->name() == TempoSection::xml_state_node_name) {
4678 TempoSection* ts = new TempoSection (*child, _frame_rate);
4679 _metrics.push_back (ts);
4682 catch (failed_constructor& err){
4683 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4684 _metrics = old_metrics;
4685 old_metrics.clear();
4689 } else if (child->name() == MeterSection::xml_state_node_name) {
4692 MeterSection* ms = new MeterSection (*child, _frame_rate);
4693 _metrics.push_back (ms);
4696 catch (failed_constructor& err) {
4697 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4698 _metrics = old_metrics;
4699 old_metrics.clear();
4705 if (niter == nlist.end()) {
4706 MetricSectionSorter cmp;
4707 _metrics.sort (cmp);
4710 /* check for legacy sessions where bbt was the base musical unit for tempo */
4711 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4713 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4714 if (t->legacy_bbt().bars != 0) {
4715 fix_legacy_session();
4719 if (t->legacy_end()) {
4720 fix_legacy_end_session();
4728 /* check for multiple tempo/meters at the same location, which
4729 ardour2 somehow allowed.
4732 Metrics::iterator prev = _metrics.end();
4733 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4734 if (prev != _metrics.end()) {
4736 MeterSection* prev_m;
4738 TempoSection* prev_t;
4739 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4740 if (prev_m->pulse() == ms->pulse()) {
4741 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4742 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4745 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4746 if (prev_t->pulse() == ts->pulse()) {
4747 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4748 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4756 recompute_map (_metrics);
4758 Metrics::const_iterator d = old_metrics.begin();
4759 while (d != old_metrics.end()) {
4763 old_metrics.clear ();
4766 PropertyChanged (PropertyChange ());
4772 TempoMap::dump (std::ostream& o) const
4774 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4775 const MeterSection* m;
4776 const TempoSection* t;
4777 const TempoSection* prev_t = 0;
4779 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4781 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4782 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4783 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4784 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4785 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4787 o << " current start : " << t->note_types_per_minute()
4788 << " current end : " << t->end_note_types_per_minute()
4789 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4790 o << " previous : " << prev_t->note_types_per_minute()
4791 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4792 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4793 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4794 << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4795 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4798 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4799 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4800 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4801 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4804 o << "------" << std::endl;
4808 TempoMap::n_tempos() const
4810 Glib::Threads::RWLock::ReaderLock lm (lock);
4813 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4814 if ((*i)->is_tempo()) {
4823 TempoMap::n_meters() const
4825 Glib::Threads::RWLock::ReaderLock lm (lock);
4828 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4829 if (!(*i)->is_tempo()) {
4838 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4840 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4841 if ((*i)->frame() >= where && !(*i)->initial ()) {
4845 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4846 gui_set_meter_position (ms, (*i)->frame() + amount);
4849 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4850 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4855 PropertyChanged (PropertyChange ());
4859 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4863 std::list<MetricSection*> metric_kill_list;
4865 TempoSection* last_tempo = NULL;
4866 MeterSection* last_meter = NULL;
4867 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4868 bool meter_after = false; // is there a meter marker likewise?
4870 Glib::Threads::RWLock::WriterLock lm (lock);
4871 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4872 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4873 metric_kill_list.push_back(*i);
4874 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4877 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4881 else if ((*i)->frame() >= where) {
4882 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4883 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4884 if ((*i)->frame() == where) {
4885 // marker was immediately after end of range
4886 tempo_after = dynamic_cast<TempoSection*> (*i);
4887 meter_after = dynamic_cast<MeterSection*> (*i);
4893 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4894 if (last_tempo && !tempo_after) {
4895 metric_kill_list.remove(last_tempo);
4896 last_tempo->set_minute (minute_at_frame (where));
4899 if (last_meter && !meter_after) {
4900 metric_kill_list.remove(last_meter);
4901 last_meter->set_minute (minute_at_frame (where));
4905 //remove all the remaining metrics
4906 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4907 _metrics.remove(*i);
4912 recompute_map (_metrics);
4915 PropertyChanged (PropertyChange ());
4919 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4920 * pos can be -ve, if required.
4923 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4925 Glib::Threads::RWLock::ReaderLock lm (lock);
4926 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4928 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4932 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4934 Glib::Threads::RWLock::ReaderLock lm (lock);
4936 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4937 pos_bbt.ticks += op.ticks;
4938 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4940 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4942 pos_bbt.beats += op.beats;
4943 /* the meter in effect will start on the bar */
4944 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();
4945 while (pos_bbt.beats >= divisions_per_bar + 1) {
4947 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4948 pos_bbt.beats -= divisions_per_bar;
4950 pos_bbt.bars += op.bars;
4952 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4955 /** Count the number of beats that are equivalent to distance when going forward,
4959 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4961 Glib::Threads::RWLock::ReaderLock lm (lock);
4963 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4967 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4973 operator<< (std::ostream& o, const Meter& m) {
4974 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4978 operator<< (std::ostream& o, const Tempo& t) {
4979 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4983 operator<< (std::ostream& o, const MetricSection& section) {
4985 o << "MetricSection @ " << section.frame() << ' ';
4987 const TempoSection* ts;
4988 const MeterSection* ms;
4990 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4991 o << *((const Tempo*) ts);
4992 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4993 o << *((const Meter*) ms);