2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0, 4.0, 120.0);
51 MetricSection::frame_at_minute (const double& time) const
53 return (framepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
57 MetricSection::minute_at_frame (const framepos_t& frame) const
59 return (frame / (double) _sample_rate) / 60.0;
62 /***********************************************************************/
65 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
67 /* This is tempo- and meter-sensitive. The number it returns
68 is based on the interval between any two lines in the
69 grid that is constructed from tempo and meter sections.
71 The return value IS NOT interpretable in terms of "beats".
74 return (60.0 * sr) / (tempo.note_types_per_minute() * (_note_type / tempo.note_type()));
78 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
80 return frames_per_grid (tempo, sr) * _divisions_per_bar;
83 /***********************************************************************/
85 const string TempoSection::xml_state_node_name = "Tempo";
87 TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate)
88 : MetricSection (0.0, 0, MusicTime, true, sample_rate)
89 , Tempo (TempoMap::default_tempo())
92 , _locked_to_meter (false)
96 XMLProperty const * prop;
102 _legacy_bbt = BBT_Time (0, 0, 0);
104 if ((prop = node.property ("start")) != 0) {
105 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
109 /* legacy session - start used to be in bbt*/
112 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
116 if ((prop = node.property ("pulse")) != 0) {
117 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
118 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
124 if ((prop = node.property ("frame")) != 0) {
125 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
126 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
127 throw failed_constructor();
129 set_minute (minute_at_frame (frame));
133 /* XX replace old beats-per-minute name with note-types-per-minute */
134 if ((prop = node.property ("beats-per-minute")) != 0) {
135 if (sscanf (prop->value().c_str(), "%lf", &_note_types_per_minute) != 1 || _note_types_per_minute < 0.0) {
136 error << _("TempoSection XML node has an illegal \"beats-per-minute\" value") << endmsg;
137 throw failed_constructor();
141 if ((prop = node.property ("note-type")) == 0) {
142 /* older session, make note type be quarter by default */
145 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
146 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
147 throw failed_constructor();
151 if ((prop = node.property ("clamped")) == 0) {
152 warning << _("TempoSection XML node has no \"clamped\" property") << endmsg;
155 set_clamped (string_is_affirmative (prop->value()));
158 /* XX replace old end-beats-per-minute name with note-types-per-minute */
159 if ((prop = node.property ("end-beats-per-minute")) != 0) {
160 if (sscanf (prop->value().c_str(), "%lf", &_end_note_types_per_minute) != 1 || _end_note_types_per_minute < 0.0) {
161 info << _("TempoSection XML node has an illegal \"in-beats-per-minute\" value") << endmsg;
162 //throw failed_constructor();
163 _end_note_types_per_minute = _note_types_per_minute;
170 if ((prop = node.property ("tempo-type")) != 0) {
171 TempoSection::Type old_type;
173 old_type = Type (string_2_enum (prop->value(), old_type));
174 if (old_type == TempoSection::Constant) {
175 _end_note_types_per_minute = _note_types_per_minute;
179 if ((prop = node.property ("movable")) == 0) {
180 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
181 throw failed_constructor();
184 set_initial (!string_is_affirmative (prop->value()));
186 if ((prop = node.property ("active")) == 0) {
187 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
190 set_active (string_is_affirmative (prop->value()));
193 if ((prop = node.property ("lock-style")) == 0) {
195 set_position_lock_style (MusicTime);
197 set_position_lock_style (AudioTime);
200 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
203 if ((prop = node.property ("locked-to-meter")) == 0) {
205 set_locked_to_meter (true);
207 set_locked_to_meter (false);
210 set_locked_to_meter (string_is_affirmative (prop->value()));
213 /* 5.5 marked initial tempo as not locked to meter. this should always be true anyway */
215 set_locked_to_meter (true);
220 TempoSection::get_state() const
222 XMLNode *root = new XMLNode (xml_state_node_name);
226 snprintf (buf, sizeof (buf), "%lf", pulse());
227 root->add_property ("pulse", buf);
228 snprintf (buf, sizeof (buf), "%li", frame());
229 root->add_property ("frame", buf);
230 snprintf (buf, sizeof (buf), "%lf", _note_types_per_minute);
231 root->add_property ("beats-per-minute", buf);
232 snprintf (buf, sizeof (buf), "%lf", _note_type);
233 root->add_property ("note-type", buf);
234 snprintf (buf, sizeof (buf), "%s", _clamped?"yes":"no");
235 root->add_property ("clamped", buf);
236 snprintf (buf, sizeof (buf), "%lf", _end_note_types_per_minute);
237 root->add_property ("end-beats-per-minute", buf);
238 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
239 root->add_property ("movable", buf);
240 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
241 root->add_property ("active", buf);
242 root->add_property ("lock-style", enum_2_string (position_lock_style()));
243 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
248 /** returns the Tempo at the session-relative minute.
251 TempoSection::tempo_at_minute (const double& m) const
253 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
255 return Tempo (note_types_per_minute(), note_type());
258 return Tempo (_tempo_at_time (m - minute()), _note_type, _end_note_types_per_minute);
261 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
262 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
263 * @param p the pulse used to calculate the returned minute for constant tempi
264 * @return the minute at the supplied tempo
266 * note that the note_type is currently ignored in this function. see below.
270 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
271 * there should be no ramp between the two even if we are ramped.
272 * in other words a ramp should only place a curve on note_types_per_minute.
273 * we should be able to use Tempo note type here, but the above
274 * complicates things a bit.
277 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
279 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
281 return ((p - pulse()) / pulses_per_minute()) + minute();
284 return _time_at_tempo (ntpm) + minute();
287 /** returns the Tempo at the supplied whole-note pulse.
290 TempoSection::tempo_at_pulse (const double& p) const
292 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
295 return Tempo (note_types_per_minute(), note_type());
298 return Tempo (_tempo_at_pulse (p - pulse()), _note_type, _end_note_types_per_minute);
301 /** returns the whole-note pulse where a tempo in note types per minute occurs.
302 * constant tempi require minute m.
303 * @param ntpm the note types per minute value used to calculate the returned pulse
304 * @param m the minute used to calculate the returned pulse if the tempo is constant
305 * @return the whole-note pulse at the supplied tempo
307 * note that note_type is currently ignored in this function. see minute_at_tempo().
309 * for constant tempi, this is anaologous to pulse_at_minute().
312 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
314 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
316 return ((m - minute()) * pulses_per_minute()) + pulse();
319 return _pulse_at_tempo (ntpm) + pulse();
322 /** returns the whole-note pulse at the supplied session-relative minute.
325 TempoSection::pulse_at_minute (const double& m) const
327 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
329 return ((m - minute()) * pulses_per_minute()) + pulse();
332 return _pulse_at_time (m - minute()) + pulse();
335 /** returns the session-relative minute at the supplied whole-note pulse.
338 TempoSection::minute_at_pulse (const double& p) const
340 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
342 return ((p - pulse()) / pulses_per_minute()) + minute();
345 return _time_at_pulse (p - pulse()) + minute();
348 /** returns thw whole-note pulse at session frame position f.
349 * @param f the frame position.
350 * @return the position in whole-note pulses corresponding to f
352 * for use with musical units whose granularity is coarser than frames (e.g. ticks)
355 TempoSection::pulse_at_frame (const framepos_t& f) const
357 const bool constant = type() == Constant || _c == 0.0 || (initial() && f < frame());
359 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
362 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
366 TempoSection::frame_at_pulse (const double& p) const
368 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
370 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
373 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
381 Tt----|-----------------*|
382 Ta----|--------------|* |
388 _______________|___|____
389 time a t (next tempo)
392 Duration in beats at time a is the integral of some Tempo function.
393 In our case, the Tempo function (Tempo at time t) is
396 with function constant
401 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
402 b(t) = T0(e^(ct) - 1) / c
404 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
405 t(b) = log((c.b / T0) + 1) / c
407 The time t at which Tempo T occurs is a as above:
408 t(T) = log(T / T0) / c
410 The beat at which a Tempo T occurs is:
413 The Tempo at which beat b occurs is:
416 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
417 Our problem is that we usually don't know t.
418 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
419 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
420 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
422 By substituting our expanded t as a in the c function above, our problem is reduced to:
423 c = T0 (e^(log (Ta / T0)) - 1) / b
425 Of course the word 'beat' has been left loosely defined above.
426 In music, a beat is defined by the musical pulse (which comes from the tempo)
427 and the meter in use at a particular time (how many pulse divisions there are in one bar).
428 It would be more accurate to substitute the work 'pulse' for 'beat' above.
432 We can now store c for future time calculations.
433 If the following tempo section (the one that defines c in conjunction with this one)
434 is changed or moved, c is no longer valid.
436 The public methods are session-relative.
438 Most of this stuff is taken from this paper:
441 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
444 Zurich University of Arts
445 Institute for Computer Music and Sound Technology
447 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
451 /** compute this ramp's function constant from some tempo-pulse point
452 * @param end_npm end tempo (in note types per minute)
453 * @param end_pulse duration (pulses into global start) of some other position.
454 * @return the calculated function constant
457 TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const
459 if (note_types_per_minute() == end_npm || type() == Constant) {
463 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
464 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
467 /** compute the function constant from some tempo-time point.
468 * @param end_npm tempo (note types/min.)
469 * @param end_minute distance (in minutes) from session origin
470 * @return the calculated function constant
473 TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const
475 if (note_types_per_minute() == end_npm || type() == Constant) {
479 return c_func (end_npm, end_minute - minute());
482 /* position function */
484 TempoSection::a_func (double end_npm, double c) const
486 return log (end_npm / note_types_per_minute()) / c;
489 /*function constant*/
491 TempoSection::c_func (double end_npm, double end_time) const
493 return log (end_npm / note_types_per_minute()) / end_time;
496 /* tempo in note types per minute at time in minutes */
498 TempoSection::_tempo_at_time (const double& time) const
500 return exp (_c * time) * note_types_per_minute();
503 /* time in minutes at tempo in note types per minute */
505 TempoSection::_time_at_tempo (const double& npm) const
507 return log (npm / note_types_per_minute()) / _c;
510 /* pulse at tempo in note types per minute */
512 TempoSection::_pulse_at_tempo (const double& npm) const
514 return ((npm - note_types_per_minute()) / _c) / _note_type;
517 /* tempo in note types per minute at pulse */
519 TempoSection::_tempo_at_pulse (const double& pulse) const
521 return (pulse * _note_type * _c) + note_types_per_minute();
524 /* pulse at time in minutes */
526 TempoSection::_pulse_at_time (const double& time) const
528 return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
531 /* time in minutes at pulse */
533 TempoSection::_time_at_pulse (const double& pulse) const
535 return log1p ((_c * pulse * _note_type) / note_types_per_minute()) / _c;
538 /***********************************************************************/
540 const string MeterSection::xml_state_node_name = "Meter";
542 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
543 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
545 XMLProperty const * prop;
550 framepos_t frame = 0;
551 pair<double, BBT_Time> start;
553 if ((prop = node.property ("start")) != 0) {
554 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
558 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
560 /* legacy session - start used to be in bbt*/
561 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
566 if ((prop = node.property ("pulse")) != 0) {
567 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
568 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
573 if ((prop = node.property ("beat")) != 0) {
574 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
575 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
581 if ((prop = node.property ("bbt")) == 0) {
582 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
583 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
587 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
588 throw failed_constructor();
594 if ((prop = node.property ("frame")) != 0) {
595 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
596 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
597 throw failed_constructor();
599 set_minute (minute_at_frame (frame));
603 /* beats-per-bar is old; divisions-per-bar is new */
605 if ((prop = node.property ("divisions-per-bar")) == 0) {
606 if ((prop = node.property ("beats-per-bar")) == 0) {
607 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
608 throw failed_constructor();
611 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
612 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
613 throw failed_constructor();
616 if ((prop = node.property ("note-type")) == 0) {
617 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
618 throw failed_constructor();
620 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
621 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
622 throw failed_constructor();
625 if ((prop = node.property ("movable")) == 0) {
626 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
627 throw failed_constructor();
630 set_initial (!string_is_affirmative (prop->value()));
632 if ((prop = node.property ("lock-style")) == 0) {
633 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
635 set_position_lock_style (MusicTime);
637 set_position_lock_style (AudioTime);
640 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
645 MeterSection::get_state() const
647 XMLNode *root = new XMLNode (xml_state_node_name);
651 snprintf (buf, sizeof (buf), "%lf", pulse());
652 root->add_property ("pulse", buf);
653 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
657 root->add_property ("bbt", buf);
658 snprintf (buf, sizeof (buf), "%lf", beat());
659 root->add_property ("beat", buf);
660 snprintf (buf, sizeof (buf), "%lf", _note_type);
661 root->add_property ("note-type", buf);
662 snprintf (buf, sizeof (buf), "%li", frame());
663 root->add_property ("frame", buf);
664 root->add_property ("lock-style", enum_2_string (position_lock_style()));
665 snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
666 root->add_property ("divisions-per-bar", buf);
667 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
668 root->add_property ("movable", buf);
673 /***********************************************************************/
677 Tempo determines the rate of musical pulse determined by its components
678 note types per minute - the rate per minute of the whole note divisor _note_type
679 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
680 Meter divides the musical pulse into measures and beats according to its components
684 TempoSection - translates between time, musical pulse and tempo.
685 has a musical location in whole notes (pulses).
686 has a time location in minutes.
687 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
688 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
690 MeterSection - translates between BBT, meter-based beat and musical pulse.
691 has a musical location in whole notes (pulses)
692 has a musical location in meter-based beats
693 has a musical location in BBT time
694 has a time location expressed in minutes.
696 TempoSection and MeterSection may be locked to either audio or music (position lock style).
697 The lock style determines the location type to be kept as a reference when location is recalculated.
699 The first tempo and meter are special. they must move together, and are locked to audio.
700 Audio locked tempi which lie before the first meter are made inactive.
702 Recomputing the map is the process where the 'missing' location types are calculated.
703 We construct the tempo map by first using the locked location type of each section
704 to determine non-locked location types (pulse or minute position).
705 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
707 Having done this, we can now traverse the Metrics list by pulse or minute
708 to query its relevant meter/tempo.
710 It is important to keep the _metrics in an order that makes sense.
711 Because ramped MusicTime and AudioTime tempos can interact with each other,
712 reordering is frequent. Care must be taken to keep _metrics in a solved state.
713 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
717 Music and audio-locked objects may seem interchangeable on the surface, but when translating
718 between audio samples and beat, remember that a sample is only a quantised approximation
719 of the actual time (in minutes) of a beat.
720 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
721 mean that this frame is the actual location in time of 1|3|0.
723 You cannot use a frame measurement to determine beat distance except under special circumstances
724 (e.g. where the user has requested that a beat lie on a SMPTE frame or if the tempo is known to be constant over the duration).
726 This means is that a user operating on a musical grid must supply the desired beat position and/or current beat quantization in order for the
727 sample space the user is operating at to be translated correctly to the object.
729 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
730 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
731 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
733 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
735 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
736 The result is rounded to audio frames.
737 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
740 frame_at_beat (beat_at_frame (frame)) == frame
742 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
744 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
745 frames_between_quarter_notes () eliminats this effect when determining time duration
746 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
748 The above pointless example could instead do:
749 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
751 The Shaggs - Things I Wonder
752 https://www.youtube.com/watch?v=9wQK6zMJOoQ
755 struct MetricSectionSorter {
756 bool operator() (const MetricSection* a, const MetricSection* b) {
757 return a->pulse() < b->pulse();
761 struct MetricSectionFrameSorter {
762 bool operator() (const MetricSection* a, const MetricSection* b) {
763 return a->frame() < b->frame();
767 TempoMap::TempoMap (framecnt_t fr)
770 BBT_Time start (1, 1, 0);
772 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo, AudioTime, fr);
773 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
775 t->set_initial (true);
776 t->set_locked_to_meter (true);
778 m->set_initial (true);
780 /* note: frame time is correct (zero) for both of these */
782 _metrics.push_back (t);
783 _metrics.push_back (m);
787 TempoMap::TempoMap (TempoMap const & other)
789 _frame_rate = other._frame_rate;
790 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
791 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
792 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
795 TempoSection* new_section = new TempoSection (*ts);
796 _metrics.push_back (new_section);
798 MeterSection* new_section = new MeterSection (*ms);
799 _metrics.push_back (new_section);
805 TempoMap::operator= (TempoMap const & other)
807 if (&other != this) {
808 _frame_rate = other._frame_rate;
810 Metrics::const_iterator d = _metrics.begin();
811 while (d != _metrics.end()) {
817 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
818 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
819 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
822 TempoSection* new_section = new TempoSection (*ts);
823 _metrics.push_back (new_section);
825 MeterSection* new_section = new MeterSection (*ms);
826 _metrics.push_back (new_section);
831 PropertyChanged (PropertyChange());
836 TempoMap::~TempoMap ()
838 Metrics::const_iterator d = _metrics.begin();
839 while (d != _metrics.end()) {
847 TempoMap::frame_at_minute (const double time) const
849 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
853 TempoMap::minute_at_frame (const framepos_t frame) const
855 return (frame / (double) _frame_rate) / 60.0;
859 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
861 bool removed = false;
864 Glib::Threads::RWLock::WriterLock lm (lock);
865 if ((removed = remove_tempo_locked (tempo))) {
866 if (complete_operation) {
867 recompute_map (_metrics);
872 if (removed && complete_operation) {
873 PropertyChanged (PropertyChange ());
878 TempoMap::remove_tempo_locked (const TempoSection& tempo)
882 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
883 if (dynamic_cast<TempoSection*> (*i) != 0) {
884 if (tempo.frame() == (*i)->frame()) {
885 if (!(*i)->initial()) {
898 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
900 bool removed = false;
903 Glib::Threads::RWLock::WriterLock lm (lock);
904 if ((removed = remove_meter_locked (tempo))) {
905 if (complete_operation) {
906 recompute_map (_metrics);
911 if (removed && complete_operation) {
912 PropertyChanged (PropertyChange ());
917 TempoMap::remove_meter_locked (const MeterSection& meter)
920 if (meter.position_lock_style() == AudioTime) {
921 /* remove meter-locked tempo */
922 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
924 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
925 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
934 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
935 if (dynamic_cast<MeterSection*> (*i) != 0) {
936 if (meter.frame() == (*i)->frame()) {
937 if (!(*i)->initial()) {
950 TempoMap::do_insert (MetricSection* section)
952 bool need_add = true;
953 /* we only allow new meters to be inserted on beat 1 of an existing
957 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
959 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
961 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
962 corrected.second.beats = 1;
963 corrected.second.ticks = 0;
964 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
965 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
966 m->bbt(), corrected.second) << endmsg;
967 //m->set_pulse (corrected);
971 /* Look for any existing MetricSection that is of the same type and
972 in the same bar as the new one, and remove it before adding
973 the new one. Note that this means that if we find a matching,
974 existing section, we can break out of the loop since we're
975 guaranteed that there is only one such match.
978 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
980 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
981 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
982 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
983 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
985 if (tempo && insert_tempo) {
988 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
989 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
991 if (tempo->initial()) {
993 /* can't (re)move this section, so overwrite
994 * its data content (but not its properties as
998 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
999 (*i)->set_position_lock_style (AudioTime);
1008 } else if (meter && insert_meter) {
1010 /* Meter Sections */
1012 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1014 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
1016 if (meter->initial()) {
1018 /* can't (re)move this section, so overwrite
1019 * its data content (but not its properties as
1023 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
1024 (*i)->set_position_lock_style (AudioTime);
1034 /* non-matching types, so we don't care */
1038 /* Add the given MetricSection, if we didn't just reset an existing
1043 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
1044 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
1045 Metrics::iterator i;
1048 TempoSection* prev_t = 0;
1050 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1051 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
1052 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1055 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
1059 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
1063 prev_t = dynamic_cast<TempoSection*> (*i);
1066 } else if (insert_tempo) {
1067 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1068 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1071 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1072 const bool lm = insert_tempo->locked_to_meter();
1073 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1074 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1081 _metrics.insert (i, section);
1085 /* user supplies the exact pulse if pls == MusicTime */
1087 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1089 if (tempo.note_types_per_minute() <= 0.0) {
1090 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1094 TempoSection* ts = 0;
1095 TempoSection* prev_tempo = 0;
1097 Glib::Threads::RWLock::WriterLock lm (lock);
1098 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true);
1099 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1101 if ((*i)->is_tempo()) {
1102 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1104 bool const ipm = ts->position_lock_style() == MusicTime;
1105 bool const lm = ts->locked_to_meter();
1106 if ((ipm && this_t->pulse() == ts->pulse()) || (!ipm && this_t->frame() == ts->frame())
1107 || (lm && this_t->pulse() == ts->pulse())) {
1108 if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1109 prev_tempo->set_end_note_types_per_minute (ts->note_types_per_minute());
1113 prev_tempo = this_t;
1116 recompute_map (_metrics);
1119 PropertyChanged (PropertyChange ());
1125 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1127 if (tempo.note_types_per_minute() <= 0.0) {
1128 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1132 bool const locked_to_meter = ts.locked_to_meter();
1133 bool const ts_clamped = ts.clamped();
1134 TempoSection* new_ts = 0;
1137 Glib::Threads::RWLock::WriterLock lm (lock);
1138 TempoSection& first (first_tempo());
1139 if (!ts.initial()) {
1140 if (locked_to_meter) {
1142 /* cannot move a meter-locked tempo section */
1143 *static_cast<Tempo*>(&ts) = tempo;
1144 recompute_map (_metrics);
1147 remove_tempo_locked (ts);
1148 new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter);
1149 new_ts->set_clamped (ts_clamped);
1151 if (new_ts && new_ts->type() == TempoSection::Constant) {
1152 new_ts->set_end_note_types_per_minute (new_ts->note_types_per_minute());
1154 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1156 if ((*i)->is_tempo()) {
1157 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1159 bool const ipm = new_ts->position_lock_style() == MusicTime;
1160 bool const lm = new_ts->locked_to_meter();
1161 if ((ipm && this_t->pulse() > new_ts->pulse()) || (!ipm && this_t->frame() > new_ts->frame())
1162 || (lm && this_t->pulse() > new_ts->pulse())) {
1163 new_ts->set_end_note_types_per_minute (tempo.end_note_types_per_minute());
1173 first.set_pulse (0.0);
1174 first.set_minute (minute_at_frame (frame));
1175 first.set_position_lock_style (AudioTime);
1176 first.set_locked_to_meter (true);
1177 first.set_clamped (ts_clamped);
1179 /* cannot move the first tempo section */
1180 *static_cast<Tempo*>(&first) = tempo;
1183 recompute_map (_metrics);
1186 PropertyChanged (PropertyChange ());
1190 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1191 , PositionLockStyle pls, bool recompute, bool locked_to_meter)
1193 TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _frame_rate);
1194 t->set_locked_to_meter (locked_to_meter);
1199 if (pls == AudioTime) {
1200 solve_map_minute (_metrics, t, t->minute());
1202 solve_map_pulse (_metrics, t, t->pulse());
1204 recompute_meters (_metrics);
1211 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1213 MeterSection* m = 0;
1215 Glib::Threads::RWLock::WriterLock lm (lock);
1216 m = add_meter_locked (meter, beat, where, frame, pls, true);
1221 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1226 PropertyChanged (PropertyChange ());
1231 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1234 Glib::Threads::RWLock::WriterLock lm (lock);
1235 const double beat = beat_at_bbt_locked (_metrics, where);
1237 if (!ms.initial()) {
1238 remove_meter_locked (ms);
1239 add_meter_locked (meter, beat, where, frame, pls, true);
1241 MeterSection& first (first_meter());
1242 TempoSection& first_t (first_tempo());
1243 /* cannot move the first meter section */
1244 *static_cast<Meter*>(&first) = meter;
1245 first.set_position_lock_style (AudioTime);
1246 first.set_pulse (0.0);
1247 first.set_minute (minute_at_frame (frame));
1248 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1249 first.set_beat (beat);
1250 first_t.set_minute (first.minute());
1251 first_t.set_locked_to_meter (true);
1252 first_t.set_pulse (0.0);
1253 first_t.set_position_lock_style (AudioTime);
1254 recompute_map (_metrics);
1258 PropertyChanged (PropertyChange ());
1262 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1264 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1265 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1266 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1267 TempoSection* mlt = 0;
1269 if (pls == AudioTime) {
1270 /* add meter-locked tempo */
1271 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), AudioTime, true, true);
1279 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1281 bool solved = false;
1283 do_insert (new_meter);
1287 if (pls == AudioTime) {
1288 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1289 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1290 fudge frame so that the meter ends up at its BBT position instead.
1293 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1296 solved = solve_map_bbt (_metrics, new_meter, where);
1297 /* required due to resetting the pulse of meter-locked tempi above.
1298 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1299 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1301 recompute_map (_metrics);
1305 if (!solved && recompute) {
1306 /* if this has failed to solve, there is little we can do other than to ensure that
1307 the new map is recalculated.
1309 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1310 recompute_map (_metrics);
1317 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1319 Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1322 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1323 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1328 Glib::Threads::RWLock::WriterLock lm (lock);
1329 *((Tempo*) t) = newtempo;
1330 recompute_map (_metrics);
1332 PropertyChanged (PropertyChange ());
1339 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1341 Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1344 TempoSection* first;
1345 Metrics::iterator i;
1347 /* find the TempoSection immediately preceding "where"
1350 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1352 if ((*i)->frame() > where) {
1358 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1371 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1381 Glib::Threads::RWLock::WriterLock lm (lock);
1382 /* cannot move the first tempo section */
1383 *((Tempo*)prev) = newtempo;
1384 recompute_map (_metrics);
1387 PropertyChanged (PropertyChange ());
1391 TempoMap::first_meter () const
1393 const MeterSection *m = 0;
1395 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1396 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1401 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1402 abort(); /*NOTREACHED*/
1407 TempoMap::first_meter ()
1409 MeterSection *m = 0;
1411 /* CALLER MUST HOLD LOCK */
1413 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1414 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1419 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1420 abort(); /*NOTREACHED*/
1425 TempoMap::first_tempo () const
1427 const TempoSection *t = 0;
1429 /* CALLER MUST HOLD LOCK */
1431 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1432 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1442 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1443 abort(); /*NOTREACHED*/
1448 TempoMap::first_tempo ()
1450 TempoSection *t = 0;
1452 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1453 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1463 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1464 abort(); /*NOTREACHED*/
1468 TempoMap::recompute_tempi (Metrics& metrics)
1470 TempoSection* prev_t = 0;
1472 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1475 if ((*i)->is_tempo()) {
1476 t = static_cast<TempoSection*> (*i);
1488 if (t->position_lock_style() == AudioTime) {
1489 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1490 if (!t->locked_to_meter()) {
1491 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1495 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1496 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1504 prev_t->set_c (0.0);
1507 /* tempos must be positioned correctly.
1508 the current approach is to use a meter's bbt time as its base position unit.
1509 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1510 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1513 TempoMap::recompute_meters (Metrics& metrics)
1515 MeterSection* meter = 0;
1516 MeterSection* prev_m = 0;
1518 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1519 if (!(*mi)->is_tempo()) {
1520 meter = static_cast<MeterSection*> (*mi);
1521 if (meter->position_lock_style() == AudioTime) {
1523 pair<double, BBT_Time> b_bbt;
1524 TempoSection* meter_locked_tempo = 0;
1525 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1527 if ((*ii)->is_tempo()) {
1528 t = static_cast<TempoSection*> (*ii);
1529 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1530 meter_locked_tempo = t;
1537 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1538 if (beats + prev_m->beat() != meter->beat()) {
1539 /* reordering caused a bbt change */
1541 beats = meter->beat() - prev_m->beat();
1542 b_bbt = make_pair (beats + prev_m->beat()
1543 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1544 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1546 } else if (!meter->initial()) {
1547 b_bbt = make_pair (meter->beat(), meter->bbt());
1548 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1551 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1553 if (meter_locked_tempo) {
1554 meter_locked_tempo->set_pulse (pulse);
1556 meter->set_beat (b_bbt);
1557 meter->set_pulse (pulse);
1562 pair<double, BBT_Time> b_bbt;
1564 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1565 if (beats + prev_m->beat() != meter->beat()) {
1566 /* reordering caused a bbt change */
1567 b_bbt = make_pair (beats + prev_m->beat()
1568 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1570 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1572 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1574 /* shouldn't happen - the first is audio-locked */
1575 pulse = pulse_at_beat_locked (metrics, meter->beat());
1576 b_bbt = make_pair (meter->beat(), meter->bbt());
1579 meter->set_beat (b_bbt);
1580 meter->set_pulse (pulse);
1581 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1590 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1592 /* CALLER MUST HOLD WRITE LOCK */
1594 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1597 /* silly call from Session::process() during startup
1602 recompute_tempi (metrics);
1603 recompute_meters (metrics);
1607 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1609 Glib::Threads::RWLock::ReaderLock lm (lock);
1610 TempoMetric m (first_meter(), first_tempo());
1613 *last = ++_metrics.begin();
1616 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1617 at something, because we insert the default tempo and meter during
1618 TempoMap construction.
1620 now see if we can find better candidates.
1623 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1625 if ((*i)->frame() > frame) {
1639 /* XX meters only */
1641 TempoMap::metric_at (BBT_Time bbt) const
1643 Glib::Threads::RWLock::ReaderLock lm (lock);
1644 TempoMetric m (first_meter(), first_tempo());
1646 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1647 at something, because we insert the default tempo and meter during
1648 TempoMap construction.
1650 now see if we can find better candidates.
1653 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1655 if (!(*i)->is_tempo()) {
1656 mw = static_cast<MeterSection*> (*i);
1657 BBT_Time section_start (mw->bbt());
1659 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1670 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1671 * @param frame The session frame position.
1672 * @return The beat duration according to the tempo map at the supplied frame.
1674 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1675 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1677 * This function uses both tempo and meter.
1680 TempoMap::beat_at_frame (const framecnt_t& frame) const
1682 Glib::Threads::RWLock::ReaderLock lm (lock);
1684 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1687 /* This function uses both tempo and meter.*/
1689 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1691 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1692 MeterSection* prev_m = 0;
1693 MeterSection* next_m = 0;
1695 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1696 if (!(*i)->is_tempo()) {
1697 if (prev_m && (*i)->minute() > minute) {
1698 next_m = static_cast<MeterSection*> (*i);
1701 prev_m = static_cast<MeterSection*> (*i);
1705 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1707 /* audio locked meters fake their beat */
1708 if (next_m && next_m->beat() < beat) {
1709 return next_m->beat();
1715 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1716 * @param beat The BBT (meter-based) beat.
1717 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1719 * This function uses both tempo and meter.
1722 TempoMap::frame_at_beat (const double& beat) const
1724 Glib::Threads::RWLock::ReaderLock lm (lock);
1726 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1729 /* meter & tempo section based */
1731 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1733 MeterSection* prev_m = 0;
1734 TempoSection* prev_t = 0;
1738 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1739 if (!(*i)->is_tempo()) {
1740 m = static_cast<MeterSection*> (*i);
1741 if (prev_m && m->beat() > beat) {
1751 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1752 if ((*i)->is_tempo()) {
1753 t = static_cast<TempoSection*> (*i);
1759 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1768 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1771 /** Returns a Tempo corresponding to the supplied frame position.
1772 * @param frame The audio frame.
1773 * @return a Tempo according to the tempo map at the supplied frame.
1777 TempoMap::tempo_at_frame (const framepos_t& frame) const
1779 Glib::Threads::RWLock::ReaderLock lm (lock);
1781 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1785 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1787 TempoSection* prev_t = 0;
1791 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1792 if ((*i)->is_tempo()) {
1793 t = static_cast<TempoSection*> (*i);
1797 if ((prev_t) && t->minute() > minute) {
1798 /* t is the section past frame */
1799 return prev_t->tempo_at_minute (minute);
1805 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1808 /** returns the frame at which the supplied tempo occurs, or
1809 * the frame of the last tempo section (search exhausted)
1810 * only the position of the first occurence will be returned
1814 TempoMap::frame_at_tempo (const Tempo& tempo) const
1816 Glib::Threads::RWLock::ReaderLock lm (lock);
1818 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1822 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1824 TempoSection* prev_t = 0;
1825 const double tempo_bpm = tempo.note_types_per_minute();
1827 Metrics::const_iterator i;
1829 for (i = metrics.begin(); i != metrics.end(); ++i) {
1831 if ((*i)->is_tempo()) {
1832 t = static_cast<TempoSection*> (*i);
1840 if (t->note_types_per_minute() == tempo_bpm) {
1845 const double prev_t_bpm = prev_t->note_types_per_minute();
1846 const double prev_t_end_bpm = prev_t->end_note_types_per_minute();
1847 if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm)
1848 || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm)
1849 || (prev_t_end_bpm == tempo_bpm)) {
1851 return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
1858 return prev_t->minute();
1862 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1864 TempoSection* prev_t = 0;
1868 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1869 if ((*i)->is_tempo()) {
1870 t = static_cast<TempoSection*> (*i);
1874 if ((prev_t) && t->pulse() > pulse) {
1875 /* t is the section past frame */
1876 return prev_t->tempo_at_pulse (pulse);
1882 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1886 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1888 TempoSection* prev_t = 0;
1889 const double tempo_bpm = tempo.note_types_per_minute();
1891 Metrics::const_iterator i;
1893 for (i = metrics.begin(); i != metrics.end(); ++i) {
1895 if ((*i)->is_tempo()) {
1896 t = static_cast<TempoSection*> (*i);
1902 const double t_bpm = t->note_types_per_minute();
1904 if (t_bpm == tempo_bpm) {
1909 const double prev_t_bpm = prev_t->note_types_per_minute();
1911 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1912 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1919 return prev_t->pulse();
1922 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1923 * @param qn the position in quarter note beats.
1924 * @return the Tempo at the supplied quarter-note.
1927 TempoMap::tempo_at_quarter_note (const double& qn) const
1929 Glib::Threads::RWLock::ReaderLock lm (lock);
1931 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1934 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1935 * @param tempo the tempo.
1936 * @return the position in quarter-note beats where the map bpm
1937 * is equal to that of the Tempo. currently ignores note_type.
1940 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1942 Glib::Threads::RWLock::ReaderLock lm (lock);
1944 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1947 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1948 * @param metrics the list of metric sections used to calculate the pulse.
1949 * @param beat The BBT (meter-based) beat.
1950 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1952 * a pulse or whole note is the base musical position of a MetricSection.
1953 * it is equivalent to four quarter notes.
1957 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1959 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1961 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1964 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1965 * @param metrics the list of metric sections used to calculate the beat.
1966 * @param pulse the whole-note pulse.
1967 * @return the meter-based beat at the supplied whole-note pulse.
1969 * a pulse or whole note is the base musical position of a MetricSection.
1970 * it is equivalent to four quarter notes.
1973 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1975 MeterSection* prev_m = 0;
1977 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1979 if (!(*i)->is_tempo()) {
1980 m = static_cast<MeterSection*> (*i);
1981 if (prev_m && m->pulse() > pulse) {
1989 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1993 /* tempo section based */
1995 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1997 /* HOLD (at least) THE READER LOCK */
1998 TempoSection* prev_t = 0;
2000 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2002 if ((*i)->is_tempo()) {
2003 t = static_cast<TempoSection*> (*i);
2007 if (prev_t && t->minute() > minute) {
2008 /*the previous ts is the one containing the frame */
2009 const double ret = prev_t->pulse_at_minute (minute);
2010 /* audio locked section in new meter*/
2011 if (t->pulse() < ret) {
2020 /* treated as constant for this ts */
2021 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
2023 return pulses_in_section + prev_t->pulse();
2026 /* tempo section based */
2028 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2030 /* HOLD THE READER LOCK */
2032 const TempoSection* prev_t = 0;
2034 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2037 if ((*i)->is_tempo()) {
2038 t = static_cast<TempoSection*> (*i);
2042 if (prev_t && t->pulse() > pulse) {
2043 return prev_t->minute_at_pulse (pulse);
2049 /* must be treated as constant, irrespective of _type */
2050 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
2052 return dtime + prev_t->minute();
2055 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
2056 * @param bbt The BBT time (meter-based).
2057 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
2061 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
2063 Glib::Threads::RWLock::ReaderLock lm (lock);
2064 return beat_at_bbt_locked (_metrics, bbt);
2069 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2071 /* CALLER HOLDS READ LOCK */
2073 MeterSection* prev_m = 0;
2075 /* because audio-locked meters have 'fake' integral beats,
2076 there is no pulse offset here.
2080 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2081 if (!(*i)->is_tempo()) {
2082 m = static_cast<MeterSection*> (*i);
2084 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2085 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2093 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2094 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2095 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2100 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2101 * @param beat The BBT (meter-based) beat.
2102 * @return The BBT time (meter-based) at the supplied meter-based beat.
2106 TempoMap::bbt_at_beat (const double& beat)
2108 Glib::Threads::RWLock::ReaderLock lm (lock);
2109 return bbt_at_beat_locked (_metrics, beat);
2113 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2115 /* CALLER HOLDS READ LOCK */
2116 MeterSection* prev_m = 0;
2117 const double beats = max (0.0, b);
2119 MeterSection* m = 0;
2121 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2122 if (!(*i)->is_tempo()) {
2123 m = static_cast<MeterSection*> (*i);
2125 if (m->beat() > beats) {
2126 /* this is the meter after the one our beat is on*/
2136 const double beats_in_ms = beats - prev_m->beat();
2137 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2138 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2139 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2140 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2144 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2145 ret.beats = (uint32_t) floor (remaining_beats);
2146 ret.bars = total_bars;
2148 /* 0 0 0 to 1 1 0 - based mapping*/
2152 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2154 ret.ticks -= BBT_Time::ticks_per_beat;
2157 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2165 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2166 * @param bbt The BBT time (meter-based).
2167 * @return the quarter note beat at the supplied BBT time
2169 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2171 * while the input uses meter, the output does not.
2174 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2176 Glib::Threads::RWLock::ReaderLock lm (lock);
2178 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2182 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2184 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2187 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2190 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2194 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2196 /* CALLER HOLDS READ LOCK */
2198 MeterSection* prev_m = 0;
2200 /* because audio-locked meters have 'fake' integral beats,
2201 there is no pulse offset here.
2205 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2206 if (!(*i)->is_tempo()) {
2207 m = static_cast<MeterSection*> (*i);
2209 if (m->bbt().bars > bbt.bars) {
2217 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2218 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2219 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2224 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2225 * @param qn the quarter-note beat.
2226 * @return The BBT time (meter-based) at the supplied meter-based beat.
2228 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2232 TempoMap::bbt_at_quarter_note (const double& qn)
2234 Glib::Threads::RWLock::ReaderLock lm (lock);
2236 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2239 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2240 * @param metrics The list of metric sections used to determine the result.
2241 * @param pulse The whole-note pulse.
2242 * @return The BBT time at the supplied whole-note pulse.
2244 * a pulse or whole note is the basic musical position of a MetricSection.
2245 * it is equivalent to four quarter notes.
2246 * while the output uses meter, the input does not.
2249 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2251 MeterSection* prev_m = 0;
2253 MeterSection* m = 0;
2255 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2257 if (!(*i)->is_tempo()) {
2258 m = static_cast<MeterSection*> (*i);
2261 double const pulses_to_m = m->pulse() - prev_m->pulse();
2262 if (prev_m->pulse() + pulses_to_m > pulse) {
2263 /* this is the meter after the one our beat is on*/
2274 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2275 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2276 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2277 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2278 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2282 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2283 ret.beats = (uint32_t) floor (remaining_beats);
2284 ret.bars = total_bars;
2286 /* 0 0 0 to 1 1 0 mapping*/
2290 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2292 ret.ticks -= BBT_Time::ticks_per_beat;
2295 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2303 /** Returns the BBT time corresponding to the supplied frame position.
2304 * @param frame the position in audio samples.
2305 * @return the BBT time at the frame position .
2309 TempoMap::bbt_at_frame (framepos_t frame)
2317 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2322 const double minute = minute_at_frame (frame);
2324 Glib::Threads::RWLock::ReaderLock lm (lock);
2326 return bbt_at_minute_locked (_metrics, minute);
2330 TempoMap::bbt_at_frame_rt (framepos_t frame)
2332 const double minute = minute_at_frame (frame);
2334 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2337 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2340 return bbt_at_minute_locked (_metrics, minute);
2344 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2354 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2355 MeterSection* prev_m = 0;
2356 MeterSection* next_m = 0;
2360 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2361 if (!(*i)->is_tempo()) {
2362 m = static_cast<MeterSection*> (*i);
2363 if (prev_m && m->minute() > minute) {
2371 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2373 /* handle frame before first meter */
2374 if (minute < prev_m->minute()) {
2377 /* audio locked meters fake their beat */
2378 if (next_m && next_m->beat() < beat) {
2379 beat = next_m->beat();
2382 beat = max (0.0, beat);
2384 const double beats_in_ms = beat - prev_m->beat();
2385 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2386 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2387 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2388 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2392 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2393 ret.beats = (uint32_t) floor (remaining_beats);
2394 ret.bars = total_bars;
2396 /* 0 0 0 to 1 1 0 - based mapping*/
2400 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2402 ret.ticks -= BBT_Time::ticks_per_beat;
2405 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2413 /** Returns the frame position corresponding to the supplied BBT time.
2414 * @param bbt the position in BBT time.
2415 * @return the frame position at bbt.
2419 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2423 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2428 if (bbt.beats < 1) {
2429 throw std::logic_error ("beats are counted from one");
2434 Glib::Threads::RWLock::ReaderLock lm (lock);
2435 minute = minute_at_bbt_locked (_metrics, bbt);
2438 return frame_at_minute (minute);
2441 /* meter & tempo section based */
2443 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2445 /* HOLD THE READER LOCK */
2447 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2452 * Returns the quarter-note beat position corresponding to the supplied frame.
2454 * @param frame the position in frames.
2455 * @return The quarter-note position of the supplied frame. Ignores meter.
2459 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2461 const double minute = minute_at_frame (frame);
2463 Glib::Threads::RWLock::ReaderLock lm (lock);
2465 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2469 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2471 const double minute = minute_at_frame (frame);
2473 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2476 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2479 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2483 * Returns the frame position corresponding to the supplied quarter-note beat.
2485 * @param quarter_note the quarter-note position.
2486 * @return the frame position of the supplied quarter-note. Ignores meter.
2491 TempoMap::frame_at_quarter_note (const double quarter_note) const
2495 Glib::Threads::RWLock::ReaderLock lm (lock);
2497 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2500 return frame_at_minute (minute);
2503 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2504 * @param beat The BBT (meter-based) beat.
2505 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2507 * a quarter-note may be compared with and assigned to Evoral::Beats.
2511 TempoMap::quarter_note_at_beat (const double beat) const
2513 Glib::Threads::RWLock::ReaderLock lm (lock);
2515 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2518 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2519 * @param quarter_note The position in quarter-note beats.
2520 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2522 * a quarter-note is the musical unit of Evoral::Beats.
2526 TempoMap::beat_at_quarter_note (const double quarter_note) const
2528 Glib::Threads::RWLock::ReaderLock lm (lock);
2530 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2533 /** Returns the duration in frames between two supplied quarter-note beat positions.
2534 * @param start the first position in quarter-note beats.
2535 * @param end the end position in quarter-note beats.
2536 * @return the frame distance ober the quarter-note beats duration.
2538 * use this rather than e.g.
2539 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2540 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2544 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2549 Glib::Threads::RWLock::ReaderLock lm (lock);
2550 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2553 return frame_at_minute (minutes);
2557 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2560 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2564 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2566 Glib::Threads::RWLock::ReaderLock lm (lock);
2568 return quarter_notes_between_frames_locked (_metrics, start, end);
2572 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2574 const TempoSection* prev_t = 0;
2576 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2579 if ((*i)->is_tempo()) {
2580 t = static_cast<TempoSection*> (*i);
2584 if (prev_t && t->frame() > start) {
2591 const double start_qn = prev_t->pulse_at_frame (start);
2593 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2596 if ((*i)->is_tempo()) {
2597 t = static_cast<TempoSection*> (*i);
2601 if (prev_t && t->frame() > end) {
2607 const double end_qn = prev_t->pulse_at_frame (end);
2609 return (end_qn - start_qn) * 4.0;
2613 TempoMap::check_solved (const Metrics& metrics) const
2615 TempoSection* prev_t = 0;
2616 MeterSection* prev_m = 0;
2618 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2621 if ((*i)->is_tempo()) {
2622 t = static_cast<TempoSection*> (*i);
2627 /* check ordering */
2628 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2632 /* precision check ensures tempo and frames align.*/
2633 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2634 if (!t->locked_to_meter()) {
2639 /* gradient limit - who knows what it should be?
2640 things are also ok (if a little chaotic) without this
2642 if (fabs (prev_t->c()) > 1000.0) {
2643 //std::cout << "c : " << prev_t->c() << std::endl;
2650 if (!(*i)->is_tempo()) {
2651 m = static_cast<MeterSection*> (*i);
2652 if (prev_m && m->position_lock_style() == AudioTime) {
2653 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2654 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2655 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2657 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2671 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2673 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2675 if ((*i)->is_tempo()) {
2676 t = static_cast<TempoSection*> (*i);
2677 if (t->locked_to_meter()) {
2678 t->set_active (true);
2679 } else if (t->position_lock_style() == AudioTime) {
2680 if (t->frame() < frame) {
2681 t->set_active (false);
2682 t->set_pulse (-1.0);
2683 } else if (t->frame() > frame) {
2684 t->set_active (true);
2685 } else if (t->frame() == frame) {
2695 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2697 TempoSection* prev_t = 0;
2698 TempoSection* section_prev = 0;
2699 double first_m_minute = 0.0;
2700 const bool sml = section->locked_to_meter();
2702 /* can't move a tempo before the first meter */
2703 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2705 if (!(*i)->is_tempo()) {
2706 m = static_cast<MeterSection*> (*i);
2708 first_m_minute = m->minute();
2713 if (!section->initial() && minute <= first_m_minute) {
2717 section->set_active (true);
2718 section->set_minute (minute);
2720 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2722 if ((*i)->is_tempo()) {
2723 t = static_cast<TempoSection*> (*i);
2735 if (t->frame() == frame_at_minute (minute)) {
2739 const bool tlm = t->position_lock_style() == MusicTime;
2741 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2742 section_prev = prev_t;
2744 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2745 if (!section->locked_to_meter()) {
2746 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2751 if (t->position_lock_style() == MusicTime) {
2752 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2753 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2755 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2756 if (!t->locked_to_meter()) {
2757 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2766 recompute_tempi (imaginary);
2768 if (check_solved (imaginary)) {
2771 dunp (imaginary, std::cout);
2775 MetricSectionFrameSorter fcmp;
2776 imaginary.sort (fcmp);
2778 recompute_tempi (imaginary);
2780 if (check_solved (imaginary)) {
2788 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2790 TempoSection* prev_t = 0;
2791 TempoSection* section_prev = 0;
2793 section->set_pulse (pulse);
2795 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2797 if ((*i)->is_tempo()) {
2798 t = static_cast<TempoSection*> (*i);
2809 section_prev = prev_t;
2813 if (t->position_lock_style() == MusicTime) {
2814 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2815 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2817 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2818 if (!t->locked_to_meter()) {
2819 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2828 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2829 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2833 recompute_tempi (imaginary);
2835 if (check_solved (imaginary)) {
2838 dunp (imaginary, std::cout);
2842 MetricSectionSorter cmp;
2843 imaginary.sort (cmp);
2845 recompute_tempi (imaginary);
2847 * XX need a restriction here, but only for this case,
2848 * as audio locked tempos don't interact in the same way.
2850 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2852 * |50 bpm |250 bpm |60 bpm
2853 * drag 250 to the pulse after 60->
2854 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2856 if (check_solved (imaginary)) {
2864 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2866 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2867 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2868 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2872 if (section->initial()) {
2873 /* lock the first tempo to our first meter */
2874 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2879 TempoSection* meter_locked_tempo = 0;
2881 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2883 if ((*ii)->is_tempo()) {
2884 t = static_cast<TempoSection*> (*ii);
2885 if (t->locked_to_meter() && t->frame() == section->frame()) {
2886 meter_locked_tempo = t;
2892 if (!meter_locked_tempo) {
2896 MeterSection* prev_m = 0;
2898 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2899 bool solved = false;
2901 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2903 if (!(*i)->is_tempo()) {
2904 m = static_cast<MeterSection*> (*i);
2906 if (prev_m && !section->initial()) {
2907 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2908 if (beats + prev_m->beat() < section->beat()) {
2909 /* set the section pulse according to its musical position,
2910 * as an earlier time than this has been requested.
2912 const double new_pulse = ((section->beat() - prev_m->beat())
2913 / prev_m->note_divisor()) + prev_m->pulse();
2915 tempo_copy->set_position_lock_style (MusicTime);
2916 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2917 meter_locked_tempo->set_position_lock_style (MusicTime);
2918 section->set_position_lock_style (MusicTime);
2919 section->set_pulse (new_pulse);
2920 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2921 meter_locked_tempo->set_position_lock_style (AudioTime);
2922 section->set_position_lock_style (AudioTime);
2923 section->set_minute (meter_locked_tempo->minute());
2929 Metrics::const_iterator d = future_map.begin();
2930 while (d != future_map.end()) {
2939 /* all is ok. set section's locked tempo if allowed.
2940 possibly disallow if there is an adjacent audio-locked tempo.
2941 XX this check could possibly go. its never actually happened here.
2943 MeterSection* meter_copy = const_cast<MeterSection*>
2944 (&meter_section_at_minute_locked (future_map, section->minute()));
2946 meter_copy->set_minute (minute);
2948 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2949 section->set_minute (minute);
2950 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2951 / prev_m->note_divisor()) + prev_m->pulse());
2952 solve_map_minute (imaginary, meter_locked_tempo, minute);
2957 Metrics::const_iterator d = future_map.begin();
2958 while (d != future_map.end()) {
2968 /* initial (first meter atm) */
2970 tempo_copy->set_minute (minute);
2971 tempo_copy->set_pulse (0.0);
2973 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2974 section->set_minute (minute);
2975 meter_locked_tempo->set_minute (minute);
2976 meter_locked_tempo->set_pulse (0.0);
2977 solve_map_minute (imaginary, meter_locked_tempo, minute);
2982 Metrics::const_iterator d = future_map.begin();
2983 while (d != future_map.end()) {
2992 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2993 section->set_beat (b_bbt);
2994 section->set_pulse (0.0);
3004 MetricSectionFrameSorter fcmp;
3005 imaginary.sort (fcmp);
3007 recompute_meters (imaginary);
3013 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
3015 /* disallow setting section to an existing meter's bbt */
3016 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
3018 if (!(*i)->is_tempo()) {
3019 m = static_cast<MeterSection*> (*i);
3020 if (m != section && m->bbt().bars == when.bars) {
3026 MeterSection* prev_m = 0;
3027 MeterSection* section_prev = 0;
3029 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
3031 if (!(*i)->is_tempo()) {
3032 m = static_cast<MeterSection*> (*i);
3038 pair<double, BBT_Time> b_bbt;
3039 double new_pulse = 0.0;
3041 if (prev_m && m->bbt().bars > when.bars && !section_prev){
3042 section_prev = prev_m;
3044 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
3045 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
3046 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
3048 section->set_beat (b_bbt);
3049 section->set_pulse (pulse);
3050 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3054 if (m->position_lock_style() == AudioTime) {
3055 TempoSection* meter_locked_tempo = 0;
3057 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
3059 if ((*ii)->is_tempo()) {
3060 t = static_cast<TempoSection*> (*ii);
3061 if (t->locked_to_meter() && t->frame() == m->frame()) {
3062 meter_locked_tempo = t;
3068 if (!meter_locked_tempo) {
3073 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3075 if (beats + prev_m->beat() != m->beat()) {
3076 /* tempo/ meter change caused a change in beat (bar). */
3078 /* the user has requested that the previous section of music overlaps this one.
3079 we have no choice but to change the bar number here, as being locked to audio means
3080 we must stay where we are on the timeline.
3082 beats = m->beat() - prev_m->beat();
3083 b_bbt = make_pair (beats + prev_m->beat()
3084 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3085 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3087 } else if (!m->initial()) {
3088 b_bbt = make_pair (m->beat(), m->bbt());
3089 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3092 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3095 meter_locked_tempo->set_pulse (new_pulse);
3096 m->set_beat (b_bbt);
3097 m->set_pulse (new_pulse);
3101 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3102 if (beats + prev_m->beat() != m->beat()) {
3103 /* tempo/ meter change caused a change in beat (bar). */
3104 b_bbt = make_pair (beats + prev_m->beat()
3105 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3107 b_bbt = make_pair (beats + prev_m->beat()
3110 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3111 m->set_beat (b_bbt);
3112 m->set_pulse (new_pulse);
3113 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3120 if (!section_prev) {
3122 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3123 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3124 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3126 section->set_beat (b_bbt);
3127 section->set_pulse (pulse);
3128 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3131 MetricSectionSorter cmp;
3132 imaginary.sort (cmp);
3134 recompute_meters (imaginary);
3139 /** places a copy of _metrics into copy and returns a pointer
3140 * to section's equivalent in copy.
3143 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
3145 TempoSection* ret = 0;
3147 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3150 if ((*i)->is_tempo()) {
3151 t = static_cast<TempoSection*> (*i);
3153 ret = new TempoSection (*t);
3154 copy.push_back (ret);
3158 TempoSection* cp = new TempoSection (*t);
3159 copy.push_back (cp);
3161 if (!(*i)->is_tempo()) {
3162 m = static_cast<MeterSection *> (*i);
3163 MeterSection* cp = new MeterSection (*m);
3164 copy.push_back (cp);
3172 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3174 MeterSection* ret = 0;
3176 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3179 if ((*i)->is_tempo()) {
3180 t = static_cast<TempoSection*> (*i);
3181 TempoSection* cp = new TempoSection (*t);
3182 copy.push_back (cp);
3185 if (!(*i)->is_tempo()) {
3186 m = static_cast<MeterSection *> (*i);
3188 ret = new MeterSection (*m);
3189 copy.push_back (ret);
3192 MeterSection* cp = new MeterSection (*m);
3193 copy.push_back (cp);
3200 /** answers the question "is this a valid beat position for this tempo section?".
3201 * it returns true if the tempo section can be moved to the requested bbt position,
3202 * leaving the tempo map in a solved state.
3203 * @param ts the tempo section to be moved
3204 * @param bbt the requested new position for the tempo section
3205 * @return true if the tempo section can be moved to the position, otherwise false.
3208 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3211 TempoSection* tempo_copy = 0;
3214 Glib::Threads::RWLock::ReaderLock lm (lock);
3215 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3221 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3223 Metrics::const_iterator d = copy.begin();
3224 while (d != copy.end()) {
3233 * 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,
3234 * taking any possible reordering as a consequence of this into account.
3235 * @param section - the section to be altered
3236 * @param bbt - the BBT time where the altered tempo will fall
3237 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3239 pair<double, framepos_t>
3240 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3243 pair<double, framepos_t> ret = make_pair (0.0, 0);
3245 Glib::Threads::RWLock::ReaderLock lm (lock);
3247 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3249 const double beat = beat_at_bbt_locked (future_map, bbt);
3251 if (section->position_lock_style() == AudioTime) {
3252 tempo_copy->set_position_lock_style (MusicTime);
3255 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3256 ret.first = tempo_copy->pulse();
3257 ret.second = tempo_copy->frame();
3259 ret.first = section->pulse();
3260 ret.second = section->frame();
3263 Metrics::const_iterator d = future_map.begin();
3264 while (d != future_map.end()) {
3271 /** moves a TempoSection to a specified position.
3272 * @param ts - the section to be moved
3273 * @param frame - the new position in frames for the tempo
3274 * @param sub_num - the snap division to use if using musical time.
3276 * if sub_num is non-zero, the frame position is used to calculate an exact
3279 * -1 | snap to bars (meter-based)
3280 * 0 | no snap - use audio frame for musical position
3281 * 1 | snap to meter-based (BBT) beat
3282 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3284 * this follows the snap convention in the gui.
3285 * if sub_num is zero, the musical position will be taken from the supplied frame.
3288 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3292 if (ts->position_lock_style() == MusicTime) {
3294 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3295 Glib::Threads::RWLock::WriterLock lm (lock);
3296 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3298 tempo_copy->set_position_lock_style (AudioTime);
3300 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3301 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3302 const double pulse = pulse_at_beat_locked (future_map, beat);
3304 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3305 solve_map_pulse (_metrics, ts, pulse);
3306 recompute_meters (_metrics);
3314 Glib::Threads::RWLock::WriterLock lm (lock);
3315 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3317 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3319 /* We're moving the object that defines the grid while snapping to it...
3320 * Placing the ts at the beat corresponding to the requested frame may shift the
3321 * grid in such a way that the mouse is left hovering over a completerly different division,
3322 * causing jittering when the mouse next moves (esp. large tempo deltas).
3324 * This alters the snap behaviour slightly in that we snap to beat divisions
3325 * in the future map rather than the existing one.
3327 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3328 const framepos_t snapped_frame = frame_at_minute (minute_at_pulse_locked (future_map, qn / 4.0));
3330 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3331 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3332 ts->set_pulse (qn / 4.0);
3333 recompute_meters (_metrics);
3336 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3337 recompute_meters (_metrics);
3343 Metrics::const_iterator d = future_map.begin();
3344 while (d != future_map.end()) {
3349 MetricPositionChanged (PropertyChange ()); // Emit Signal
3352 /** moves a MeterSection to a specified position.
3353 * @param ms - the section to be moved
3354 * @param frame - the new position in frames for the meter
3356 * as a meter cannot snap to anything but bars,
3357 * the supplied frame is rounded to the nearest bar, possibly
3358 * leaving the meter position unchanged.
3361 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3365 if (ms->position_lock_style() == AudioTime) {
3368 Glib::Threads::RWLock::WriterLock lm (lock);
3369 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3371 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3372 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3373 recompute_tempi (_metrics);
3378 Glib::Threads::RWLock::WriterLock lm (lock);
3379 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3381 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3382 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3384 if (solve_map_bbt (future_map, copy, bbt)) {
3385 solve_map_bbt (_metrics, ms, bbt);
3386 recompute_tempi (_metrics);
3391 Metrics::const_iterator d = future_map.begin();
3392 while (d != future_map.end()) {
3397 MetricPositionChanged (PropertyChange ()); // Emit Signal
3401 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3404 bool can_solve = false;
3406 Glib::Threads::RWLock::WriterLock lm (lock);
3407 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3409 if (tempo_copy->type() == TempoSection::Constant) {
3410 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3411 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3413 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3414 tempo_copy->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3417 if (ts->clamped()) {
3418 TempoSection* prev = 0;
3419 if ((prev = previous_tempo_section_locked (future_map, tempo_copy)) != 0) {
3420 prev->set_end_note_types_per_minute (tempo_copy->note_types_per_minute());
3424 recompute_tempi (future_map);
3426 if (check_solved (future_map)) {
3427 if (ts->type() == TempoSection::Constant) {
3428 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3429 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3431 ts->set_end_note_types_per_minute (bpm.end_note_types_per_minute());
3432 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3435 if (ts->clamped()) {
3436 TempoSection* prev = 0;
3437 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3438 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3442 recompute_map (_metrics);
3447 Metrics::const_iterator d = future_map.begin();
3448 while (d != future_map.end()) {
3453 MetricPositionChanged (PropertyChange ()); // Emit Signal
3460 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3463 Ts (future prev_t) Tnext
3466 |----------|----------
3473 Glib::Threads::RWLock::WriterLock lm (lock);
3479 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3485 /* minimum allowed measurement distance in frames */
3486 framepos_t const min_dframe = 2;
3489 if (prev_t->clamped()) {
3490 TempoSection* next_t = next_tempo_section_locked (future_map, prev_t);
3491 TempoSection* prev_to_prev_t = previous_tempo_section_locked (future_map, prev_t);
3492 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3493 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3495 double contribution = 0.0;
3496 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3497 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3499 framepos_t const fr_off = (end_frame - frame);
3500 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3502 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3503 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3504 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3506 new_bpm = prev_t->note_types_per_minute();
3509 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3511 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
3512 / (double) (end_frame - prev_t->frame()));
3514 new_bpm = prev_t->note_types_per_minute();
3517 new_bpm = min (new_bpm, (double) 1000.0);
3519 /* don't clamp and proceed here.
3520 testing has revealed that this can go negative,
3521 which is an entirely different thing to just being too low.
3524 if (new_bpm < 0.5) {
3528 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3529 prev_t->set_note_types_per_minute (new_bpm);
3531 prev_t->set_end_note_types_per_minute (new_bpm);
3532 prev_t->set_note_types_per_minute (new_bpm);
3535 if (prev_t->clamped()) {
3536 TempoSection* prev = 0;
3537 if ((prev = previous_tempo_section_locked (future_map, prev_t)) != 0) {
3538 prev->set_end_note_types_per_minute (prev_t->note_types_per_minute());
3542 recompute_tempi (future_map);
3543 recompute_meters (future_map);
3545 if (check_solved (future_map)) {
3546 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3547 ts->set_note_types_per_minute (new_bpm);
3549 ts->set_end_note_types_per_minute (new_bpm);
3550 ts->set_note_types_per_minute (new_bpm);
3552 if (ts->clamped()) {
3553 TempoSection* prev = 0;
3554 if ((prev = previous_tempo_section_locked (_metrics, ts)) != 0) {
3555 prev->set_end_note_types_per_minute (ts->note_types_per_minute());
3558 recompute_tempi (_metrics);
3559 recompute_meters (_metrics);
3565 Metrics::const_iterator d = future_map.begin();
3566 while (d != future_map.end()) {
3570 MetricPositionChanged (PropertyChange ()); // Emit Signal
3575 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3578 Ts (future prev_t) Tnext
3581 |----------|----------
3588 Glib::Threads::RWLock::WriterLock lm (lock);
3594 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3600 /* minimum allowed measurement distance in frames */
3601 framepos_t const min_dframe = 2;
3604 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3605 new_bpm = prev_t->end_note_types_per_minute() * ((prev_t->frame() - frame)
3606 / (double) (prev_t->frame() - end_frame));
3608 new_bpm = prev_t->end_note_types_per_minute();
3611 new_bpm = min (new_bpm, (double) 1000.0);
3613 if (new_bpm < 0.5) {
3617 prev_t->set_end_note_types_per_minute (new_bpm);
3619 TempoSection* next = 0;
3620 if ((next = next_tempo_section_locked (future_map, prev_t)) != 0) {
3621 if (next->clamped()) {
3622 next->set_note_types_per_minute (prev_t->end_note_types_per_minute());
3626 recompute_tempi (future_map);
3627 recompute_meters (future_map);
3629 if (check_solved (future_map)) {
3630 ts->set_end_note_types_per_minute (new_bpm);
3632 TempoSection* true_next = 0;
3633 if ((true_next = next_tempo_section_locked (_metrics, ts)) != 0) {
3634 if (true_next->clamped()) {
3635 true_next->set_note_types_per_minute (ts->end_note_types_per_minute());
3639 recompute_tempi (_metrics);
3640 recompute_meters (_metrics);
3646 Metrics::const_iterator d = future_map.begin();
3647 while (d != future_map.end()) {
3652 MetricPositionChanged (PropertyChange ()); // Emit Signal
3656 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
3658 TempoSection* next_t = 0;
3659 TempoSection* next_to_next_t = 0;
3661 bool can_solve = false;
3663 /* minimum allowed measurement distance in frames */
3664 framepos_t const min_dframe = 2;
3667 Glib::Threads::RWLock::WriterLock lm (lock);
3672 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3673 TempoSection* prev_to_prev_t = 0;
3674 const frameoffset_t fr_off = end_frame - frame;
3680 if (tempo_copy->pulse() > 0.0) {
3681 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
3684 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3685 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3686 next_t = static_cast<TempoSection*> (*i);
3695 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3696 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3697 next_to_next_t = static_cast<TempoSection*> (*i);
3702 if (!next_to_next_t) {
3706 double prev_contribution = 0.0;
3708 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3709 prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3712 const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
3715 framepos_t old_tc_minute = tempo_copy->minute();
3716 double old_next_minute = next_t->minute();
3717 double old_next_to_next_minute = next_to_next_t->minute();
3720 double new_next_bpm;
3721 double new_copy_end_bpm;
3723 if (frame > tempo_copy->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > tempo_copy->frame() + min_dframe) {
3724 new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
3725 / (double) (end_frame - tempo_copy->frame()));
3727 new_bpm = tempo_copy->note_types_per_minute();
3730 /* don't clamp and proceed here.
3731 testing has revealed that this can go negative,
3732 which is an entirely different thing to just being too low.
3734 if (new_bpm < 0.5) {
3738 new_bpm = min (new_bpm, (double) 1000.0);
3740 tempo_copy->set_note_types_per_minute (new_bpm);
3741 if (tempo_copy->type() == TempoSection::Constant) {
3742 tempo_copy->set_end_note_types_per_minute (new_bpm);
3745 recompute_tempi (future_map);
3747 if (check_solved (future_map)) {
3753 ts->set_note_types_per_minute (new_bpm);
3754 if (ts->type() == TempoSection::Constant) {
3755 ts->set_end_note_types_per_minute (new_bpm);
3758 recompute_map (_metrics);
3763 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3764 if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) {
3766 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3767 / (double) ((old_next_to_next_minute) - old_next_minute));
3770 new_next_bpm = next_t->note_types_per_minute();
3773 next_t->set_note_types_per_minute (new_next_bpm);
3774 recompute_tempi (future_map);
3776 if (check_solved (future_map)) {
3777 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3778 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3779 next_t = static_cast<TempoSection*> (*i);
3787 next_t->set_note_types_per_minute (new_next_bpm);
3788 recompute_map (_metrics);
3792 double next_frame_ratio = 1.0;
3793 double copy_frame_ratio = 1.0;
3795 if (next_to_next_t) {
3796 next_frame_ratio = (next_to_next_t->minute() - old_next_minute) / (old_next_to_next_minute - old_next_minute);
3798 copy_frame_ratio = ((old_tc_minute - next_t->minute()) / (double) (old_tc_minute - old_next_minute));
3801 new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
3802 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
3804 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3806 if (next_t->clamped()) {
3807 next_t->set_note_types_per_minute (new_copy_end_bpm);
3809 next_t->set_note_types_per_minute (new_next_bpm);
3812 recompute_tempi (future_map);
3814 if (check_solved (future_map)) {
3815 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3816 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3817 next_t = static_cast<TempoSection*> (*i);
3826 if (next_t->clamped()) {
3827 next_t->set_note_types_per_minute (new_copy_end_bpm);
3829 next_t->set_note_types_per_minute (new_next_bpm);
3832 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3833 recompute_map (_metrics);
3839 Metrics::const_iterator d = future_map.begin();
3840 while (d != future_map.end()) {
3845 MetricPositionChanged (PropertyChange ()); // Emit Signal
3849 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3850 * the supplied frame, possibly returning a negative value.
3852 * @param frame The session frame position.
3853 * @param sub_num The subdivision to use when rounding the beat.
3854 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3855 * Positive integers indicate quarter note (non BBT) divisions.
3856 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3857 * @return The beat position of the supplied frame.
3859 * when working to a musical grid, the use of sub_nom indicates that
3860 * the position should be interpreted musically.
3862 * it effectively snaps to meter bars, meter beats or quarter note divisions
3863 * (as per current gui convention) and returns a musical position independent of frame rate.
3865 * If the supplied frame lies before the first meter, the return will be negative,
3866 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3867 * the continuation of the tempo curve (backwards).
3869 * This function is sensitive to tempo and meter.
3872 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3874 Glib::Threads::RWLock::ReaderLock lm (lock);
3876 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3880 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3882 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3885 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3886 * the supplied frame, possibly returning a negative value.
3888 * @param frame The session frame position.
3889 * @param sub_num The subdivision to use when rounding the quarter note.
3890 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3891 * Positive integers indicate quarter note (non BBT) divisions.
3892 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3893 * @return The quarter note position of the supplied frame.
3895 * When working to a musical grid, the use of sub_nom indicates that
3896 * the frame position should be interpreted musically.
3898 * it effectively snaps to meter bars, meter beats or quarter note divisions
3899 * (as per current gui convention) and returns a musical position independent of frame rate.
3901 * If the supplied frame lies before the first meter, the return will be negative,
3902 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3903 * the continuation of the tempo curve (backwards).
3905 * This function is tempo-sensitive.
3908 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3910 Glib::Threads::RWLock::ReaderLock lm (lock);
3912 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3916 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3918 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3921 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3922 } else if (sub_num == 1) {
3923 /* the gui requested exact musical (BBT) beat */
3924 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3925 } else if (sub_num == -1) {
3927 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3931 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3933 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3935 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3945 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3946 * @param pos the frame position in the tempo map.
3947 * @param bbt the distance in BBT time from pos to calculate.
3948 * @param dir the rounding direction..
3949 * @return the duration in frames between pos and bbt
3952 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3954 Glib::Threads::RWLock::ReaderLock lm (lock);
3956 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3958 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3961 pos_bbt.bars += bbt.bars;
3963 pos_bbt.ticks += bbt.ticks;
3964 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3966 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3969 pos_bbt.beats += bbt.beats;
3970 if ((double) pos_bbt.beats > divisions) {
3972 pos_bbt.beats -= divisions;
3974 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3976 return pos_bbt_frame - pos;
3980 if (pos_bbt.bars <= bbt.bars) {
3983 pos_bbt.bars -= bbt.bars;
3986 if (pos_bbt.ticks < bbt.ticks) {
3987 if (pos_bbt.bars > 1) {
3988 if (pos_bbt.beats == 1) {
3990 pos_bbt.beats = divisions;
3994 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
4000 pos_bbt.ticks -= bbt.ticks;
4003 if (pos_bbt.beats <= bbt.beats) {
4004 if (pos_bbt.bars > 1) {
4006 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
4011 pos_bbt.beats -= bbt.beats;
4014 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4021 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
4023 return round_to_type (fr, dir, Bar);
4027 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
4029 return round_to_type (fr, dir, Beat);
4033 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
4035 Glib::Threads::RWLock::ReaderLock lm (lock);
4036 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);
4037 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
4038 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
4040 ticks -= beats * BBT_Time::ticks_per_beat;
4043 /* round to next (or same iff dir == RoundUpMaybe) */
4045 uint32_t mod = ticks % ticks_one_subdivisions_worth;
4047 if (mod == 0 && dir == RoundUpMaybe) {
4048 /* right on the subdivision, which is fine, so do nothing */
4050 } else if (mod == 0) {
4051 /* right on the subdivision, so the difference is just the subdivision ticks */
4052 ticks += ticks_one_subdivisions_worth;
4055 /* not on subdivision, compute distance to next subdivision */
4057 ticks += ticks_one_subdivisions_worth - mod;
4060 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
4061 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
4062 // And since the "prev" direction DOES move beats, I assume this code is unintended.
4063 // But I'm keeping it around, until we determine there are no terrible consequences.
4064 // if (ticks >= BBT_Time::ticks_per_beat) {
4065 // ticks -= BBT_Time::ticks_per_beat;
4068 } else if (dir < 0) {
4070 /* round to previous (or same iff dir == RoundDownMaybe) */
4072 uint32_t difference = ticks % ticks_one_subdivisions_worth;
4074 if (difference == 0 && dir == RoundDownAlways) {
4075 /* right on the subdivision, but force-rounding down,
4076 so the difference is just the subdivision ticks */
4077 difference = ticks_one_subdivisions_worth;
4080 if (ticks < difference) {
4081 ticks = BBT_Time::ticks_per_beat - ticks;
4083 ticks -= difference;
4087 /* round to nearest */
4090 /* compute the distance to the previous and next subdivision */
4092 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
4094 /* closer to the next subdivision, so shift forward */
4096 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
4098 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
4100 if (ticks > BBT_Time::ticks_per_beat) {
4102 ticks -= BBT_Time::ticks_per_beat;
4103 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
4106 } else if (rem > 0) {
4108 /* closer to previous subdivision, so shift backward */
4112 /* can't go backwards past zero, so ... */
4113 return MusicFrame (0, 0);
4115 /* step back to previous beat */
4117 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4118 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4120 ticks = lrint (ticks - rem);
4121 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4124 /* on the subdivision, do nothing */
4128 MusicFrame ret (0, 0);
4129 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4130 ret.division = sub_num;
4136 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
4138 Glib::Threads::RWLock::ReaderLock lm (lock);
4139 const double minute = minute_at_frame (frame);
4140 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4141 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
4142 MusicFrame ret (0, 0);
4149 /* find bar previous to 'frame' */
4155 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4159 } else if (dir > 0) {
4160 /* find bar following 'frame' */
4165 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4169 /* true rounding: find nearest bar */
4170 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4173 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4175 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4177 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4178 ret.frame = next_ft;
4183 ret.frame = prev_ft;
4195 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
4198 } else if (dir > 0) {
4199 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
4203 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
4210 return MusicFrame (0, 0);
4214 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4215 framepos_t lower, framepos_t upper, uint32_t bar_mod)
4217 Glib::Threads::RWLock::ReaderLock lm (lock);
4218 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
4220 /* although the map handles negative beats, bbt doesn't. */
4225 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
4229 while (pos >= 0 && pos < upper) {
4230 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
4231 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4232 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4233 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4235 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4239 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
4244 bbt.bars -= bbt.bars % bar_mod;
4248 while (pos >= 0 && pos < upper) {
4249 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4250 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4251 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4252 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4253 bbt.bars += bar_mod;
4259 TempoMap::tempo_section_at_frame (framepos_t frame) const
4261 Glib::Threads::RWLock::ReaderLock lm (lock);
4263 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4267 TempoMap::tempo_section_at_frame (framepos_t frame)
4269 Glib::Threads::RWLock::ReaderLock lm (lock);
4271 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4275 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4277 TempoSection* prev = 0;
4281 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4283 if ((*i)->is_tempo()) {
4284 t = static_cast<TempoSection*> (*i);
4288 if (prev && t->minute() > minute) {
4298 abort(); /*NOTREACHED*/
4304 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4306 TempoSection* prev = 0;
4310 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4312 if ((*i)->is_tempo()) {
4313 t = static_cast<TempoSection*> (*i);
4317 if (prev && t->minute() > minute) {
4327 abort(); /*NOTREACHED*/
4333 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4335 TempoSection* prev_t = 0;
4336 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4340 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4341 if ((*i)->is_tempo()) {
4342 t = static_cast<TempoSection*> (*i);
4348 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4359 TempoMap::previous_tempo_section (TempoSection* ts) const
4361 Glib::Threads::RWLock::ReaderLock lm (lock);
4363 return previous_tempo_section_locked (_metrics, ts);
4368 TempoMap::previous_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4374 TempoSection* prev = 0;
4376 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4378 if ((*i)->is_tempo()) {
4379 TempoSection* t = static_cast<TempoSection*> (*i);
4385 if (prev && t == ts) {
4396 abort(); /*NOTREACHED*/
4403 TempoMap::next_tempo_section (TempoSection* ts) const
4405 Glib::Threads::RWLock::ReaderLock lm (lock);
4407 return next_tempo_section_locked (_metrics, ts);
4411 TempoMap::next_tempo_section_locked (const Metrics& metrics, TempoSection* ts) const
4417 TempoSection* prev = 0;
4419 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4421 if ((*i)->is_tempo()) {
4422 TempoSection* t = static_cast<TempoSection*> (*i);
4428 if (prev && prev == ts) {
4439 abort(); /*NOTREACHED*/
4444 /* don't use this to calculate length (the tempo is only correct for this frame).
4445 do that stuff based on the beat_at_frame and frame_at_beat api
4448 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4450 Glib::Threads::RWLock::ReaderLock lm (lock);
4452 const TempoSection* ts_at = 0;
4453 const TempoSection* ts_after = 0;
4454 Metrics::const_iterator i;
4457 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4459 if ((*i)->is_tempo()) {
4460 t = static_cast<TempoSection*> (*i);
4464 if (ts_at && (*i)->frame() > frame) {
4474 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4476 /* must be treated as constant tempo */
4477 return ts_at->frames_per_quarter_note (_frame_rate);
4481 TempoMap::meter_section_at_frame (framepos_t frame) const
4483 Glib::Threads::RWLock::ReaderLock lm (lock);
4484 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4488 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4490 Metrics::const_iterator i;
4491 MeterSection* prev = 0;
4495 for (i = metrics.begin(); i != metrics.end(); ++i) {
4497 if (!(*i)->is_tempo()) {
4498 m = static_cast<MeterSection*> (*i);
4500 if (prev && (*i)->minute() > minute) {
4510 abort(); /*NOTREACHED*/
4517 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4519 MeterSection* prev_m = 0;
4521 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4523 if (!(*i)->is_tempo()) {
4524 m = static_cast<MeterSection*> (*i);
4525 if (prev_m && m->beat() > beat) {
4536 TempoMap::meter_section_at_beat (double beat) const
4538 Glib::Threads::RWLock::ReaderLock lm (lock);
4539 return meter_section_at_beat_locked (_metrics, beat);
4543 TempoMap::meter_at_frame (framepos_t frame) const
4545 TempoMetric m (metric_at (frame));
4550 TempoMap::fix_legacy_session ()
4552 MeterSection* prev_m = 0;
4553 TempoSection* prev_t = 0;
4554 bool have_initial_t = false;
4556 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4560 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4562 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4565 m->set_minute (0.0);
4566 m->set_position_lock_style (AudioTime);
4571 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4572 + (m->bbt().beats - 1)
4573 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4575 m->set_beat (start);
4576 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4577 + (m->bbt().beats - 1)
4578 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4579 m->set_pulse (start_beat / prev_m->note_divisor());
4582 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4590 t->set_minute (0.0);
4591 t->set_position_lock_style (AudioTime);
4593 have_initial_t = true;
4598 /* some 4.x sessions have no initial (non-movable) tempo. */
4599 if (!have_initial_t) {
4600 prev_t->set_pulse (0.0);
4601 prev_t->set_minute (0.0);
4602 prev_t->set_position_lock_style (AudioTime);
4603 prev_t->set_initial (true);
4604 prev_t->set_locked_to_meter (true);
4605 have_initial_t = true;
4608 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4609 + (t->legacy_bbt().beats - 1)
4610 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4612 t->set_pulse (beat / prev_m->note_divisor());
4614 /* really shouldn't happen but.. */
4615 t->set_pulse (beat / 4.0);
4623 TempoMap::fix_legacy_end_session ()
4625 TempoSection* prev_t = 0;
4627 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4630 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4637 if (prev_t->type() != TempoSection::Constant) {
4638 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4648 TempoMap::get_state ()
4650 Metrics::const_iterator i;
4651 XMLNode *root = new XMLNode ("TempoMap");
4654 Glib::Threads::RWLock::ReaderLock lm (lock);
4655 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4656 root->add_child_nocopy ((*i)->get_state());
4664 TempoMap::set_state (const XMLNode& node, int /*version*/)
4667 Glib::Threads::RWLock::WriterLock lm (lock);
4670 XMLNodeConstIterator niter;
4671 Metrics old_metrics (_metrics);
4674 nlist = node.children();
4676 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4677 XMLNode* child = *niter;
4679 if (child->name() == TempoSection::xml_state_node_name) {
4682 TempoSection* ts = new TempoSection (*child, _frame_rate);
4683 _metrics.push_back (ts);
4686 catch (failed_constructor& err){
4687 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4688 _metrics = old_metrics;
4689 old_metrics.clear();
4693 } else if (child->name() == MeterSection::xml_state_node_name) {
4696 MeterSection* ms = new MeterSection (*child, _frame_rate);
4697 _metrics.push_back (ms);
4700 catch (failed_constructor& err) {
4701 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4702 _metrics = old_metrics;
4703 old_metrics.clear();
4709 if (niter == nlist.end()) {
4710 MetricSectionSorter cmp;
4711 _metrics.sort (cmp);
4714 /* check for legacy sessions where bbt was the base musical unit for tempo */
4715 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4717 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4718 if (t->legacy_bbt().bars != 0) {
4719 fix_legacy_session();
4723 if (t->legacy_end()) {
4724 fix_legacy_end_session();
4732 /* check for multiple tempo/meters at the same location, which
4733 ardour2 somehow allowed.
4736 Metrics::iterator prev = _metrics.end();
4737 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4738 if (prev != _metrics.end()) {
4740 MeterSection* prev_m;
4742 TempoSection* prev_t;
4743 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4744 if (prev_m->pulse() == ms->pulse()) {
4745 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4746 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4749 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4750 if (prev_t->pulse() == ts->pulse()) {
4751 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4752 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4760 recompute_map (_metrics);
4762 Metrics::const_iterator d = old_metrics.begin();
4763 while (d != old_metrics.end()) {
4767 old_metrics.clear ();
4770 PropertyChanged (PropertyChange ());
4776 TempoMap::dump (std::ostream& o) const
4778 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4779 const MeterSection* m;
4780 const TempoSection* t;
4781 const TempoSection* prev_t = 0;
4783 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4785 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4786 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4787 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4788 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4789 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4791 o << " current start : " << t->note_types_per_minute()
4792 << " current end : " << t->end_note_types_per_minute()
4793 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4794 o << " previous : " << prev_t->note_types_per_minute()
4795 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4796 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4797 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4798 << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4799 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4802 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4803 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4804 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4805 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4808 o << "------" << std::endl;
4812 TempoMap::n_tempos() const
4814 Glib::Threads::RWLock::ReaderLock lm (lock);
4817 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4818 if ((*i)->is_tempo()) {
4827 TempoMap::n_meters() const
4829 Glib::Threads::RWLock::ReaderLock lm (lock);
4832 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4833 if (!(*i)->is_tempo()) {
4842 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4844 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4845 if ((*i)->frame() >= where && !(*i)->initial ()) {
4849 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4850 gui_set_meter_position (ms, (*i)->frame() + amount);
4853 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4854 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4859 PropertyChanged (PropertyChange ());
4863 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4867 std::list<MetricSection*> metric_kill_list;
4869 TempoSection* last_tempo = NULL;
4870 MeterSection* last_meter = NULL;
4871 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4872 bool meter_after = false; // is there a meter marker likewise?
4874 Glib::Threads::RWLock::WriterLock lm (lock);
4875 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4876 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4877 metric_kill_list.push_back(*i);
4878 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4881 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4885 else if ((*i)->frame() >= where) {
4886 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4887 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4888 if ((*i)->frame() == where) {
4889 // marker was immediately after end of range
4890 tempo_after = dynamic_cast<TempoSection*> (*i);
4891 meter_after = dynamic_cast<MeterSection*> (*i);
4897 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4898 if (last_tempo && !tempo_after) {
4899 metric_kill_list.remove(last_tempo);
4900 last_tempo->set_minute (minute_at_frame (where));
4903 if (last_meter && !meter_after) {
4904 metric_kill_list.remove(last_meter);
4905 last_meter->set_minute (minute_at_frame (where));
4909 //remove all the remaining metrics
4910 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4911 _metrics.remove(*i);
4916 recompute_map (_metrics);
4919 PropertyChanged (PropertyChange ());
4923 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4924 * pos can be -ve, if required.
4927 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4929 Glib::Threads::RWLock::ReaderLock lm (lock);
4930 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4932 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4936 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4938 Glib::Threads::RWLock::ReaderLock lm (lock);
4940 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4941 pos_bbt.ticks += op.ticks;
4942 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4944 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4946 pos_bbt.beats += op.beats;
4947 /* the meter in effect will start on the bar */
4948 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();
4949 while (pos_bbt.beats >= divisions_per_bar + 1) {
4951 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4952 pos_bbt.beats -= divisions_per_bar;
4954 pos_bbt.bars += op.bars;
4956 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4959 /** Count the number of beats that are equivalent to distance when going forward,
4963 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4965 Glib::Threads::RWLock::ReaderLock lm (lock);
4967 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4971 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4977 operator<< (std::ostream& o, const Meter& m) {
4978 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4982 operator<< (std::ostream& o, const Tempo& t) {
4983 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4987 operator<< (std::ostream& o, const MetricSection& section) {
4989 o << "MetricSection @ " << section.frame() << ' ';
4991 const TempoSection* ts;
4992 const MeterSection* ms;
4994 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4995 o << *((const Tempo*) ts);
4996 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4997 o << *((const Meter*) ms);