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);
788 TempoMap::operator= (TempoMap const & other)
790 if (&other != this) {
791 Glib::Threads::RWLock::ReaderLock lr (other.lock);
792 Glib::Threads::RWLock::WriterLock lm (lock);
793 _frame_rate = other._frame_rate;
795 Metrics::const_iterator d = _metrics.begin();
796 while (d != _metrics.end()) {
802 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
803 TempoSection const * const ts = dynamic_cast<TempoSection const * const> (*m);
804 MeterSection const * const ms = dynamic_cast<MeterSection const * const> (*m);
807 TempoSection* new_section = new TempoSection (*ts);
808 _metrics.push_back (new_section);
810 MeterSection* new_section = new MeterSection (*ms);
811 _metrics.push_back (new_section);
816 PropertyChanged (PropertyChange());
821 TempoMap::~TempoMap ()
823 Metrics::const_iterator d = _metrics.begin();
824 while (d != _metrics.end()) {
832 TempoMap::frame_at_minute (const double time) const
834 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
838 TempoMap::minute_at_frame (const framepos_t frame) const
840 return (frame / (double) _frame_rate) / 60.0;
844 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
846 bool removed = false;
849 Glib::Threads::RWLock::WriterLock lm (lock);
850 if ((removed = remove_tempo_locked (tempo))) {
851 if (complete_operation) {
852 recompute_map (_metrics);
857 if (removed && complete_operation) {
858 PropertyChanged (PropertyChange ());
863 TempoMap::remove_tempo_locked (const TempoSection& tempo)
867 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
868 if (dynamic_cast<TempoSection*> (*i) != 0) {
869 if (tempo.frame() == (*i)->frame()) {
870 if (!(*i)->initial()) {
883 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
885 bool removed = false;
888 Glib::Threads::RWLock::WriterLock lm (lock);
889 if ((removed = remove_meter_locked (tempo))) {
890 if (complete_operation) {
891 recompute_map (_metrics);
896 if (removed && complete_operation) {
897 PropertyChanged (PropertyChange ());
902 TempoMap::remove_meter_locked (const MeterSection& meter)
905 if (meter.position_lock_style() == AudioTime) {
906 /* remove meter-locked tempo */
907 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
909 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
910 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
919 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
920 if (dynamic_cast<MeterSection*> (*i) != 0) {
921 if (meter.frame() == (*i)->frame()) {
922 if (!(*i)->initial()) {
935 TempoMap::do_insert (MetricSection* section)
937 bool need_add = true;
938 /* we only allow new meters to be inserted on beat 1 of an existing
942 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
944 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
946 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
947 corrected.second.beats = 1;
948 corrected.second.ticks = 0;
949 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
950 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
951 m->bbt(), corrected.second) << endmsg;
952 //m->set_pulse (corrected);
956 /* Look for any existing MetricSection that is of the same type and
957 in the same bar as the new one, and remove it before adding
958 the new one. Note that this means that if we find a matching,
959 existing section, we can break out of the loop since we're
960 guaranteed that there is only one such match.
963 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
965 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
966 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
967 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
968 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
970 if (tempo && insert_tempo) {
973 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
974 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
976 if (tempo->initial()) {
978 /* can't (re)move this section, so overwrite
979 * its data content (but not its properties as
983 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
984 (*i)->set_position_lock_style (AudioTime);
993 } else if (meter && insert_meter) {
997 bool const ipm = insert_meter->position_lock_style() == MusicTime;
999 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
1001 if (meter->initial()) {
1003 /* can't (re)move this section, so overwrite
1004 * its data content (but not its properties as
1008 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
1009 (*i)->set_position_lock_style (AudioTime);
1019 /* non-matching types, so we don't care */
1023 /* Add the given MetricSection, if we didn't just reset an existing
1028 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
1029 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
1030 Metrics::iterator i;
1033 TempoSection* prev_t = 0;
1035 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1036 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
1037 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1040 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
1044 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
1048 prev_t = dynamic_cast<TempoSection*> (*i);
1051 } else if (insert_tempo) {
1052 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1053 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1056 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1057 const bool lm = insert_tempo->locked_to_meter();
1058 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1059 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1066 _metrics.insert (i, section);
1070 /* user supplies the exact pulse if pls == MusicTime */
1072 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1074 if (tempo.note_types_per_minute() <= 0.0) {
1075 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1079 TempoSection* ts = 0;
1080 TempoSection* prev_tempo = 0;
1082 Glib::Threads::RWLock::WriterLock lm (lock);
1083 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true);
1084 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1086 if ((*i)->is_tempo()) {
1087 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1089 bool const ipm = ts->position_lock_style() == MusicTime;
1090 bool const lm = ts->locked_to_meter();
1091 if ((ipm && this_t->pulse() == ts->pulse()) || (!ipm && this_t->frame() == ts->frame())
1092 || (lm && this_t->pulse() == ts->pulse())) {
1093 if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1094 prev_tempo->set_end_note_types_per_minute (ts->note_types_per_minute());
1098 prev_tempo = this_t;
1101 recompute_map (_metrics);
1104 PropertyChanged (PropertyChange ());
1110 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1112 if (tempo.note_types_per_minute() <= 0.0) {
1113 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1117 bool const locked_to_meter = ts.locked_to_meter();
1118 bool const ts_clamped = ts.clamped();
1119 TempoSection* new_ts = 0;
1122 Glib::Threads::RWLock::WriterLock lm (lock);
1123 TempoSection& first (first_tempo());
1124 if (!ts.initial()) {
1125 if (locked_to_meter) {
1127 /* cannot move a meter-locked tempo section */
1128 *static_cast<Tempo*>(&ts) = tempo;
1129 recompute_map (_metrics);
1132 remove_tempo_locked (ts);
1133 new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter);
1134 new_ts->set_clamped (ts_clamped);
1136 if (new_ts && new_ts->type() == TempoSection::Constant) {
1137 new_ts->set_end_note_types_per_minute (new_ts->note_types_per_minute());
1139 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1141 if ((*i)->is_tempo()) {
1142 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1144 bool const ipm = new_ts->position_lock_style() == MusicTime;
1145 bool const lm = new_ts->locked_to_meter();
1146 if ((ipm && this_t->pulse() > new_ts->pulse()) || (!ipm && this_t->frame() > new_ts->frame())
1147 || (lm && this_t->pulse() > new_ts->pulse())) {
1148 new_ts->set_end_note_types_per_minute (tempo.end_note_types_per_minute());
1158 first.set_pulse (0.0);
1159 first.set_minute (minute_at_frame (frame));
1160 first.set_position_lock_style (AudioTime);
1161 first.set_locked_to_meter (true);
1162 first.set_clamped (ts_clamped);
1164 /* cannot move the first tempo section */
1165 *static_cast<Tempo*>(&first) = tempo;
1168 recompute_map (_metrics);
1171 PropertyChanged (PropertyChange ());
1175 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1176 , PositionLockStyle pls, bool recompute, bool locked_to_meter)
1178 TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _frame_rate);
1179 t->set_locked_to_meter (locked_to_meter);
1184 if (pls == AudioTime) {
1185 solve_map_minute (_metrics, t, t->minute());
1187 solve_map_pulse (_metrics, t, t->pulse());
1189 recompute_meters (_metrics);
1196 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1198 MeterSection* m = 0;
1200 Glib::Threads::RWLock::WriterLock lm (lock);
1201 m = add_meter_locked (meter, beat, where, frame, pls, true);
1206 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1211 PropertyChanged (PropertyChange ());
1216 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1219 Glib::Threads::RWLock::WriterLock lm (lock);
1220 const double beat = beat_at_bbt_locked (_metrics, where);
1222 if (!ms.initial()) {
1223 remove_meter_locked (ms);
1224 add_meter_locked (meter, beat, where, frame, pls, true);
1226 MeterSection& first (first_meter());
1227 TempoSection& first_t (first_tempo());
1228 /* cannot move the first meter section */
1229 *static_cast<Meter*>(&first) = meter;
1230 first.set_position_lock_style (AudioTime);
1231 first.set_pulse (0.0);
1232 first.set_minute (minute_at_frame (frame));
1233 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1234 first.set_beat (beat);
1235 first_t.set_minute (first.minute());
1236 first_t.set_locked_to_meter (true);
1237 first_t.set_pulse (0.0);
1238 first_t.set_position_lock_style (AudioTime);
1239 recompute_map (_metrics);
1243 PropertyChanged (PropertyChange ());
1247 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1249 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1250 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1251 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1252 TempoSection* mlt = 0;
1254 if (pls == AudioTime) {
1255 /* add meter-locked tempo */
1256 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), AudioTime, true, true);
1264 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1266 bool solved = false;
1268 do_insert (new_meter);
1272 if (pls == AudioTime) {
1273 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1274 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1275 fudge frame so that the meter ends up at its BBT position instead.
1278 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1281 solved = solve_map_bbt (_metrics, new_meter, where);
1282 /* required due to resetting the pulse of meter-locked tempi above.
1283 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1284 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1286 recompute_map (_metrics);
1290 if (!solved && recompute) {
1291 /* if this has failed to solve, there is little we can do other than to ensure that
1292 the new map is recalculated.
1294 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1295 recompute_map (_metrics);
1302 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1304 Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1307 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1308 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1313 Glib::Threads::RWLock::WriterLock lm (lock);
1314 *((Tempo*) t) = newtempo;
1315 recompute_map (_metrics);
1317 PropertyChanged (PropertyChange ());
1324 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1326 Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1329 TempoSection* first;
1330 Metrics::iterator i;
1332 /* find the TempoSection immediately preceding "where"
1335 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1337 if ((*i)->frame() > where) {
1343 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1356 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1366 Glib::Threads::RWLock::WriterLock lm (lock);
1367 /* cannot move the first tempo section */
1368 *((Tempo*)prev) = newtempo;
1369 recompute_map (_metrics);
1372 PropertyChanged (PropertyChange ());
1376 TempoMap::first_meter () const
1378 const MeterSection *m = 0;
1380 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1381 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1386 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1387 abort(); /*NOTREACHED*/
1392 TempoMap::first_meter ()
1394 MeterSection *m = 0;
1396 /* CALLER MUST HOLD LOCK */
1398 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1399 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1404 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1405 abort(); /*NOTREACHED*/
1410 TempoMap::first_tempo () const
1412 const TempoSection *t = 0;
1414 /* CALLER MUST HOLD LOCK */
1416 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1417 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1427 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1428 abort(); /*NOTREACHED*/
1433 TempoMap::first_tempo ()
1435 TempoSection *t = 0;
1437 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1438 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1448 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1449 abort(); /*NOTREACHED*/
1453 TempoMap::recompute_tempi (Metrics& metrics)
1455 TempoSection* prev_t = 0;
1457 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1460 if ((*i)->is_tempo()) {
1461 t = static_cast<TempoSection*> (*i);
1473 if (t->position_lock_style() == AudioTime) {
1474 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1475 if (!t->locked_to_meter()) {
1476 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1480 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1481 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1489 prev_t->set_c (0.0);
1492 /* tempos must be positioned correctly.
1493 the current approach is to use a meter's bbt time as its base position unit.
1494 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1495 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1498 TempoMap::recompute_meters (Metrics& metrics)
1500 MeterSection* meter = 0;
1501 MeterSection* prev_m = 0;
1503 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1504 if (!(*mi)->is_tempo()) {
1505 meter = static_cast<MeterSection*> (*mi);
1506 if (meter->position_lock_style() == AudioTime) {
1508 pair<double, BBT_Time> b_bbt;
1509 TempoSection* meter_locked_tempo = 0;
1510 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1512 if ((*ii)->is_tempo()) {
1513 t = static_cast<TempoSection*> (*ii);
1514 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1515 meter_locked_tempo = t;
1522 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1523 if (beats + prev_m->beat() != meter->beat()) {
1524 /* reordering caused a bbt change */
1526 beats = meter->beat() - prev_m->beat();
1527 b_bbt = make_pair (beats + prev_m->beat()
1528 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1529 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1531 } else if (!meter->initial()) {
1532 b_bbt = make_pair (meter->beat(), meter->bbt());
1533 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1536 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1538 if (meter_locked_tempo) {
1539 meter_locked_tempo->set_pulse (pulse);
1541 meter->set_beat (b_bbt);
1542 meter->set_pulse (pulse);
1547 pair<double, BBT_Time> b_bbt;
1549 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1550 if (beats + prev_m->beat() != meter->beat()) {
1551 /* reordering caused a bbt change */
1552 b_bbt = make_pair (beats + prev_m->beat()
1553 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1555 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1557 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1559 /* shouldn't happen - the first is audio-locked */
1560 pulse = pulse_at_beat_locked (metrics, meter->beat());
1561 b_bbt = make_pair (meter->beat(), meter->bbt());
1564 meter->set_beat (b_bbt);
1565 meter->set_pulse (pulse);
1566 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1575 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1577 /* CALLER MUST HOLD WRITE LOCK */
1579 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1582 /* silly call from Session::process() during startup
1587 recompute_tempi (metrics);
1588 recompute_meters (metrics);
1592 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1594 Glib::Threads::RWLock::ReaderLock lm (lock);
1595 TempoMetric m (first_meter(), first_tempo());
1598 *last = ++_metrics.begin();
1601 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1602 at something, because we insert the default tempo and meter during
1603 TempoMap construction.
1605 now see if we can find better candidates.
1608 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1610 if ((*i)->frame() > frame) {
1624 /* XX meters only */
1626 TempoMap::metric_at (BBT_Time bbt) const
1628 Glib::Threads::RWLock::ReaderLock lm (lock);
1629 TempoMetric m (first_meter(), first_tempo());
1631 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1632 at something, because we insert the default tempo and meter during
1633 TempoMap construction.
1635 now see if we can find better candidates.
1638 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1640 if (!(*i)->is_tempo()) {
1641 mw = static_cast<MeterSection*> (*i);
1642 BBT_Time section_start (mw->bbt());
1644 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1655 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1656 * @param frame The session frame position.
1657 * @return The beat duration according to the tempo map at the supplied frame.
1659 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1660 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1662 * This function uses both tempo and meter.
1665 TempoMap::beat_at_frame (const framecnt_t& frame) const
1667 Glib::Threads::RWLock::ReaderLock lm (lock);
1669 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1672 /* This function uses both tempo and meter.*/
1674 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1676 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1677 MeterSection* prev_m = 0;
1678 MeterSection* next_m = 0;
1680 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1681 if (!(*i)->is_tempo()) {
1682 if (prev_m && (*i)->minute() > minute) {
1683 next_m = static_cast<MeterSection*> (*i);
1686 prev_m = static_cast<MeterSection*> (*i);
1690 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1692 /* audio locked meters fake their beat */
1693 if (next_m && next_m->beat() < beat) {
1694 return next_m->beat();
1700 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1701 * @param beat The BBT (meter-based) beat.
1702 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1704 * This function uses both tempo and meter.
1707 TempoMap::frame_at_beat (const double& beat) const
1709 Glib::Threads::RWLock::ReaderLock lm (lock);
1711 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1714 /* meter & tempo section based */
1716 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1718 MeterSection* prev_m = 0;
1719 TempoSection* prev_t = 0;
1723 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1724 if (!(*i)->is_tempo()) {
1725 m = static_cast<MeterSection*> (*i);
1726 if (prev_m && m->beat() > beat) {
1736 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1737 if ((*i)->is_tempo()) {
1738 t = static_cast<TempoSection*> (*i);
1744 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1753 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1756 /** Returns a Tempo corresponding to the supplied frame position.
1757 * @param frame The audio frame.
1758 * @return a Tempo according to the tempo map at the supplied frame.
1762 TempoMap::tempo_at_frame (const framepos_t& frame) const
1764 Glib::Threads::RWLock::ReaderLock lm (lock);
1766 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1770 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1772 TempoSection* prev_t = 0;
1776 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1777 if ((*i)->is_tempo()) {
1778 t = static_cast<TempoSection*> (*i);
1782 if ((prev_t) && t->minute() > minute) {
1783 /* t is the section past frame */
1784 return prev_t->tempo_at_minute (minute);
1790 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1793 /** returns the frame at which the supplied tempo occurs, or
1794 * the frame of the last tempo section (search exhausted)
1795 * only the position of the first occurence will be returned
1799 TempoMap::frame_at_tempo (const Tempo& tempo) const
1801 Glib::Threads::RWLock::ReaderLock lm (lock);
1803 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1807 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1809 TempoSection* prev_t = 0;
1810 const double tempo_bpm = tempo.note_types_per_minute();
1812 Metrics::const_iterator i;
1814 for (i = metrics.begin(); i != metrics.end(); ++i) {
1816 if ((*i)->is_tempo()) {
1817 t = static_cast<TempoSection*> (*i);
1825 if (t->note_types_per_minute() == tempo_bpm) {
1830 const double prev_t_bpm = prev_t->note_types_per_minute();
1831 const double prev_t_end_bpm = prev_t->end_note_types_per_minute();
1832 if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm)
1833 || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm)
1834 || (prev_t_end_bpm == tempo_bpm)) {
1836 return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
1843 return prev_t->minute();
1847 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1849 TempoSection* prev_t = 0;
1853 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1854 if ((*i)->is_tempo()) {
1855 t = static_cast<TempoSection*> (*i);
1859 if ((prev_t) && t->pulse() > pulse) {
1860 /* t is the section past frame */
1861 return prev_t->tempo_at_pulse (pulse);
1867 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1871 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1873 TempoSection* prev_t = 0;
1874 const double tempo_bpm = tempo.note_types_per_minute();
1876 Metrics::const_iterator i;
1878 for (i = metrics.begin(); i != metrics.end(); ++i) {
1880 if ((*i)->is_tempo()) {
1881 t = static_cast<TempoSection*> (*i);
1887 const double t_bpm = t->note_types_per_minute();
1889 if (t_bpm == tempo_bpm) {
1894 const double prev_t_bpm = prev_t->note_types_per_minute();
1896 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1897 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1904 return prev_t->pulse();
1907 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1908 * @param qn the position in quarter note beats.
1909 * @return the Tempo at the supplied quarter-note.
1912 TempoMap::tempo_at_quarter_note (const double& qn) const
1914 Glib::Threads::RWLock::ReaderLock lm (lock);
1916 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1919 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1920 * @param tempo the tempo.
1921 * @return the position in quarter-note beats where the map bpm
1922 * is equal to that of the Tempo. currently ignores note_type.
1925 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1927 Glib::Threads::RWLock::ReaderLock lm (lock);
1929 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1932 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1933 * @param metrics the list of metric sections used to calculate the pulse.
1934 * @param beat The BBT (meter-based) beat.
1935 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1937 * a pulse or whole note is the base musical position of a MetricSection.
1938 * it is equivalent to four quarter notes.
1942 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1944 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1946 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1949 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1950 * @param metrics the list of metric sections used to calculate the beat.
1951 * @param pulse the whole-note pulse.
1952 * @return the meter-based beat at the supplied whole-note pulse.
1954 * a pulse or whole note is the base musical position of a MetricSection.
1955 * it is equivalent to four quarter notes.
1958 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1960 MeterSection* prev_m = 0;
1962 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1964 if (!(*i)->is_tempo()) {
1965 m = static_cast<MeterSection*> (*i);
1966 if (prev_m && m->pulse() > pulse) {
1974 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1978 /* tempo section based */
1980 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1982 /* HOLD (at least) THE READER LOCK */
1983 TempoSection* prev_t = 0;
1985 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1987 if ((*i)->is_tempo()) {
1988 t = static_cast<TempoSection*> (*i);
1992 if (prev_t && t->minute() > minute) {
1993 /*the previous ts is the one containing the frame */
1994 const double ret = prev_t->pulse_at_minute (minute);
1995 /* audio locked section in new meter*/
1996 if (t->pulse() < ret) {
2005 /* treated as constant for this ts */
2006 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
2008 return pulses_in_section + prev_t->pulse();
2011 /* tempo section based */
2013 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2015 /* HOLD THE READER LOCK */
2017 const TempoSection* prev_t = 0;
2019 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2022 if ((*i)->is_tempo()) {
2023 t = static_cast<TempoSection*> (*i);
2027 if (prev_t && t->pulse() > pulse) {
2028 return prev_t->minute_at_pulse (pulse);
2034 /* must be treated as constant, irrespective of _type */
2035 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
2037 return dtime + prev_t->minute();
2040 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
2041 * @param bbt The BBT time (meter-based).
2042 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
2046 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
2048 Glib::Threads::RWLock::ReaderLock lm (lock);
2049 return beat_at_bbt_locked (_metrics, bbt);
2054 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2056 /* CALLER HOLDS READ LOCK */
2058 MeterSection* prev_m = 0;
2060 /* because audio-locked meters have 'fake' integral beats,
2061 there is no pulse offset here.
2065 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2066 if (!(*i)->is_tempo()) {
2067 m = static_cast<MeterSection*> (*i);
2069 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2070 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2078 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2079 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2080 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2085 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2086 * @param beat The BBT (meter-based) beat.
2087 * @return The BBT time (meter-based) at the supplied meter-based beat.
2091 TempoMap::bbt_at_beat (const double& beat)
2093 Glib::Threads::RWLock::ReaderLock lm (lock);
2094 return bbt_at_beat_locked (_metrics, beat);
2098 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2100 /* CALLER HOLDS READ LOCK */
2101 MeterSection* prev_m = 0;
2102 const double beats = max (0.0, b);
2104 MeterSection* m = 0;
2106 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2107 if (!(*i)->is_tempo()) {
2108 m = static_cast<MeterSection*> (*i);
2110 if (m->beat() > beats) {
2111 /* this is the meter after the one our beat is on*/
2121 const double beats_in_ms = beats - prev_m->beat();
2122 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2123 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2124 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2125 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2129 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2130 ret.beats = (uint32_t) floor (remaining_beats);
2131 ret.bars = total_bars;
2133 /* 0 0 0 to 1 1 0 - based mapping*/
2137 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2139 ret.ticks -= BBT_Time::ticks_per_beat;
2142 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2150 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2151 * @param bbt The BBT time (meter-based).
2152 * @return the quarter note beat at the supplied BBT time
2154 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2156 * while the input uses meter, the output does not.
2159 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2161 Glib::Threads::RWLock::ReaderLock lm (lock);
2163 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2167 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2169 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2172 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2175 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2179 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2181 /* CALLER HOLDS READ LOCK */
2183 MeterSection* prev_m = 0;
2185 /* because audio-locked meters have 'fake' integral beats,
2186 there is no pulse offset here.
2190 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2191 if (!(*i)->is_tempo()) {
2192 m = static_cast<MeterSection*> (*i);
2194 if (m->bbt().bars > bbt.bars) {
2202 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2203 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2204 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2209 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2210 * @param qn the quarter-note beat.
2211 * @return The BBT time (meter-based) at the supplied meter-based beat.
2213 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2217 TempoMap::bbt_at_quarter_note (const double& qn)
2219 Glib::Threads::RWLock::ReaderLock lm (lock);
2221 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2224 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2225 * @param metrics The list of metric sections used to determine the result.
2226 * @param pulse The whole-note pulse.
2227 * @return The BBT time at the supplied whole-note pulse.
2229 * a pulse or whole note is the basic musical position of a MetricSection.
2230 * it is equivalent to four quarter notes.
2231 * while the output uses meter, the input does not.
2234 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2236 MeterSection* prev_m = 0;
2238 MeterSection* m = 0;
2240 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2242 if (!(*i)->is_tempo()) {
2243 m = static_cast<MeterSection*> (*i);
2246 double const pulses_to_m = m->pulse() - prev_m->pulse();
2247 if (prev_m->pulse() + pulses_to_m > pulse) {
2248 /* this is the meter after the one our beat is on*/
2259 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2260 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2261 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2262 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2263 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2267 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2268 ret.beats = (uint32_t) floor (remaining_beats);
2269 ret.bars = total_bars;
2271 /* 0 0 0 to 1 1 0 mapping*/
2275 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2277 ret.ticks -= BBT_Time::ticks_per_beat;
2280 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2288 /** Returns the BBT time corresponding to the supplied frame position.
2289 * @param frame the position in audio samples.
2290 * @return the BBT time at the frame position .
2294 TempoMap::bbt_at_frame (framepos_t frame)
2302 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2307 const double minute = minute_at_frame (frame);
2309 Glib::Threads::RWLock::ReaderLock lm (lock);
2311 return bbt_at_minute_locked (_metrics, minute);
2315 TempoMap::bbt_at_frame_rt (framepos_t frame)
2317 const double minute = minute_at_frame (frame);
2319 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2322 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2325 return bbt_at_minute_locked (_metrics, minute);
2329 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2339 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2340 MeterSection* prev_m = 0;
2341 MeterSection* next_m = 0;
2345 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2346 if (!(*i)->is_tempo()) {
2347 m = static_cast<MeterSection*> (*i);
2348 if (prev_m && m->minute() > minute) {
2356 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2358 /* handle frame before first meter */
2359 if (minute < prev_m->minute()) {
2362 /* audio locked meters fake their beat */
2363 if (next_m && next_m->beat() < beat) {
2364 beat = next_m->beat();
2367 beat = max (0.0, beat);
2369 const double beats_in_ms = beat - prev_m->beat();
2370 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2371 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2372 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2373 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2377 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2378 ret.beats = (uint32_t) floor (remaining_beats);
2379 ret.bars = total_bars;
2381 /* 0 0 0 to 1 1 0 - based mapping*/
2385 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2387 ret.ticks -= BBT_Time::ticks_per_beat;
2390 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2398 /** Returns the frame position corresponding to the supplied BBT time.
2399 * @param bbt the position in BBT time.
2400 * @return the frame position at bbt.
2404 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2408 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2413 if (bbt.beats < 1) {
2414 throw std::logic_error ("beats are counted from one");
2419 Glib::Threads::RWLock::ReaderLock lm (lock);
2420 minute = minute_at_bbt_locked (_metrics, bbt);
2423 return frame_at_minute (minute);
2426 /* meter & tempo section based */
2428 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2430 /* HOLD THE READER LOCK */
2432 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2437 * Returns the quarter-note beat position corresponding to the supplied frame.
2439 * @param frame the position in frames.
2440 * @return The quarter-note position of the supplied frame. Ignores meter.
2444 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2446 const double minute = minute_at_frame (frame);
2448 Glib::Threads::RWLock::ReaderLock lm (lock);
2450 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2454 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2456 const double minute = minute_at_frame (frame);
2458 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2461 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2464 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2468 * Returns the frame position corresponding to the supplied quarter-note beat.
2470 * @param quarter_note the quarter-note position.
2471 * @return the frame position of the supplied quarter-note. Ignores meter.
2476 TempoMap::frame_at_quarter_note (const double quarter_note) const
2480 Glib::Threads::RWLock::ReaderLock lm (lock);
2482 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2485 return frame_at_minute (minute);
2488 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2489 * @param beat The BBT (meter-based) beat.
2490 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2492 * a quarter-note may be compared with and assigned to Evoral::Beats.
2496 TempoMap::quarter_note_at_beat (const double beat) const
2498 Glib::Threads::RWLock::ReaderLock lm (lock);
2500 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2503 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2504 * @param quarter_note The position in quarter-note beats.
2505 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2507 * a quarter-note is the musical unit of Evoral::Beats.
2511 TempoMap::beat_at_quarter_note (const double quarter_note) const
2513 Glib::Threads::RWLock::ReaderLock lm (lock);
2515 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2518 /** Returns the duration in frames between two supplied quarter-note beat positions.
2519 * @param start the first position in quarter-note beats.
2520 * @param end the end position in quarter-note beats.
2521 * @return the frame distance ober the quarter-note beats duration.
2523 * use this rather than e.g.
2524 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2525 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2529 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2534 Glib::Threads::RWLock::ReaderLock lm (lock);
2535 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2538 return frame_at_minute (minutes);
2542 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2545 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2549 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2551 Glib::Threads::RWLock::ReaderLock lm (lock);
2553 return quarter_notes_between_frames_locked (_metrics, start, end);
2557 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2559 const TempoSection* prev_t = 0;
2561 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2564 if ((*i)->is_tempo()) {
2565 t = static_cast<TempoSection*> (*i);
2569 if (prev_t && t->frame() > start) {
2576 const double start_qn = prev_t->pulse_at_frame (start);
2578 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2581 if ((*i)->is_tempo()) {
2582 t = static_cast<TempoSection*> (*i);
2586 if (prev_t && t->frame() > end) {
2592 const double end_qn = prev_t->pulse_at_frame (end);
2594 return (end_qn - start_qn) * 4.0;
2598 TempoMap::check_solved (const Metrics& metrics) const
2600 TempoSection* prev_t = 0;
2601 MeterSection* prev_m = 0;
2603 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2606 if ((*i)->is_tempo()) {
2607 t = static_cast<TempoSection*> (*i);
2612 /* check ordering */
2613 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2617 /* precision check ensures tempo and frames align.*/
2618 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2619 if (!t->locked_to_meter()) {
2624 /* gradient limit - who knows what it should be?
2625 things are also ok (if a little chaotic) without this
2627 if (fabs (prev_t->c()) > 1000.0) {
2628 //std::cout << "c : " << prev_t->c() << std::endl;
2635 if (!(*i)->is_tempo()) {
2636 m = static_cast<MeterSection*> (*i);
2637 if (prev_m && m->position_lock_style() == AudioTime) {
2638 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2639 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2640 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2642 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2656 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2658 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2660 if ((*i)->is_tempo()) {
2661 t = static_cast<TempoSection*> (*i);
2662 if (t->locked_to_meter()) {
2663 t->set_active (true);
2664 } else if (t->position_lock_style() == AudioTime) {
2665 if (t->frame() < frame) {
2666 t->set_active (false);
2667 t->set_pulse (-1.0);
2668 } else if (t->frame() > frame) {
2669 t->set_active (true);
2670 } else if (t->frame() == frame) {
2680 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2682 TempoSection* prev_t = 0;
2683 TempoSection* section_prev = 0;
2684 double first_m_minute = 0.0;
2685 const bool sml = section->locked_to_meter();
2687 /* can't move a tempo before the first meter */
2688 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2690 if (!(*i)->is_tempo()) {
2691 m = static_cast<MeterSection*> (*i);
2693 first_m_minute = m->minute();
2698 if (!section->initial() && minute <= first_m_minute) {
2702 section->set_active (true);
2703 section->set_minute (minute);
2705 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2707 if ((*i)->is_tempo()) {
2708 t = static_cast<TempoSection*> (*i);
2720 if (t->frame() == frame_at_minute (minute)) {
2724 const bool tlm = t->position_lock_style() == MusicTime;
2726 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2727 section_prev = prev_t;
2729 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2730 if (!section->locked_to_meter()) {
2731 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2736 if (t->position_lock_style() == MusicTime) {
2737 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2738 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2740 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2741 if (!t->locked_to_meter()) {
2742 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2751 recompute_tempi (imaginary);
2753 if (check_solved (imaginary)) {
2756 dunp (imaginary, std::cout);
2760 MetricSectionFrameSorter fcmp;
2761 imaginary.sort (fcmp);
2763 recompute_tempi (imaginary);
2765 if (check_solved (imaginary)) {
2773 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2775 TempoSection* prev_t = 0;
2776 TempoSection* section_prev = 0;
2778 section->set_pulse (pulse);
2780 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2782 if ((*i)->is_tempo()) {
2783 t = static_cast<TempoSection*> (*i);
2794 section_prev = prev_t;
2798 if (t->position_lock_style() == MusicTime) {
2799 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2800 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2802 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2803 if (!t->locked_to_meter()) {
2804 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2813 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2814 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2818 recompute_tempi (imaginary);
2820 if (check_solved (imaginary)) {
2823 dunp (imaginary, std::cout);
2827 MetricSectionSorter cmp;
2828 imaginary.sort (cmp);
2830 recompute_tempi (imaginary);
2832 * XX need a restriction here, but only for this case,
2833 * as audio locked tempos don't interact in the same way.
2835 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2837 * |50 bpm |250 bpm |60 bpm
2838 * drag 250 to the pulse after 60->
2839 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2841 if (check_solved (imaginary)) {
2849 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2851 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2852 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2853 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2857 if (section->initial()) {
2858 /* lock the first tempo to our first meter */
2859 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2864 TempoSection* meter_locked_tempo = 0;
2866 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2868 if ((*ii)->is_tempo()) {
2869 t = static_cast<TempoSection*> (*ii);
2870 if (t->locked_to_meter() && t->frame() == section->frame()) {
2871 meter_locked_tempo = t;
2877 if (!meter_locked_tempo) {
2881 MeterSection* prev_m = 0;
2883 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2884 bool solved = false;
2886 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2888 if (!(*i)->is_tempo()) {
2889 m = static_cast<MeterSection*> (*i);
2891 if (prev_m && !section->initial()) {
2892 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2893 if (beats + prev_m->beat() < section->beat()) {
2894 /* set the section pulse according to its musical position,
2895 * as an earlier time than this has been requested.
2897 const double new_pulse = ((section->beat() - prev_m->beat())
2898 / prev_m->note_divisor()) + prev_m->pulse();
2900 tempo_copy->set_position_lock_style (MusicTime);
2901 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2902 meter_locked_tempo->set_position_lock_style (MusicTime);
2903 section->set_position_lock_style (MusicTime);
2904 section->set_pulse (new_pulse);
2905 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2906 meter_locked_tempo->set_position_lock_style (AudioTime);
2907 section->set_position_lock_style (AudioTime);
2908 section->set_minute (meter_locked_tempo->minute());
2914 Metrics::const_iterator d = future_map.begin();
2915 while (d != future_map.end()) {
2924 /* all is ok. set section's locked tempo if allowed.
2925 possibly disallow if there is an adjacent audio-locked tempo.
2926 XX this check could possibly go. its never actually happened here.
2928 MeterSection* meter_copy = const_cast<MeterSection*>
2929 (&meter_section_at_minute_locked (future_map, section->minute()));
2931 meter_copy->set_minute (minute);
2933 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2934 section->set_minute (minute);
2935 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2936 / prev_m->note_divisor()) + prev_m->pulse());
2937 solve_map_minute (imaginary, meter_locked_tempo, minute);
2942 Metrics::const_iterator d = future_map.begin();
2943 while (d != future_map.end()) {
2953 /* initial (first meter atm) */
2955 tempo_copy->set_minute (minute);
2956 tempo_copy->set_pulse (0.0);
2958 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2959 section->set_minute (minute);
2960 meter_locked_tempo->set_minute (minute);
2961 meter_locked_tempo->set_pulse (0.0);
2962 solve_map_minute (imaginary, meter_locked_tempo, minute);
2967 Metrics::const_iterator d = future_map.begin();
2968 while (d != future_map.end()) {
2977 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2978 section->set_beat (b_bbt);
2979 section->set_pulse (0.0);
2989 MetricSectionFrameSorter fcmp;
2990 imaginary.sort (fcmp);
2992 recompute_meters (imaginary);
2998 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
3000 /* disallow setting section to an existing meter's bbt */
3001 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
3003 if (!(*i)->is_tempo()) {
3004 m = static_cast<MeterSection*> (*i);
3005 if (m != section && m->bbt().bars == when.bars) {
3011 MeterSection* prev_m = 0;
3012 MeterSection* section_prev = 0;
3014 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
3016 if (!(*i)->is_tempo()) {
3017 m = static_cast<MeterSection*> (*i);
3023 pair<double, BBT_Time> b_bbt;
3024 double new_pulse = 0.0;
3026 if (prev_m && m->bbt().bars > when.bars && !section_prev){
3027 section_prev = prev_m;
3029 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
3030 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
3031 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
3033 section->set_beat (b_bbt);
3034 section->set_pulse (pulse);
3035 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3039 if (m->position_lock_style() == AudioTime) {
3040 TempoSection* meter_locked_tempo = 0;
3042 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
3044 if ((*ii)->is_tempo()) {
3045 t = static_cast<TempoSection*> (*ii);
3046 if (t->locked_to_meter() && t->frame() == m->frame()) {
3047 meter_locked_tempo = t;
3053 if (!meter_locked_tempo) {
3058 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3060 if (beats + prev_m->beat() != m->beat()) {
3061 /* tempo/ meter change caused a change in beat (bar). */
3063 /* the user has requested that the previous section of music overlaps this one.
3064 we have no choice but to change the bar number here, as being locked to audio means
3065 we must stay where we are on the timeline.
3067 beats = m->beat() - prev_m->beat();
3068 b_bbt = make_pair (beats + prev_m->beat()
3069 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3070 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3072 } else if (!m->initial()) {
3073 b_bbt = make_pair (m->beat(), m->bbt());
3074 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3077 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3080 meter_locked_tempo->set_pulse (new_pulse);
3081 m->set_beat (b_bbt);
3082 m->set_pulse (new_pulse);
3086 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3087 if (beats + prev_m->beat() != m->beat()) {
3088 /* tempo/ meter change caused a change in beat (bar). */
3089 b_bbt = make_pair (beats + prev_m->beat()
3090 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3092 b_bbt = make_pair (beats + prev_m->beat()
3095 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3096 m->set_beat (b_bbt);
3097 m->set_pulse (new_pulse);
3098 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3105 if (!section_prev) {
3107 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3108 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3109 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3111 section->set_beat (b_bbt);
3112 section->set_pulse (pulse);
3113 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3116 MetricSectionSorter cmp;
3117 imaginary.sort (cmp);
3119 recompute_meters (imaginary);
3124 /** places a copy of _metrics into copy and returns a pointer
3125 * to section's equivalent in copy.
3128 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section) const
3130 TempoSection* ret = 0;
3132 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3133 if ((*i)->is_tempo()) {
3134 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3136 ret = new TempoSection (*t);
3137 copy.push_back (ret);
3141 TempoSection* cp = new TempoSection (*t);
3142 copy.push_back (cp);
3144 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3145 MeterSection* cp = new MeterSection (*m);
3146 copy.push_back (cp);
3154 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section) const
3156 MeterSection* ret = 0;
3158 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3159 if ((*i)->is_tempo()) {
3160 TempoSection const * const t = dynamic_cast<TempoSection const * const> (*i);
3161 TempoSection* cp = new TempoSection (*t);
3162 copy.push_back (cp);
3164 MeterSection const * const m = dynamic_cast<MeterSection const * const> (*i);
3166 ret = new MeterSection (*m);
3167 copy.push_back (ret);
3170 MeterSection* cp = new MeterSection (*m);
3171 copy.push_back (cp);
3178 /** answers the question "is this a valid beat position for this tempo section?".
3179 * it returns true if the tempo section can be moved to the requested bbt position,
3180 * leaving the tempo map in a solved state.
3181 * @param ts the tempo section to be moved
3182 * @param bbt the requested new position for the tempo section
3183 * @return true if the tempo section can be moved to the position, otherwise false.
3186 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3189 TempoSection* tempo_copy = 0;
3192 Glib::Threads::RWLock::ReaderLock lm (lock);
3193 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3199 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3201 Metrics::const_iterator d = copy.begin();
3202 while (d != copy.end()) {
3211 * 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,
3212 * taking any possible reordering as a consequence of this into account.
3213 * @param section - the section to be altered
3214 * @param bbt - the BBT time where the altered tempo will fall
3215 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3217 pair<double, framepos_t>
3218 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3221 pair<double, framepos_t> ret = make_pair (0.0, 0);
3223 Glib::Threads::RWLock::ReaderLock lm (lock);
3225 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3227 const double beat = beat_at_bbt_locked (future_map, bbt);
3229 if (section->position_lock_style() == AudioTime) {
3230 tempo_copy->set_position_lock_style (MusicTime);
3233 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3234 ret.first = tempo_copy->pulse();
3235 ret.second = tempo_copy->frame();
3237 ret.first = section->pulse();
3238 ret.second = section->frame();
3241 Metrics::const_iterator d = future_map.begin();
3242 while (d != future_map.end()) {
3249 /** moves a TempoSection to a specified position.
3250 * @param ts - the section to be moved
3251 * @param frame - the new position in frames for the tempo
3252 * @param sub_num - the snap division to use if using musical time.
3254 * if sub_num is non-zero, the frame position is used to calculate an exact
3257 * -1 | snap to bars (meter-based)
3258 * 0 | no snap - use audio frame for musical position
3259 * 1 | snap to meter-based (BBT) beat
3260 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3262 * this follows the snap convention in the gui.
3263 * if sub_num is zero, the musical position will be taken from the supplied frame.
3266 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3270 if (ts->position_lock_style() == MusicTime) {
3272 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3273 Glib::Threads::RWLock::WriterLock lm (lock);
3274 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3276 tempo_copy->set_position_lock_style (AudioTime);
3278 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3279 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3280 const double pulse = pulse_at_beat_locked (future_map, beat);
3282 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3283 solve_map_pulse (_metrics, ts, pulse);
3284 recompute_meters (_metrics);
3292 Glib::Threads::RWLock::WriterLock lm (lock);
3293 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3295 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3297 /* We're moving the object that defines the grid while snapping to it...
3298 * Placing the ts at the beat corresponding to the requested frame may shift the
3299 * grid in such a way that the mouse is left hovering over a completerly different division,
3300 * causing jittering when the mouse next moves (esp. large tempo deltas).
3302 * This alters the snap behaviour slightly in that we snap to beat divisions
3303 * in the future map rather than the existing one.
3305 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3306 const framepos_t snapped_frame = frame_at_minute (minute_at_pulse_locked (future_map, qn / 4.0));
3308 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3309 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3310 ts->set_pulse (qn / 4.0);
3311 recompute_meters (_metrics);
3314 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3315 recompute_meters (_metrics);
3321 Metrics::const_iterator d = future_map.begin();
3322 while (d != future_map.end()) {
3327 MetricPositionChanged (PropertyChange ()); // Emit Signal
3330 /** moves a MeterSection to a specified position.
3331 * @param ms - the section to be moved
3332 * @param frame - the new position in frames for the meter
3334 * as a meter cannot snap to anything but bars,
3335 * the supplied frame is rounded to the nearest bar, possibly
3336 * leaving the meter position unchanged.
3339 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3343 if (ms->position_lock_style() == AudioTime) {
3346 Glib::Threads::RWLock::WriterLock lm (lock);
3347 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3349 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3350 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3351 recompute_tempi (_metrics);
3356 Glib::Threads::RWLock::WriterLock lm (lock);
3357 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3359 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3360 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3362 if (solve_map_bbt (future_map, copy, bbt)) {
3363 solve_map_bbt (_metrics, ms, bbt);
3364 recompute_tempi (_metrics);
3369 Metrics::const_iterator d = future_map.begin();
3370 while (d != future_map.end()) {
3375 MetricPositionChanged (PropertyChange ()); // Emit Signal
3379 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3382 bool can_solve = false;
3384 Glib::Threads::RWLock::WriterLock lm (lock);
3385 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3387 if (tempo_copy->type() == TempoSection::Constant) {
3388 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3389 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3391 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3392 tempo_copy->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3395 if (ts->clamped()) {
3396 TempoSection* prev = 0;
3397 if ((prev = previous_tempo_section_locked (future_map, tempo_copy)) != 0) {
3398 prev->set_end_note_types_per_minute (tempo_copy->note_types_per_minute());
3402 recompute_tempi (future_map);
3404 if (check_solved (future_map)) {
3405 if (ts->type() == TempoSection::Constant) {
3406 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3407 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3409 ts->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3410 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3413 if (ts->clamped()) {
3414 TempoSection* prev = 0;
3415 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3416 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3420 recompute_map (_metrics);
3425 Metrics::const_iterator d = future_map.begin();
3426 while (d != future_map.end()) {
3431 MetricPositionChanged (PropertyChange ()); // Emit Signal
3438 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3441 Ts (future prev_t) Tnext
3444 |----------|----------
3451 Glib::Threads::RWLock::WriterLock lm (lock);
3457 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3463 /* minimum allowed measurement distance in frames */
3464 framepos_t const min_dframe = 2;
3467 if (prev_t->clamped()) {
3468 TempoSection* next_t = next_tempo_section_locked (future_map, prev_t);
3469 TempoSection* prev_to_prev_t = previous_tempo_section_locked (future_map, prev_t);
3470 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3471 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3473 double contribution = 0.0;
3474 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3475 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3477 framepos_t const fr_off = (end_frame - frame);
3478 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3480 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3481 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3482 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3484 new_bpm = prev_t->note_types_per_minute();
3487 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3489 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
3490 / (double) (end_frame - prev_t->frame()));
3492 new_bpm = prev_t->note_types_per_minute();
3495 new_bpm = min (new_bpm, (double) 1000.0);
3497 /* don't clamp and proceed here.
3498 testing has revealed that this can go negative,
3499 which is an entirely different thing to just being too low.
3502 if (new_bpm < 0.5) {
3506 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3507 prev_t->set_note_types_per_minute (new_bpm);
3509 prev_t->set_end_note_types_per_minute (new_bpm);
3510 prev_t->set_note_types_per_minute (new_bpm);
3513 if (prev_t->clamped()) {
3514 TempoSection* prev = 0;
3515 if ((prev = previous_tempo_section_locked (future_map, prev_t)) != 0) {
3516 prev->set_end_note_types_per_minute (prev_t->note_types_per_minute());
3520 recompute_tempi (future_map);
3521 recompute_meters (future_map);
3523 if (check_solved (future_map)) {
3524 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3525 ts->set_note_types_per_minute (new_bpm);
3527 ts->set_end_note_types_per_minute (new_bpm);
3528 ts->set_note_types_per_minute (new_bpm);
3530 if (ts->clamped()) {
3531 TempoSection* prev = 0;
3532 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3533 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3536 recompute_tempi (_metrics);
3537 recompute_meters (_metrics);
3543 Metrics::const_iterator d = future_map.begin();
3544 while (d != future_map.end()) {
3548 MetricPositionChanged (PropertyChange ()); // Emit Signal
3553 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3556 Ts (future prev_t) Tnext
3559 |----------|----------
3566 Glib::Threads::RWLock::WriterLock lm (lock);
3572 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3578 /* minimum allowed measurement distance in frames */
3579 framepos_t const min_dframe = 2;
3582 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3583 new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->frame() - frame)
3584 / (double) (prev_t->frame() - end_frame));
3586 new_bpm = prev_t->end_note_types_per_minute();
3589 new_bpm = min (new_bpm, (double) 1000.0);
3591 if (new_bpm < 0.5) {
3595 prev_t->set_end_note_types_per_minute (new_bpm);
3597 TempoSection* next = 0;
3598 if ((next = next_tempo_section_locked (future_map, prev_t)) != 0) {
3599 if (next->clamped()) {
3600 next->set_note_types_per_minute (prev_t->end_note_types_per_minute());
3604 recompute_tempi (future_map);
3605 recompute_meters (future_map);
3607 if (check_solved (future_map)) {
3608 ts->set_end_note_types_per_minute (new_bpm);
3610 TempoSection* true_next = 0;
3611 if ((true_next = next_tempo_section_locked (_metrics, ts)) != 0) {
3612 if (true_next->clamped()) {
3613 true_next->set_note_types_per_minute (ts->end_note_types_per_minute());
3617 recompute_tempi (_metrics);
3618 recompute_meters (_metrics);
3624 Metrics::const_iterator d = future_map.begin();
3625 while (d != future_map.end()) {
3630 MetricPositionChanged (PropertyChange ()); // Emit Signal
3634 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
3636 TempoSection* next_t = 0;
3637 TempoSection* next_to_next_t = 0;
3639 bool can_solve = false;
3641 /* minimum allowed measurement distance in frames */
3642 framepos_t const min_dframe = 2;
3645 Glib::Threads::RWLock::WriterLock lm (lock);
3650 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3651 TempoSection* prev_to_prev_t = 0;
3652 const frameoffset_t fr_off = end_frame - frame;
3658 if (tempo_copy->pulse() > 0.0) {
3659 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
3662 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3663 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3664 next_t = static_cast<TempoSection*> (*i);
3673 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3674 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3675 next_to_next_t = static_cast<TempoSection*> (*i);
3680 if (!next_to_next_t) {
3684 double prev_contribution = 0.0;
3686 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3687 prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3690 const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
3693 framepos_t old_tc_minute = tempo_copy->minute();
3694 double old_next_minute = next_t->minute();
3695 double old_next_to_next_minute = next_to_next_t->minute();
3698 double new_next_bpm;
3699 double new_copy_end_bpm;
3701 if (frame > tempo_copy->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > tempo_copy->frame() + min_dframe) {
3702 new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
3703 / (double) (end_frame - tempo_copy->frame()));
3705 new_bpm = tempo_copy->note_types_per_minute();
3708 /* don't clamp and proceed here.
3709 testing has revealed that this can go negative,
3710 which is an entirely different thing to just being too low.
3712 if (new_bpm < 0.5) {
3716 new_bpm = min (new_bpm, (double) 1000.0);
3718 tempo_copy->set_note_types_per_minute (new_bpm);
3719 if (tempo_copy->type() == TempoSection::Constant) {
3720 tempo_copy->set_end_note_types_per_minute (new_bpm);
3723 recompute_tempi (future_map);
3725 if (check_solved (future_map)) {
3731 ts->set_note_types_per_minute (new_bpm);
3732 if (ts->type() == TempoSection::Constant) {
3733 ts->set_end_note_types_per_minute (new_bpm);
3736 recompute_map (_metrics);
3741 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3742 if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) {
3744 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3745 / (double) ((old_next_to_next_minute) - old_next_minute));
3748 new_next_bpm = next_t->note_types_per_minute();
3751 next_t->set_note_types_per_minute (new_next_bpm);
3752 recompute_tempi (future_map);
3754 if (check_solved (future_map)) {
3755 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3756 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3757 next_t = static_cast<TempoSection*> (*i);
3765 next_t->set_note_types_per_minute (new_next_bpm);
3766 recompute_map (_metrics);
3770 double next_frame_ratio = 1.0;
3771 double copy_frame_ratio = 1.0;
3773 if (next_to_next_t) {
3774 next_frame_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute - old_next_minute);
3776 copy_frame_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute));
3779 new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
3780 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
3782 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3784 if (next_t->clamped()) {
3785 next_t->set_note_types_per_minute (new_copy_end_bpm);
3787 next_t->set_note_types_per_minute (new_next_bpm);
3790 recompute_tempi (future_map);
3792 if (check_solved (future_map)) {
3793 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3794 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3795 next_t = static_cast<TempoSection*> (*i);
3804 if (next_t->clamped()) {
3805 next_t->set_note_types_per_minute (new_copy_end_bpm);
3807 next_t->set_note_types_per_minute (new_next_bpm);
3810 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3811 recompute_map (_metrics);
3817 Metrics::const_iterator d = future_map.begin();
3818 while (d != future_map.end()) {
3823 MetricPositionChanged (PropertyChange ()); // Emit Signal
3827 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3828 * the supplied frame, possibly returning a negative value.
3830 * @param frame The session frame position.
3831 * @param sub_num The subdivision to use when rounding the beat.
3832 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3833 * Positive integers indicate quarter note (non BBT) divisions.
3834 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3835 * @return The beat position of the supplied frame.
3837 * when working to a musical grid, the use of sub_nom indicates that
3838 * the position should be interpreted musically.
3840 * it effectively snaps to meter bars, meter beats or quarter note divisions
3841 * (as per current gui convention) and returns a musical position independent of frame rate.
3843 * If the supplied frame lies before the first meter, the return will be negative,
3844 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3845 * the continuation of the tempo curve (backwards).
3847 * This function is sensitive to tempo and meter.
3850 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3852 Glib::Threads::RWLock::ReaderLock lm (lock);
3854 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3858 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3860 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3863 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3864 * the supplied frame, possibly returning a negative value.
3866 * @param frame The session frame position.
3867 * @param sub_num The subdivision to use when rounding the quarter note.
3868 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3869 * Positive integers indicate quarter note (non BBT) divisions.
3870 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3871 * @return The quarter note position of the supplied frame.
3873 * When working to a musical grid, the use of sub_nom indicates that
3874 * the frame position should be interpreted musically.
3876 * it effectively snaps to meter bars, meter beats or quarter note divisions
3877 * (as per current gui convention) and returns a musical position independent of frame rate.
3879 * If the supplied frame lies before the first meter, the return will be negative,
3880 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3881 * the continuation of the tempo curve (backwards).
3883 * This function is tempo-sensitive.
3886 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3888 Glib::Threads::RWLock::ReaderLock lm (lock);
3890 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3894 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3896 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3899 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3900 } else if (sub_num == 1) {
3901 /* the gui requested exact musical (BBT) beat */
3902 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3903 } else if (sub_num == -1) {
3905 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3909 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3911 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3913 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3923 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3924 * @param pos the frame position in the tempo map.
3925 * @param bbt the distance in BBT time from pos to calculate.
3926 * @param dir the rounding direction..
3927 * @return the duration in frames between pos and bbt
3930 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3932 Glib::Threads::RWLock::ReaderLock lm (lock);
3934 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3936 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3939 pos_bbt.bars += bbt.bars;
3941 pos_bbt.ticks += bbt.ticks;
3942 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3944 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3947 pos_bbt.beats += bbt.beats;
3948 if ((double) pos_bbt.beats > divisions) {
3950 pos_bbt.beats -= divisions;
3952 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3954 return pos_bbt_frame - pos;
3958 if (pos_bbt.bars <= bbt.bars) {
3961 pos_bbt.bars -= bbt.bars;
3964 if (pos_bbt.ticks < bbt.ticks) {
3965 if (pos_bbt.bars > 1) {
3966 if (pos_bbt.beats == 1) {
3968 pos_bbt.beats = divisions;
3972 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3978 pos_bbt.ticks -= bbt.ticks;
3981 if (pos_bbt.beats <= bbt.beats) {
3982 if (pos_bbt.bars > 1) {
3984 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3989 pos_bbt.beats -= bbt.beats;
3992 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3999 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
4001 return round_to_type (fr, dir, Bar);
4005 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
4007 return round_to_type (fr, dir, Beat);
4011 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
4013 Glib::Threads::RWLock::ReaderLock lm (lock);
4014 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);
4015 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
4016 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
4018 ticks -= beats * BBT_Time::ticks_per_beat;
4021 /* round to next (or same iff dir == RoundUpMaybe) */
4023 uint32_t mod = ticks % ticks_one_subdivisions_worth;
4025 if (mod == 0 && dir == RoundUpMaybe) {
4026 /* right on the subdivision, which is fine, so do nothing */
4028 } else if (mod == 0) {
4029 /* right on the subdivision, so the difference is just the subdivision ticks */
4030 ticks += ticks_one_subdivisions_worth;
4033 /* not on subdivision, compute distance to next subdivision */
4035 ticks += ticks_one_subdivisions_worth - mod;
4038 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
4039 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
4040 // And since the "prev" direction DOES move beats, I assume this code is unintended.
4041 // But I'm keeping it around, until we determine there are no terrible consequences.
4042 // if (ticks >= BBT_Time::ticks_per_beat) {
4043 // ticks -= BBT_Time::ticks_per_beat;
4046 } else if (dir < 0) {
4048 /* round to previous (or same iff dir == RoundDownMaybe) */
4050 uint32_t difference = ticks % ticks_one_subdivisions_worth;
4052 if (difference == 0 && dir == RoundDownAlways) {
4053 /* right on the subdivision, but force-rounding down,
4054 so the difference is just the subdivision ticks */
4055 difference = ticks_one_subdivisions_worth;
4058 if (ticks < difference) {
4059 ticks = BBT_Time::ticks_per_beat - ticks;
4061 ticks -= difference;
4065 /* round to nearest */
4068 /* compute the distance to the previous and next subdivision */
4070 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
4072 /* closer to the next subdivision, so shift forward */
4074 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
4076 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
4078 if (ticks > BBT_Time::ticks_per_beat) {
4080 ticks -= BBT_Time::ticks_per_beat;
4081 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
4084 } else if (rem > 0) {
4086 /* closer to previous subdivision, so shift backward */
4090 /* can't go backwards past zero, so ... */
4091 return MusicFrame (0, 0);
4093 /* step back to previous beat */
4095 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4096 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4098 ticks = lrint (ticks - rem);
4099 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4102 /* on the subdivision, do nothing */
4106 MusicFrame ret (0, 0);
4107 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4108 ret.division = sub_num;
4114 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
4116 Glib::Threads::RWLock::ReaderLock lm (lock);
4117 const double minute = minute_at_frame (frame);
4118 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4119 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
4120 MusicFrame ret (0, 0);
4127 /* find bar previous to 'frame' */
4133 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4137 } else if (dir > 0) {
4138 /* find bar following 'frame' */
4143 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4147 /* true rounding: find nearest bar */
4148 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4151 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4153 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4155 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4156 ret.frame = next_ft;
4161 ret.frame = prev_ft;
4173 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
4176 } else if (dir > 0) {
4177 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
4181 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
4188 return MusicFrame (0, 0);
4192 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4193 framepos_t lower, framepos_t upper, uint32_t bar_mod)
4195 Glib::Threads::RWLock::ReaderLock lm (lock);
4196 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
4198 /* although the map handles negative beats, bbt doesn't. */
4203 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
4207 while (pos >= 0 && pos < upper) {
4208 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
4209 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4210 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4211 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4213 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4217 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
4222 bbt.bars -= bbt.bars % bar_mod;
4226 while (pos >= 0 && pos < upper) {
4227 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4228 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4229 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4230 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4231 bbt.bars += bar_mod;
4237 TempoMap::tempo_section_at_frame (framepos_t frame) const
4239 Glib::Threads::RWLock::ReaderLock lm (lock);
4241 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4245 TempoMap::tempo_section_at_frame (framepos_t frame)
4247 Glib::Threads::RWLock::ReaderLock lm (lock);
4249 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4253 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4255 TempoSection* prev = 0;
4259 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4261 if ((*i)->is_tempo()) {
4262 t = static_cast<TempoSection*> (*i);
4266 if (prev && t->minute() > minute) {
4276 abort(); /*NOTREACHED*/
4282 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4284 TempoSection* prev = 0;
4288 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4290 if ((*i)->is_tempo()) {
4291 t = static_cast<TempoSection*> (*i);
4295 if (prev && t->minute() > minute) {
4305 abort(); /*NOTREACHED*/
4311 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4313 TempoSection* prev_t = 0;
4314 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4318 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4319 if ((*i)->is_tempo()) {
4320 t = static_cast<TempoSection*> (*i);
4326 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4337 TempoMap::previous_tempo_section (TempoSection* ts) const
4339 Glib::Threads::RWLock::ReaderLock lm (lock);
4341 return previous_tempo_section_locked (_metrics, ts);
4346 TempoMap::previous_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4352 TempoSection* prev = 0;
4354 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4356 if ((*i)->is_tempo()) {
4357 TempoSection* t = static_cast<TempoSection*> (*i);
4363 if (prev && t == ts) {
4374 abort(); /*NOTREACHED*/
4381 TempoMap::next_tempo_section (TempoSection* ts) const
4383 Glib::Threads::RWLock::ReaderLock lm (lock);
4385 return next_tempo_section_locked (_metrics, ts);
4389 TempoMap::next_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4395 TempoSection* prev = 0;
4397 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4399 if ((*i)->is_tempo()) {
4400 TempoSection* t = static_cast<TempoSection*> (*i);
4406 if (prev && prev == ts) {
4417 abort(); /*NOTREACHED*/
4422 /* don't use this to calculate length (the tempo is only correct for this frame).
4423 do that stuff based on the beat_at_frame and frame_at_beat api
4426 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4428 Glib::Threads::RWLock::ReaderLock lm (lock);
4430 const TempoSection* ts_at = 0;
4431 const TempoSection* ts_after = 0;
4432 Metrics::const_iterator i;
4435 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4437 if ((*i)->is_tempo()) {
4438 t = static_cast<TempoSection*> (*i);
4442 if (ts_at && (*i)->frame() > frame) {
4452 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4454 /* must be treated as constant tempo */
4455 return ts_at->frames_per_quarter_note (_frame_rate);
4459 TempoMap::meter_section_at_frame (framepos_t frame) const
4461 Glib::Threads::RWLock::ReaderLock lm (lock);
4462 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4466 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4468 Metrics::const_iterator i;
4469 MeterSection* prev = 0;
4473 for (i = metrics.begin(); i != metrics.end(); ++i) {
4475 if (!(*i)->is_tempo()) {
4476 m = static_cast<MeterSection*> (*i);
4478 if (prev && (*i)->minute() > minute) {
4488 abort(); /*NOTREACHED*/
4495 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4497 MeterSection* prev_m = 0;
4499 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4501 if (!(*i)->is_tempo()) {
4502 m = static_cast<MeterSection*> (*i);
4503 if (prev_m && m->beat() > beat) {
4514 TempoMap::meter_section_at_beat (double beat) const
4516 Glib::Threads::RWLock::ReaderLock lm (lock);
4517 return meter_section_at_beat_locked (_metrics, beat);
4521 TempoMap::meter_at_frame (framepos_t frame) const
4523 TempoMetric m (metric_at (frame));
4528 TempoMap::fix_legacy_session ()
4530 MeterSection* prev_m = 0;
4531 TempoSection* prev_t = 0;
4532 bool have_initial_t = false;
4534 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4538 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4540 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4543 m->set_minute (0.0);
4544 m->set_position_lock_style (AudioTime);
4549 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4550 + (m->bbt().beats - 1)
4551 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4553 m->set_beat (start);
4554 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4555 + (m->bbt().beats - 1)
4556 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4557 m->set_pulse (start_beat / prev_m->note_divisor());
4560 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4568 t->set_minute (0.0);
4569 t->set_position_lock_style (AudioTime);
4571 have_initial_t = true;
4576 /* some 4.x sessions have no initial (non-movable) tempo. */
4577 if (!have_initial_t) {
4578 prev_t->set_pulse (0.0);
4579 prev_t->set_minute (0.0);
4580 prev_t->set_position_lock_style (AudioTime);
4581 prev_t->set_initial (true);
4582 prev_t->set_locked_to_meter (true);
4583 have_initial_t = true;
4586 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4587 + (t->legacy_bbt().beats - 1)
4588 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4590 t->set_pulse (beat / prev_m->note_divisor());
4592 /* really shouldn't happen but.. */
4593 t->set_pulse (beat / 4.0);
4601 TempoMap::fix_legacy_end_session ()
4603 TempoSection* prev_t = 0;
4605 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4608 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4615 if (prev_t->type() != TempoSection::Constant) {
4616 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4626 TempoMap::get_state ()
4628 Metrics::const_iterator i;
4629 XMLNode *root = new XMLNode ("TempoMap");
4632 Glib::Threads::RWLock::ReaderLock lm (lock);
4633 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4634 root->add_child_nocopy ((*i)->get_state());
4642 TempoMap::set_state (const XMLNode& node, int /*version*/)
4645 Glib::Threads::RWLock::WriterLock lm (lock);
4648 XMLNodeConstIterator niter;
4649 Metrics old_metrics (_metrics);
4652 nlist = node.children();
4654 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4655 XMLNode* child = *niter;
4657 if (child->name() == TempoSection::xml_state_node_name) {
4660 TempoSection* ts = new TempoSection (*child, _frame_rate);
4661 _metrics.push_back (ts);
4664 catch (failed_constructor& err){
4665 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4666 _metrics = old_metrics;
4667 old_metrics.clear();
4671 } else if (child->name() == MeterSection::xml_state_node_name) {
4674 MeterSection* ms = new MeterSection (*child, _frame_rate);
4675 _metrics.push_back (ms);
4678 catch (failed_constructor& err) {
4679 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4680 _metrics = old_metrics;
4681 old_metrics.clear();
4687 if (niter == nlist.end()) {
4688 MetricSectionSorter cmp;
4689 _metrics.sort (cmp);
4692 /* check for legacy sessions where bbt was the base musical unit for tempo */
4693 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4695 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4696 if (t->legacy_bbt().bars != 0) {
4697 fix_legacy_session();
4701 if (t->legacy_end()) {
4702 fix_legacy_end_session();
4710 /* check for multiple tempo/meters at the same location, which
4711 ardour2 somehow allowed.
4714 Metrics::iterator prev = _metrics.end();
4715 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4716 if (prev != _metrics.end()) {
4718 MeterSection* prev_m;
4720 TempoSection* prev_t;
4721 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4722 if (prev_m->pulse() == ms->pulse()) {
4723 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4724 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4727 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4728 if (prev_t->pulse() == ts->pulse()) {
4729 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4730 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4738 recompute_map (_metrics);
4740 Metrics::const_iterator d = old_metrics.begin();
4741 while (d != old_metrics.end()) {
4745 old_metrics.clear ();
4748 PropertyChanged (PropertyChange ());
4754 TempoMap::dump (std::ostream& o) const
4756 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4757 const MeterSection* m;
4758 const TempoSection* t;
4759 const TempoSection* prev_t = 0;
4761 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4763 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4764 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4765 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4766 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4767 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4769 o << " current start : " << t->note_types_per_minute()
4770 << " current end : " << t->end_note_types_per_minute()
4771 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4772 o << " previous : " << prev_t->note_types_per_minute()
4773 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4774 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4775 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4776 << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4777 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4780 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4781 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4782 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4783 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4786 o << "------" << std::endl;
4790 TempoMap::n_tempos() const
4792 Glib::Threads::RWLock::ReaderLock lm (lock);
4795 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4796 if ((*i)->is_tempo()) {
4805 TempoMap::n_meters() const
4807 Glib::Threads::RWLock::ReaderLock lm (lock);
4810 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4811 if (!(*i)->is_tempo()) {
4820 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4822 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4823 if ((*i)->frame() >= where && !(*i)->initial ()) {
4827 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4828 gui_set_meter_position (ms, (*i)->frame() + amount);
4831 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4832 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4837 PropertyChanged (PropertyChange ());
4841 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4845 std::list<MetricSection*> metric_kill_list;
4847 TempoSection* last_tempo = NULL;
4848 MeterSection* last_meter = NULL;
4849 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4850 bool meter_after = false; // is there a meter marker likewise?
4852 Glib::Threads::RWLock::WriterLock lm (lock);
4853 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4854 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4855 metric_kill_list.push_back(*i);
4856 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4859 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4863 else if ((*i)->frame() >= where) {
4864 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4865 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4866 if ((*i)->frame() == where) {
4867 // marker was immediately after end of range
4868 tempo_after = dynamic_cast<TempoSection*> (*i);
4869 meter_after = dynamic_cast<MeterSection*> (*i);
4875 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4876 if (last_tempo && !tempo_after) {
4877 metric_kill_list.remove(last_tempo);
4878 last_tempo->set_minute (minute_at_frame (where));
4881 if (last_meter && !meter_after) {
4882 metric_kill_list.remove(last_meter);
4883 last_meter->set_minute (minute_at_frame (where));
4887 //remove all the remaining metrics
4888 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4889 _metrics.remove(*i);
4894 recompute_map (_metrics);
4897 PropertyChanged (PropertyChange ());
4901 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4902 * pos can be -ve, if required.
4905 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4907 Glib::Threads::RWLock::ReaderLock lm (lock);
4908 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4910 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4914 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4916 Glib::Threads::RWLock::ReaderLock lm (lock);
4918 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4919 pos_bbt.ticks += op.ticks;
4920 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4922 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4924 pos_bbt.beats += op.beats;
4925 /* the meter in effect will start on the bar */
4926 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();
4927 while (pos_bbt.beats >= divisions_per_bar + 1) {
4929 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4930 pos_bbt.beats -= divisions_per_bar;
4932 pos_bbt.bars += op.bars;
4934 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4937 /** Count the number of beats that are equivalent to distance when going forward,
4941 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4943 Glib::Threads::RWLock::ReaderLock lm (lock);
4945 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4949 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4955 operator<< (std::ostream& o, const Meter& m) {
4956 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4960 operator<< (std::ostream& o, const Tempo& t) {
4961 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4965 operator<< (std::ostream& o, const MetricSection& section) {
4967 o << "MetricSection @ " << section.frame() << ' ';
4969 const TempoSection* ts;
4970 const MeterSection* ms;
4972 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4973 o << *((const Tempo*) ts);
4974 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4975 o << *((const Meter*) ms);