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)
95 XMLProperty const * prop;
101 _legacy_bbt = BBT_Time (0, 0, 0);
103 if ((prop = node.property ("start")) != 0) {
104 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
108 /* legacy session - start used to be in bbt*/
111 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
115 if ((prop = node.property ("pulse")) != 0) {
116 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
117 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
123 if ((prop = node.property ("frame")) != 0) {
124 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
125 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
126 throw failed_constructor();
128 set_minute (minute_at_frame (frame));
132 /* XX replace old beats-per-minute name with note-types-per-minute */
133 if ((prop = node.property ("beats-per-minute")) != 0) {
134 if (sscanf (prop->value().c_str(), "%lf", &_note_types_per_minute) != 1 || _note_types_per_minute < 0.0) {
135 error << _("TempoSection XML node has an illegal \"beats-per-minute\" value") << endmsg;
136 throw failed_constructor();
140 if ((prop = node.property ("note-type")) == 0) {
141 /* older session, make note type be quarter by default */
144 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
145 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
146 throw failed_constructor();
150 /* XX replace old end-beats-per-minute name with note-types-per-minute */
151 if ((prop = node.property ("end-beats-per-minute")) != 0) {
152 if (sscanf (prop->value().c_str(), "%lf", &_end_note_types_per_minute) != 1 || _end_note_types_per_minute < 0.0) {
153 info << _("TempoSection XML node has an illegal \"in-beats-per-minute\" value") << endmsg;
154 //throw failed_constructor();
155 _end_note_types_per_minute = _note_types_per_minute;
162 if ((prop = node.property ("tempo-type")) != 0) {
163 TempoSection::Type old_type;
165 old_type = Type (string_2_enum (prop->value(), old_type));
166 if (old_type == TempoSection::Constant) {
167 _end_note_types_per_minute = _note_types_per_minute;
171 if ((prop = node.property ("movable")) == 0) {
172 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
173 throw failed_constructor();
176 set_initial (!string_is_affirmative (prop->value()));
178 if ((prop = node.property ("active")) == 0) {
179 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
182 set_active (string_is_affirmative (prop->value()));
185 if ((prop = node.property ("lock-style")) == 0) {
187 set_position_lock_style (MusicTime);
189 set_position_lock_style (AudioTime);
192 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
195 if ((prop = node.property ("locked-to-meter")) == 0) {
197 set_locked_to_meter (true);
199 set_locked_to_meter (false);
202 set_locked_to_meter (string_is_affirmative (prop->value()));
205 /* 5.5 marked initial tempo as not locked to meter. this should always be true anyway */
207 set_locked_to_meter (true);
212 TempoSection::get_state() const
214 XMLNode *root = new XMLNode (xml_state_node_name);
218 snprintf (buf, sizeof (buf), "%lf", pulse());
219 root->add_property ("pulse", buf);
220 snprintf (buf, sizeof (buf), "%li", frame());
221 root->add_property ("frame", buf);
222 snprintf (buf, sizeof (buf), "%lf", _note_types_per_minute);
223 root->add_property ("beats-per-minute", buf);
224 snprintf (buf, sizeof (buf), "%lf", _note_type);
225 root->add_property ("note-type", buf);
226 snprintf (buf, sizeof (buf), "%lf", _end_note_types_per_minute);
227 root->add_property ("end-beats-per-minute", buf);
228 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
229 root->add_property ("movable", buf);
230 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
231 root->add_property ("active", buf);
232 root->add_property ("lock-style", enum_2_string (position_lock_style()));
233 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
238 /** returns the Tempo at the session-relative minute.
241 TempoSection::tempo_at_minute (const double& m) const
243 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
245 return Tempo (note_types_per_minute(), note_type());
248 return Tempo (_tempo_at_time (m - minute()), _note_type, _end_note_types_per_minute);
251 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
252 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
253 * @param p the pulse used to calculate the returned minute for constant tempi
254 * @return the minute at the supplied tempo
256 * note that the note_type is currently ignored in this function. see below.
260 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
261 * there should be no ramp between the two even if we are ramped.
262 * in other words a ramp should only place a curve on note_types_per_minute.
263 * we should be able to use Tempo note type here, but the above
264 * complicates things a bit.
267 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
269 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
271 return ((p - pulse()) / pulses_per_minute()) + minute();
274 return _time_at_tempo (ntpm) + minute();
277 /** returns the Tempo at the supplied whole-note pulse.
280 TempoSection::tempo_at_pulse (const double& p) const
282 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
285 return Tempo (note_types_per_minute(), note_type());
288 return Tempo (_tempo_at_pulse (p - pulse()), _note_type, _end_note_types_per_minute);
291 /** returns the whole-note pulse where a tempo in note types per minute occurs.
292 * constant tempi require minute m.
293 * @param ntpm the note types per minute value used to calculate the returned pulse
294 * @param m the minute used to calculate the returned pulse if the tempo is constant
295 * @return the whole-note pulse at the supplied tempo
297 * note that note_type is currently ignored in this function. see minute_at_tempo().
299 * for constant tempi, this is anaologous to pulse_at_minute().
302 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
304 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
306 return ((m - minute()) * pulses_per_minute()) + pulse();
309 return _pulse_at_tempo (ntpm) + pulse();
312 /** returns the whole-note pulse at the supplied session-relative minute.
315 TempoSection::pulse_at_minute (const double& m) const
317 const bool constant = type() == Constant || _c == 0.0 || (initial() && m < minute());
319 return ((m - minute()) * pulses_per_minute()) + pulse();
322 return _pulse_at_time (m - minute()) + pulse();
325 /** returns the session-relative minute at the supplied whole-note pulse.
328 TempoSection::minute_at_pulse (const double& p) const
330 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
332 return ((p - pulse()) / pulses_per_minute()) + minute();
335 return _time_at_pulse (p - pulse()) + minute();
338 /** returns thw whole-note pulse at session frame position f.
339 * @param f the frame position.
340 * @return the position in whole-note pulses corresponding to f
342 * for use with musical units whose granularity is coarser than frames (e.g. ticks)
345 TempoSection::pulse_at_frame (const framepos_t& f) const
347 const bool constant = type() == Constant || _c == 0.0 || (initial() && f < frame());
349 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
352 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
356 TempoSection::frame_at_pulse (const double& p) const
358 const bool constant = type() == Constant || _c == 0.0 || (initial() && p < pulse());
360 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
363 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
371 Tt----|-----------------*|
372 Ta----|--------------|* |
378 _______________|___|____
379 time a t (next tempo)
382 Duration in beats at time a is the integral of some Tempo function.
383 In our case, the Tempo function (Tempo at time t) is
386 with function constant
391 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
392 b(t) = T0(e^(ct) - 1) / c
394 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:
395 t(b) = log((c.b / T0) + 1) / c
397 The time t at which Tempo T occurs is a as above:
398 t(T) = log(T / T0) / c
400 The beat at which a Tempo T occurs is:
403 The Tempo at which beat b occurs is:
406 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
407 Our problem is that we usually don't know t.
408 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.
409 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
410 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
412 By substituting our expanded t as a in the c function above, our problem is reduced to:
413 c = T0 (e^(log (Ta / T0)) - 1) / b
415 Of course the word 'beat' has been left loosely defined above.
416 In music, a beat is defined by the musical pulse (which comes from the tempo)
417 and the meter in use at a particular time (how many pulse divisions there are in one bar).
418 It would be more accurate to substitute the work 'pulse' for 'beat' above.
422 We can now store c for future time calculations.
423 If the following tempo section (the one that defines c in conjunction with this one)
424 is changed or moved, c is no longer valid.
426 The public methods are session-relative.
428 Most of this stuff is taken from this paper:
431 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
434 Zurich University of Arts
435 Institute for Computer Music and Sound Technology
437 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
441 /** compute this ramp's function constant from some tempo-pulse point
442 * @param end_npm end tempo (in note types per minute)
443 * @param end_pulse duration (pulses into global start) of some other position.
444 * @return the calculated function constant
447 TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const
449 if (note_types_per_minute() == end_npm || type() == Constant) {
453 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
454 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
457 /** compute the function constant from some tempo-time point.
458 * @param end_npm tempo (note types/min.)
459 * @param end_minute distance (in minutes) from session origin
460 * @return the calculated function constant
463 TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const
465 if (note_types_per_minute() == end_npm || type() == Constant) {
469 return c_func (end_npm, end_minute - minute());
472 /* position function */
474 TempoSection::a_func (double end_npm, double c) const
476 return log (end_npm / note_types_per_minute()) / c;
479 /*function constant*/
481 TempoSection::c_func (double end_npm, double end_time) const
483 return log (end_npm / note_types_per_minute()) / end_time;
486 /* tempo in note types per minute at time in minutes */
488 TempoSection::_tempo_at_time (const double& time) const
490 return exp (_c * time) * note_types_per_minute();
493 /* time in minutes at tempo in note types per minute */
495 TempoSection::_time_at_tempo (const double& npm) const
497 return log (npm / note_types_per_minute()) / _c;
500 /* pulse at tempo in note types per minute */
502 TempoSection::_pulse_at_tempo (const double& npm) const
504 return ((npm - note_types_per_minute()) / _c) / _note_type;
507 /* tempo in note types per minute at pulse */
509 TempoSection::_tempo_at_pulse (const double& pulse) const
511 return (pulse * _note_type * _c) + note_types_per_minute();
514 /* pulse at time in minutes */
516 TempoSection::_pulse_at_time (const double& time) const
518 return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
521 /* time in minutes at pulse */
523 TempoSection::_time_at_pulse (const double& pulse) const
525 return log1p ((_c * pulse * _note_type) / note_types_per_minute()) / _c;
528 /***********************************************************************/
530 const string MeterSection::xml_state_node_name = "Meter";
532 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
533 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
535 XMLProperty const * prop;
540 framepos_t frame = 0;
541 pair<double, BBT_Time> start;
543 if ((prop = node.property ("start")) != 0) {
544 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
548 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
550 /* legacy session - start used to be in bbt*/
551 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
556 if ((prop = node.property ("pulse")) != 0) {
557 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
558 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
563 if ((prop = node.property ("beat")) != 0) {
564 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
565 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
571 if ((prop = node.property ("bbt")) == 0) {
572 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
573 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
577 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
578 throw failed_constructor();
584 if ((prop = node.property ("frame")) != 0) {
585 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
586 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
587 throw failed_constructor();
589 set_minute (minute_at_frame (frame));
593 /* beats-per-bar is old; divisions-per-bar is new */
595 if ((prop = node.property ("divisions-per-bar")) == 0) {
596 if ((prop = node.property ("beats-per-bar")) == 0) {
597 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
598 throw failed_constructor();
601 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
602 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
603 throw failed_constructor();
606 if ((prop = node.property ("note-type")) == 0) {
607 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
608 throw failed_constructor();
610 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
611 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
612 throw failed_constructor();
615 if ((prop = node.property ("movable")) == 0) {
616 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
617 throw failed_constructor();
620 set_initial (!string_is_affirmative (prop->value()));
622 if ((prop = node.property ("lock-style")) == 0) {
623 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
625 set_position_lock_style (MusicTime);
627 set_position_lock_style (AudioTime);
630 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
635 MeterSection::get_state() const
637 XMLNode *root = new XMLNode (xml_state_node_name);
641 snprintf (buf, sizeof (buf), "%lf", pulse());
642 root->add_property ("pulse", buf);
643 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
647 root->add_property ("bbt", buf);
648 snprintf (buf, sizeof (buf), "%lf", beat());
649 root->add_property ("beat", buf);
650 snprintf (buf, sizeof (buf), "%lf", _note_type);
651 root->add_property ("note-type", buf);
652 snprintf (buf, sizeof (buf), "%li", frame());
653 root->add_property ("frame", buf);
654 root->add_property ("lock-style", enum_2_string (position_lock_style()));
655 snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
656 root->add_property ("divisions-per-bar", buf);
657 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
658 root->add_property ("movable", buf);
663 /***********************************************************************/
667 Tempo determines the rate of musical pulse determined by its components
668 note types per minute - the rate per minute of the whole note divisor _note_type
669 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
670 Meter divides the musical pulse into measures and beats according to its components
674 TempoSection - translates between time, musical pulse and tempo.
675 has a musical location in whole notes (pulses).
676 has a time location in minutes.
677 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
678 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
680 MeterSection - translates between BBT, meter-based beat and musical pulse.
681 has a musical location in whole notes (pulses)
682 has a musical location in meter-based beats
683 has a musical location in BBT time
684 has a time location expressed in minutes.
686 TempoSection and MeterSection may be locked to either audio or music (position lock style).
687 The lock style determines the location type to be kept as a reference when location is recalculated.
689 The first tempo and meter are special. they must move together, and are locked to audio.
690 Audio locked tempi which lie before the first meter are made inactive.
692 Recomputing the map is the process where the 'missing' location types are calculated.
693 We construct the tempo map by first using the locked location type of each section
694 to determine non-locked location types (pulse or minute position).
695 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
697 Having done this, we can now traverse the Metrics list by pulse or minute
698 to query its relevant meter/tempo.
700 It is important to keep the _metrics in an order that makes sense.
701 Because ramped MusicTime and AudioTime tempos can interact with each other,
702 reordering is frequent. Care must be taken to keep _metrics in a solved state.
703 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
707 Music and audio-locked objects may seem interchangeable on the surface, but when translating
708 between audio samples and beat, remember that a sample is only a quantised approximation
709 of the actual time (in minutes) of a beat.
710 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
711 mean that this frame is the actual location in time of 1|3|0.
713 You cannot use a frame measurement to determine beat distance except under special circumstances
714 (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).
716 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
717 sample space the user is operating at to be translated correctly to the object.
719 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
720 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
721 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
723 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
725 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
726 The result is rounded to audio frames.
727 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
730 frame_at_beat (beat_at_frame (frame)) == frame
732 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
734 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
735 frames_between_quarter_notes () eliminats this effect when determining time duration
736 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
738 The above pointless example could instead do:
739 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
741 The Shaggs - Things I Wonder
742 https://www.youtube.com/watch?v=9wQK6zMJOoQ
745 struct MetricSectionSorter {
746 bool operator() (const MetricSection* a, const MetricSection* b) {
747 return a->pulse() < b->pulse();
751 struct MetricSectionFrameSorter {
752 bool operator() (const MetricSection* a, const MetricSection* b) {
753 return a->frame() < b->frame();
757 TempoMap::TempoMap (framecnt_t fr)
760 BBT_Time start (1, 1, 0);
762 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo, AudioTime, fr);
763 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
765 t->set_initial (true);
766 t->set_locked_to_meter (true);
768 m->set_initial (true);
770 /* note: frame time is correct (zero) for both of these */
772 _metrics.push_back (t);
773 _metrics.push_back (m);
777 TempoMap::TempoMap (TempoMap const & other)
779 _frame_rate = other._frame_rate;
780 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
781 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
782 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
785 TempoSection* new_section = new TempoSection (*ts);
786 _metrics.push_back (new_section);
788 MeterSection* new_section = new MeterSection (*ms);
789 _metrics.push_back (new_section);
795 TempoMap::operator= (TempoMap const & other)
797 if (&other != this) {
798 _frame_rate = other._frame_rate;
800 Metrics::const_iterator d = _metrics.begin();
801 while (d != _metrics.end()) {
807 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
808 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
809 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
812 TempoSection* new_section = new TempoSection (*ts);
813 _metrics.push_back (new_section);
815 MeterSection* new_section = new MeterSection (*ms);
816 _metrics.push_back (new_section);
821 PropertyChanged (PropertyChange());
826 TempoMap::~TempoMap ()
828 Metrics::const_iterator d = _metrics.begin();
829 while (d != _metrics.end()) {
837 TempoMap::frame_at_minute (const double time) const
839 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
843 TempoMap::minute_at_frame (const framepos_t frame) const
845 return (frame / (double) _frame_rate) / 60.0;
849 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
851 bool removed = false;
854 Glib::Threads::RWLock::WriterLock lm (lock);
855 if ((removed = remove_tempo_locked (tempo))) {
856 if (complete_operation) {
857 recompute_map (_metrics);
862 if (removed && complete_operation) {
863 PropertyChanged (PropertyChange ());
868 TempoMap::remove_tempo_locked (const TempoSection& tempo)
872 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
873 if (dynamic_cast<TempoSection*> (*i) != 0) {
874 if (tempo.frame() == (*i)->frame()) {
875 if (!(*i)->initial()) {
888 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
890 bool removed = false;
893 Glib::Threads::RWLock::WriterLock lm (lock);
894 if ((removed = remove_meter_locked (tempo))) {
895 if (complete_operation) {
896 recompute_map (_metrics);
901 if (removed && complete_operation) {
902 PropertyChanged (PropertyChange ());
907 TempoMap::remove_meter_locked (const MeterSection& meter)
910 if (meter.position_lock_style() == AudioTime) {
911 /* remove meter-locked tempo */
912 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
914 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
915 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
924 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
925 if (dynamic_cast<MeterSection*> (*i) != 0) {
926 if (meter.frame() == (*i)->frame()) {
927 if (!(*i)->initial()) {
940 TempoMap::do_insert (MetricSection* section)
942 bool need_add = true;
943 /* we only allow new meters to be inserted on beat 1 of an existing
947 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
949 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
951 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
952 corrected.second.beats = 1;
953 corrected.second.ticks = 0;
954 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
955 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
956 m->bbt(), corrected.second) << endmsg;
957 //m->set_pulse (corrected);
961 /* Look for any existing MetricSection that is of the same type and
962 in the same bar as the new one, and remove it before adding
963 the new one. Note that this means that if we find a matching,
964 existing section, we can break out of the loop since we're
965 guaranteed that there is only one such match.
968 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
970 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
971 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
972 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
973 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
975 if (tempo && insert_tempo) {
978 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
979 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
981 if (tempo->initial()) {
983 /* can't (re)move this section, so overwrite
984 * its data content (but not its properties as
988 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
989 (*i)->set_position_lock_style (AudioTime);
998 } else if (meter && insert_meter) {
1000 /* Meter Sections */
1002 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1004 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
1006 if (meter->initial()) {
1008 /* can't (re)move this section, so overwrite
1009 * its data content (but not its properties as
1013 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
1014 (*i)->set_position_lock_style (AudioTime);
1024 /* non-matching types, so we don't care */
1028 /* Add the given MetricSection, if we didn't just reset an existing
1033 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
1034 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
1035 Metrics::iterator i;
1038 TempoSection* prev_t = 0;
1040 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1041 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
1042 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1045 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
1049 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
1053 prev_t = dynamic_cast<TempoSection*> (*i);
1056 } else if (insert_tempo) {
1057 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1058 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1061 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1062 const bool lm = insert_tempo->locked_to_meter();
1063 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1064 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1071 _metrics.insert (i, section);
1075 /* user supplies the exact pulse if pls == MusicTime */
1077 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1079 if (tempo.note_types_per_minute() <= 0.0) {
1080 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1084 TempoSection* ts = 0;
1085 TempoSection* prev_tempo = 0;
1087 Glib::Threads::RWLock::WriterLock lm (lock);
1088 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true);
1089 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1091 if ((*i)->is_tempo()) {
1092 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1094 bool const ipm = ts->position_lock_style() == MusicTime;
1095 bool const lm = ts->locked_to_meter();
1096 if ((ipm && this_t->pulse() == ts->pulse()) || (!ipm && this_t->frame() == ts->frame())
1097 || (lm && this_t->pulse() == ts->pulse())) {
1098 if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1099 prev_tempo->set_end_note_types_per_minute (ts->note_types_per_minute());
1103 prev_tempo = this_t;
1106 recompute_map (_metrics);
1109 PropertyChanged (PropertyChange ());
1115 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, PositionLockStyle pls)
1117 if (tempo.note_types_per_minute() <= 0.0) {
1118 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1122 const bool locked_to_meter = ts.locked_to_meter();
1123 TempoSection* new_ts = 0;
1126 Glib::Threads::RWLock::WriterLock lm (lock);
1127 TempoSection& first (first_tempo());
1128 if (!ts.initial()) {
1129 if (locked_to_meter) {
1131 /* cannot move a meter-locked tempo section */
1132 *static_cast<Tempo*>(&ts) = tempo;
1133 recompute_map (_metrics);
1136 remove_tempo_locked (ts);
1137 new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), pls, true, locked_to_meter);
1139 if (new_ts && new_ts->type() == TempoSection::Constant) {
1140 new_ts->set_end_note_types_per_minute (new_ts->note_types_per_minute());
1142 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1144 if ((*i)->is_tempo()) {
1145 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1147 bool const ipm = new_ts->position_lock_style() == MusicTime;
1148 bool const lm = new_ts->locked_to_meter();
1149 if ((ipm && this_t->pulse() > new_ts->pulse()) || (!ipm && this_t->frame() > new_ts->frame())
1150 || (lm && this_t->pulse() > new_ts->pulse())) {
1151 new_ts->set_end_note_types_per_minute (tempo.end_note_types_per_minute());
1161 first.set_pulse (0.0);
1162 first.set_minute (minute_at_frame (frame));
1163 first.set_position_lock_style (AudioTime);
1164 first.set_locked_to_meter (true);
1166 /* cannot move the first tempo section */
1167 *static_cast<Tempo*>(&first) = tempo;
1170 recompute_map (_metrics);
1173 PropertyChanged (PropertyChange ());
1177 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1178 , PositionLockStyle pls, bool recompute, bool locked_to_meter)
1180 TempoSection* t = new TempoSection (pulse, minute, tempo, pls, _frame_rate);
1181 t->set_locked_to_meter (locked_to_meter);
1186 if (pls == AudioTime) {
1187 solve_map_minute (_metrics, t, t->minute());
1189 solve_map_pulse (_metrics, t, t->pulse());
1191 recompute_meters (_metrics);
1198 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1200 MeterSection* m = 0;
1202 Glib::Threads::RWLock::WriterLock lm (lock);
1203 m = add_meter_locked (meter, beat, where, frame, pls, true);
1208 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1213 PropertyChanged (PropertyChange ());
1218 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1221 Glib::Threads::RWLock::WriterLock lm (lock);
1222 const double beat = beat_at_bbt_locked (_metrics, where);
1224 if (!ms.initial()) {
1225 remove_meter_locked (ms);
1226 add_meter_locked (meter, beat, where, frame, pls, true);
1228 MeterSection& first (first_meter());
1229 TempoSection& first_t (first_tempo());
1230 /* cannot move the first meter section */
1231 *static_cast<Meter*>(&first) = meter;
1232 first.set_position_lock_style (AudioTime);
1233 first.set_pulse (0.0);
1234 first.set_minute (minute_at_frame (frame));
1235 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1236 first.set_beat (beat);
1237 first_t.set_minute (first.minute());
1238 first_t.set_locked_to_meter (true);
1239 first_t.set_pulse (0.0);
1240 first_t.set_position_lock_style (AudioTime);
1241 recompute_map (_metrics);
1245 PropertyChanged (PropertyChange ());
1249 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1251 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1252 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1253 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1254 TempoSection* mlt = 0;
1256 if (pls == AudioTime) {
1257 /* add meter-locked tempo */
1258 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), AudioTime, true, true);
1266 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1268 bool solved = false;
1270 do_insert (new_meter);
1274 if (pls == AudioTime) {
1275 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1276 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1277 fudge frame so that the meter ends up at its BBT position instead.
1280 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1283 solved = solve_map_bbt (_metrics, new_meter, where);
1284 /* required due to resetting the pulse of meter-locked tempi above.
1285 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1286 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1288 recompute_map (_metrics);
1292 if (!solved && recompute) {
1293 /* if this has failed to solve, there is little we can do other than to ensure that
1294 the new map is recalculated.
1296 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1297 recompute_map (_metrics);
1304 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1306 Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1309 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1310 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1315 Glib::Threads::RWLock::WriterLock lm (lock);
1316 *((Tempo*) t) = newtempo;
1317 recompute_map (_metrics);
1319 PropertyChanged (PropertyChange ());
1326 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1328 Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1331 TempoSection* first;
1332 Metrics::iterator i;
1334 /* find the TempoSection immediately preceding "where"
1337 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1339 if ((*i)->frame() > where) {
1345 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1358 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1368 Glib::Threads::RWLock::WriterLock lm (lock);
1369 /* cannot move the first tempo section */
1370 *((Tempo*)prev) = newtempo;
1371 recompute_map (_metrics);
1374 PropertyChanged (PropertyChange ());
1378 TempoMap::first_meter () const
1380 const MeterSection *m = 0;
1382 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1383 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1388 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1389 abort(); /*NOTREACHED*/
1394 TempoMap::first_meter ()
1396 MeterSection *m = 0;
1398 /* CALLER MUST HOLD LOCK */
1400 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1401 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1406 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1407 abort(); /*NOTREACHED*/
1412 TempoMap::first_tempo () const
1414 const TempoSection *t = 0;
1416 /* CALLER MUST HOLD LOCK */
1418 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1419 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1429 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1430 abort(); /*NOTREACHED*/
1435 TempoMap::first_tempo ()
1437 TempoSection *t = 0;
1439 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1440 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1450 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1451 abort(); /*NOTREACHED*/
1455 TempoMap::recompute_tempi (Metrics& metrics)
1457 TempoSection* prev_t = 0;
1459 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1462 if ((*i)->is_tempo()) {
1463 t = static_cast<TempoSection*> (*i);
1475 if (t->position_lock_style() == AudioTime) {
1476 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1477 if (!t->locked_to_meter()) {
1478 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1482 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1483 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1491 prev_t->set_c (0.0);
1494 /* tempos must be positioned correctly.
1495 the current approach is to use a meter's bbt time as its base position unit.
1496 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1497 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1500 TempoMap::recompute_meters (Metrics& metrics)
1502 MeterSection* meter = 0;
1503 MeterSection* prev_m = 0;
1505 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1506 if (!(*mi)->is_tempo()) {
1507 meter = static_cast<MeterSection*> (*mi);
1508 if (meter->position_lock_style() == AudioTime) {
1510 pair<double, BBT_Time> b_bbt;
1511 TempoSection* meter_locked_tempo = 0;
1512 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1514 if ((*ii)->is_tempo()) {
1515 t = static_cast<TempoSection*> (*ii);
1516 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1517 meter_locked_tempo = t;
1524 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1525 if (beats + prev_m->beat() != meter->beat()) {
1526 /* reordering caused a bbt change */
1528 beats = meter->beat() - prev_m->beat();
1529 b_bbt = make_pair (beats + prev_m->beat()
1530 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1531 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1533 } else if (!meter->initial()) {
1534 b_bbt = make_pair (meter->beat(), meter->bbt());
1535 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1538 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1540 if (meter_locked_tempo) {
1541 meter_locked_tempo->set_pulse (pulse);
1543 meter->set_beat (b_bbt);
1544 meter->set_pulse (pulse);
1549 pair<double, BBT_Time> b_bbt;
1551 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1552 if (beats + prev_m->beat() != meter->beat()) {
1553 /* reordering caused a bbt change */
1554 b_bbt = make_pair (beats + prev_m->beat()
1555 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1557 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1559 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1561 /* shouldn't happen - the first is audio-locked */
1562 pulse = pulse_at_beat_locked (metrics, meter->beat());
1563 b_bbt = make_pair (meter->beat(), meter->bbt());
1566 meter->set_beat (b_bbt);
1567 meter->set_pulse (pulse);
1568 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1577 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1579 /* CALLER MUST HOLD WRITE LOCK */
1581 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1584 /* silly call from Session::process() during startup
1589 recompute_tempi (metrics);
1590 recompute_meters (metrics);
1594 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1596 Glib::Threads::RWLock::ReaderLock lm (lock);
1597 TempoMetric m (first_meter(), first_tempo());
1599 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1600 at something, because we insert the default tempo and meter during
1601 TempoMap construction.
1603 now see if we can find better candidates.
1606 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1608 if ((*i)->frame() > frame) {
1622 /* XX meters only */
1624 TempoMap::metric_at (BBT_Time bbt) const
1626 Glib::Threads::RWLock::ReaderLock lm (lock);
1627 TempoMetric m (first_meter(), first_tempo());
1629 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1630 at something, because we insert the default tempo and meter during
1631 TempoMap construction.
1633 now see if we can find better candidates.
1636 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1638 if (!(*i)->is_tempo()) {
1639 mw = static_cast<MeterSection*> (*i);
1640 BBT_Time section_start (mw->bbt());
1642 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1653 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1654 * @param frame The session frame position.
1655 * @return The beat duration according to the tempo map at the supplied frame.
1657 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1658 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1660 * This function uses both tempo and meter.
1663 TempoMap::beat_at_frame (const framecnt_t& frame) const
1665 Glib::Threads::RWLock::ReaderLock lm (lock);
1667 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1670 /* This function uses both tempo and meter.*/
1672 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1674 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1675 MeterSection* prev_m = 0;
1676 MeterSection* next_m = 0;
1678 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1679 if (!(*i)->is_tempo()) {
1680 if (prev_m && (*i)->minute() > minute) {
1681 next_m = static_cast<MeterSection*> (*i);
1684 prev_m = static_cast<MeterSection*> (*i);
1688 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1690 /* audio locked meters fake their beat */
1691 if (next_m && next_m->beat() < beat) {
1692 return next_m->beat();
1698 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1699 * @param beat The BBT (meter-based) beat.
1700 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1702 * This function uses both tempo and meter.
1705 TempoMap::frame_at_beat (const double& beat) const
1707 Glib::Threads::RWLock::ReaderLock lm (lock);
1709 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1712 /* meter & tempo section based */
1714 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1716 MeterSection* prev_m = 0;
1717 TempoSection* prev_t = 0;
1721 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1722 if (!(*i)->is_tempo()) {
1723 m = static_cast<MeterSection*> (*i);
1724 if (prev_m && m->beat() > beat) {
1734 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1735 if ((*i)->is_tempo()) {
1736 t = static_cast<TempoSection*> (*i);
1742 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1751 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1754 /** Returns a Tempo corresponding to the supplied frame position.
1755 * @param frame The audio frame.
1756 * @return a Tempo according to the tempo map at the supplied frame.
1760 TempoMap::tempo_at_frame (const framepos_t& frame) const
1762 Glib::Threads::RWLock::ReaderLock lm (lock);
1764 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1768 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1770 TempoSection* prev_t = 0;
1774 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1775 if ((*i)->is_tempo()) {
1776 t = static_cast<TempoSection*> (*i);
1780 if ((prev_t) && t->minute() > minute) {
1781 /* t is the section past frame */
1782 return prev_t->tempo_at_minute (minute);
1788 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1791 /** returns the frame at which the supplied tempo occurs, or
1792 * the frame of the last tempo section (search exhausted)
1793 * only the position of the first occurence will be returned
1797 TempoMap::frame_at_tempo (const Tempo& tempo) const
1799 Glib::Threads::RWLock::ReaderLock lm (lock);
1801 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1805 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1807 TempoSection* prev_t = 0;
1808 const double tempo_bpm = tempo.note_types_per_minute();
1810 Metrics::const_iterator i;
1812 for (i = metrics.begin(); i != metrics.end(); ++i) {
1814 if ((*i)->is_tempo()) {
1815 t = static_cast<TempoSection*> (*i);
1823 if (t->note_types_per_minute() == tempo_bpm) {
1828 const double prev_t_bpm = prev_t->note_types_per_minute();
1829 const double prev_t_end_bpm = prev_t->end_note_types_per_minute();
1830 if ((prev_t_bpm > tempo_bpm && prev_t_end_bpm < tempo_bpm)
1831 || (prev_t_bpm < tempo_bpm && prev_t_end_bpm > tempo_bpm)
1832 || (prev_t_end_bpm == tempo_bpm)) {
1834 return prev_t->minute_at_ntpm (tempo_bpm, t->pulse());
1841 return prev_t->minute();
1845 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1847 TempoSection* prev_t = 0;
1851 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1852 if ((*i)->is_tempo()) {
1853 t = static_cast<TempoSection*> (*i);
1857 if ((prev_t) && t->pulse() > pulse) {
1858 /* t is the section past frame */
1859 return prev_t->tempo_at_pulse (pulse);
1865 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1869 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1871 TempoSection* prev_t = 0;
1872 const double tempo_bpm = tempo.note_types_per_minute();
1874 Metrics::const_iterator i;
1876 for (i = metrics.begin(); i != metrics.end(); ++i) {
1878 if ((*i)->is_tempo()) {
1879 t = static_cast<TempoSection*> (*i);
1885 const double t_bpm = t->note_types_per_minute();
1887 if (t_bpm == tempo_bpm) {
1892 const double prev_t_bpm = prev_t->note_types_per_minute();
1894 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1895 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1902 return prev_t->pulse();
1905 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1906 * @param qn the position in quarter note beats.
1907 * @return the Tempo at the supplied quarter-note.
1910 TempoMap::tempo_at_quarter_note (const double& qn) const
1912 Glib::Threads::RWLock::ReaderLock lm (lock);
1914 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1917 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1918 * @param tempo the tempo.
1919 * @return the position in quarter-note beats where the map bpm
1920 * is equal to that of the Tempo. currently ignores note_type.
1923 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1925 Glib::Threads::RWLock::ReaderLock lm (lock);
1927 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1930 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1931 * @param metrics the list of metric sections used to calculate the pulse.
1932 * @param beat The BBT (meter-based) beat.
1933 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1935 * a pulse or whole note is the base musical position of a MetricSection.
1936 * it is equivalent to four quarter notes.
1940 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1942 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1944 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1947 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1948 * @param metrics the list of metric sections used to calculate the beat.
1949 * @param pulse the whole-note pulse.
1950 * @return the meter-based beat at the supplied whole-note pulse.
1952 * a pulse or whole note is the base musical position of a MetricSection.
1953 * it is equivalent to four quarter notes.
1956 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1958 MeterSection* prev_m = 0;
1960 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1962 if (!(*i)->is_tempo()) {
1963 m = static_cast<MeterSection*> (*i);
1964 if (prev_m && m->pulse() > pulse) {
1972 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1976 /* tempo section based */
1978 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1980 /* HOLD (at least) THE READER LOCK */
1981 TempoSection* prev_t = 0;
1983 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1985 if ((*i)->is_tempo()) {
1986 t = static_cast<TempoSection*> (*i);
1990 if (prev_t && t->minute() > minute) {
1991 /*the previous ts is the one containing the frame */
1992 const double ret = prev_t->pulse_at_minute (minute);
1993 /* audio locked section in new meter*/
1994 if (t->pulse() < ret) {
2003 /* treated as constant for this ts */
2004 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
2006 return pulses_in_section + prev_t->pulse();
2009 /* tempo section based */
2011 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2013 /* HOLD THE READER LOCK */
2015 const TempoSection* prev_t = 0;
2017 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2020 if ((*i)->is_tempo()) {
2021 t = static_cast<TempoSection*> (*i);
2025 if (prev_t && t->pulse() > pulse) {
2026 return prev_t->minute_at_pulse (pulse);
2032 /* must be treated as constant, irrespective of _type */
2033 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
2035 return dtime + prev_t->minute();
2038 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
2039 * @param bbt The BBT time (meter-based).
2040 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
2044 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
2046 Glib::Threads::RWLock::ReaderLock lm (lock);
2047 return beat_at_bbt_locked (_metrics, bbt);
2052 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2054 /* CALLER HOLDS READ LOCK */
2056 MeterSection* prev_m = 0;
2058 /* because audio-locked meters have 'fake' integral beats,
2059 there is no pulse offset here.
2063 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2064 if (!(*i)->is_tempo()) {
2065 m = static_cast<MeterSection*> (*i);
2067 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2068 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2076 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2077 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2078 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2083 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2084 * @param beat The BBT (meter-based) beat.
2085 * @return The BBT time (meter-based) at the supplied meter-based beat.
2089 TempoMap::bbt_at_beat (const double& beat)
2091 Glib::Threads::RWLock::ReaderLock lm (lock);
2092 return bbt_at_beat_locked (_metrics, beat);
2096 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2098 /* CALLER HOLDS READ LOCK */
2099 MeterSection* prev_m = 0;
2100 const double beats = max (0.0, b);
2102 MeterSection* m = 0;
2104 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2105 if (!(*i)->is_tempo()) {
2106 m = static_cast<MeterSection*> (*i);
2108 if (m->beat() > beats) {
2109 /* this is the meter after the one our beat is on*/
2119 const double beats_in_ms = beats - prev_m->beat();
2120 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2121 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2122 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2123 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2127 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2128 ret.beats = (uint32_t) floor (remaining_beats);
2129 ret.bars = total_bars;
2131 /* 0 0 0 to 1 1 0 - based mapping*/
2135 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2137 ret.ticks -= BBT_Time::ticks_per_beat;
2140 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2148 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2149 * @param bbt The BBT time (meter-based).
2150 * @return the quarter note beat at the supplied BBT time
2152 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2154 * while the input uses meter, the output does not.
2157 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2159 Glib::Threads::RWLock::ReaderLock lm (lock);
2161 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2165 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2167 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2170 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2173 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2177 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2179 /* CALLER HOLDS READ LOCK */
2181 MeterSection* prev_m = 0;
2183 /* because audio-locked meters have 'fake' integral beats,
2184 there is no pulse offset here.
2188 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2189 if (!(*i)->is_tempo()) {
2190 m = static_cast<MeterSection*> (*i);
2192 if (m->bbt().bars > bbt.bars) {
2200 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2201 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2202 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2207 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2208 * @param qn the quarter-note beat.
2209 * @return The BBT time (meter-based) at the supplied meter-based beat.
2211 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2215 TempoMap::bbt_at_quarter_note (const double& qn)
2217 Glib::Threads::RWLock::ReaderLock lm (lock);
2219 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2222 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2223 * @param metrics The list of metric sections used to determine the result.
2224 * @param pulse The whole-note pulse.
2225 * @return The BBT time at the supplied whole-note pulse.
2227 * a pulse or whole note is the basic musical position of a MetricSection.
2228 * it is equivalent to four quarter notes.
2229 * while the output uses meter, the input does not.
2232 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2234 MeterSection* prev_m = 0;
2236 MeterSection* m = 0;
2238 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2240 if (!(*i)->is_tempo()) {
2241 m = static_cast<MeterSection*> (*i);
2244 double const pulses_to_m = m->pulse() - prev_m->pulse();
2245 if (prev_m->pulse() + pulses_to_m > pulse) {
2246 /* this is the meter after the one our beat is on*/
2257 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2258 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2259 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2260 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2261 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2265 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2266 ret.beats = (uint32_t) floor (remaining_beats);
2267 ret.bars = total_bars;
2269 /* 0 0 0 to 1 1 0 mapping*/
2273 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2275 ret.ticks -= BBT_Time::ticks_per_beat;
2278 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2286 /** Returns the BBT time corresponding to the supplied frame position.
2287 * @param frame the position in audio samples.
2288 * @return the BBT time at the frame position .
2292 TempoMap::bbt_at_frame (framepos_t frame)
2300 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2305 const double minute = minute_at_frame (frame);
2307 Glib::Threads::RWLock::ReaderLock lm (lock);
2309 return bbt_at_minute_locked (_metrics, minute);
2313 TempoMap::bbt_at_frame_rt (framepos_t frame)
2315 const double minute = minute_at_frame (frame);
2317 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2320 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2323 return bbt_at_minute_locked (_metrics, minute);
2327 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2337 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2338 MeterSection* prev_m = 0;
2339 MeterSection* next_m = 0;
2343 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2344 if (!(*i)->is_tempo()) {
2345 m = static_cast<MeterSection*> (*i);
2346 if (prev_m && m->minute() > minute) {
2354 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2356 /* handle frame before first meter */
2357 if (minute < prev_m->minute()) {
2360 /* audio locked meters fake their beat */
2361 if (next_m && next_m->beat() < beat) {
2362 beat = next_m->beat();
2365 beat = max (0.0, beat);
2367 const double beats_in_ms = beat - prev_m->beat();
2368 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2369 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2370 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2371 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2375 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2376 ret.beats = (uint32_t) floor (remaining_beats);
2377 ret.bars = total_bars;
2379 /* 0 0 0 to 1 1 0 - based mapping*/
2383 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2385 ret.ticks -= BBT_Time::ticks_per_beat;
2388 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2396 /** Returns the frame position corresponding to the supplied BBT time.
2397 * @param bbt the position in BBT time.
2398 * @return the frame position at bbt.
2402 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2406 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2411 if (bbt.beats < 1) {
2412 throw std::logic_error ("beats are counted from one");
2417 Glib::Threads::RWLock::ReaderLock lm (lock);
2418 minute = minute_at_bbt_locked (_metrics, bbt);
2421 return frame_at_minute (minute);
2424 /* meter & tempo section based */
2426 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2428 /* HOLD THE READER LOCK */
2430 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2435 * Returns the quarter-note beat position corresponding to the supplied frame.
2437 * @param frame the position in frames.
2438 * @return The quarter-note position of the supplied frame. Ignores meter.
2442 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2444 const double minute = minute_at_frame (frame);
2446 Glib::Threads::RWLock::ReaderLock lm (lock);
2448 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2452 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2454 const double minute = minute_at_frame (frame);
2456 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2459 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2462 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2466 * Returns the frame position corresponding to the supplied quarter-note beat.
2468 * @param quarter_note the quarter-note position.
2469 * @return the frame position of the supplied quarter-note. Ignores meter.
2474 TempoMap::frame_at_quarter_note (const double quarter_note) const
2478 Glib::Threads::RWLock::ReaderLock lm (lock);
2480 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2483 return frame_at_minute (minute);
2486 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2487 * @param beat The BBT (meter-based) beat.
2488 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2490 * a quarter-note may be compared with and assigned to Evoral::Beats.
2494 TempoMap::quarter_note_at_beat (const double beat) const
2496 Glib::Threads::RWLock::ReaderLock lm (lock);
2498 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2501 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2502 * @param quarter_note The position in quarter-note beats.
2503 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2505 * a quarter-note is the musical unit of Evoral::Beats.
2509 TempoMap::beat_at_quarter_note (const double quarter_note) const
2511 Glib::Threads::RWLock::ReaderLock lm (lock);
2513 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2516 /** Returns the duration in frames between two supplied quarter-note beat positions.
2517 * @param start the first position in quarter-note beats.
2518 * @param end the end position in quarter-note beats.
2519 * @return the frame distance ober the quarter-note beats duration.
2521 * use this rather than e.g.
2522 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2523 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2527 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2532 Glib::Threads::RWLock::ReaderLock lm (lock);
2533 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2536 return frame_at_minute (minutes);
2540 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2543 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2547 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2549 Glib::Threads::RWLock::ReaderLock lm (lock);
2551 return quarter_notes_between_frames_locked (_metrics, start, end);
2555 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2557 const TempoSection* prev_t = 0;
2559 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2562 if ((*i)->is_tempo()) {
2563 t = static_cast<TempoSection*> (*i);
2567 if (prev_t && t->frame() > start) {
2574 const double start_qn = prev_t->pulse_at_frame (start);
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() > end) {
2590 const double end_qn = prev_t->pulse_at_frame (end);
2592 return (end_qn - start_qn) * 4.0;
2596 TempoMap::check_solved (const Metrics& metrics) const
2598 TempoSection* prev_t = 0;
2599 MeterSection* prev_m = 0;
2601 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2604 if ((*i)->is_tempo()) {
2605 t = static_cast<TempoSection*> (*i);
2610 /* check ordering */
2611 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2615 /* precision check ensures tempo and frames align.*/
2616 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2617 if (!t->locked_to_meter()) {
2622 /* gradient limit - who knows what it should be?
2623 things are also ok (if a little chaotic) without this
2625 if (fabs (prev_t->c()) > 1000.0) {
2626 //std::cout << "c : " << prev_t->c() << std::endl;
2633 if (!(*i)->is_tempo()) {
2634 m = static_cast<MeterSection*> (*i);
2635 if (prev_m && m->position_lock_style() == AudioTime) {
2636 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2637 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2638 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2640 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2654 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2656 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2658 if ((*i)->is_tempo()) {
2659 t = static_cast<TempoSection*> (*i);
2660 if (t->locked_to_meter()) {
2661 t->set_active (true);
2662 } else if (t->position_lock_style() == AudioTime) {
2663 if (t->frame() < frame) {
2664 t->set_active (false);
2665 t->set_pulse (-1.0);
2666 } else if (t->frame() > frame) {
2667 t->set_active (true);
2668 } else if (t->frame() == frame) {
2678 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2680 TempoSection* prev_t = 0;
2681 TempoSection* section_prev = 0;
2682 double first_m_minute = 0.0;
2683 const bool sml = section->locked_to_meter();
2685 /* can't move a tempo before the first meter */
2686 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2688 if (!(*i)->is_tempo()) {
2689 m = static_cast<MeterSection*> (*i);
2691 first_m_minute = m->minute();
2696 if (!section->initial() && minute <= first_m_minute) {
2700 section->set_active (true);
2701 section->set_minute (minute);
2703 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2705 if ((*i)->is_tempo()) {
2706 t = static_cast<TempoSection*> (*i);
2718 if (t->frame() == frame_at_minute (minute)) {
2722 const bool tlm = t->position_lock_style() == MusicTime;
2724 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2725 section_prev = prev_t;
2727 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2728 if (!section->locked_to_meter()) {
2729 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2734 if (t->position_lock_style() == MusicTime) {
2735 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2736 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2738 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2739 if (!t->locked_to_meter()) {
2740 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2749 recompute_tempi (imaginary);
2751 if (check_solved (imaginary)) {
2754 dunp (imaginary, std::cout);
2758 MetricSectionFrameSorter fcmp;
2759 imaginary.sort (fcmp);
2761 recompute_tempi (imaginary);
2763 if (check_solved (imaginary)) {
2771 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2773 TempoSection* prev_t = 0;
2774 TempoSection* section_prev = 0;
2776 section->set_pulse (pulse);
2778 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2780 if ((*i)->is_tempo()) {
2781 t = static_cast<TempoSection*> (*i);
2792 section_prev = prev_t;
2796 if (t->position_lock_style() == MusicTime) {
2797 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2798 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2800 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2801 if (!t->locked_to_meter()) {
2802 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2811 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2812 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2816 recompute_tempi (imaginary);
2818 if (check_solved (imaginary)) {
2821 dunp (imaginary, std::cout);
2825 MetricSectionSorter cmp;
2826 imaginary.sort (cmp);
2828 recompute_tempi (imaginary);
2830 * XX need a restriction here, but only for this case,
2831 * as audio locked tempos don't interact in the same way.
2833 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2835 * |50 bpm |250 bpm |60 bpm
2836 * drag 250 to the pulse after 60->
2837 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2839 if (check_solved (imaginary)) {
2847 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2849 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2850 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2851 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2855 if (section->initial()) {
2856 /* lock the first tempo to our first meter */
2857 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2862 TempoSection* meter_locked_tempo = 0;
2864 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2866 if ((*ii)->is_tempo()) {
2867 t = static_cast<TempoSection*> (*ii);
2868 if (t->locked_to_meter() && t->frame() == section->frame()) {
2869 meter_locked_tempo = t;
2875 if (!meter_locked_tempo) {
2879 MeterSection* prev_m = 0;
2881 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2882 bool solved = false;
2884 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2886 if (!(*i)->is_tempo()) {
2887 m = static_cast<MeterSection*> (*i);
2889 if (prev_m && !section->initial()) {
2890 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2891 if (beats + prev_m->beat() < section->beat()) {
2892 /* set the section pulse according to its musical position,
2893 * as an earlier time than this has been requested.
2895 const double new_pulse = ((section->beat() - prev_m->beat())
2896 / prev_m->note_divisor()) + prev_m->pulse();
2898 tempo_copy->set_position_lock_style (MusicTime);
2899 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2900 meter_locked_tempo->set_position_lock_style (MusicTime);
2901 section->set_position_lock_style (MusicTime);
2902 section->set_pulse (new_pulse);
2903 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2904 meter_locked_tempo->set_position_lock_style (AudioTime);
2905 section->set_position_lock_style (AudioTime);
2906 section->set_minute (meter_locked_tempo->minute());
2912 Metrics::const_iterator d = future_map.begin();
2913 while (d != future_map.end()) {
2922 /* all is ok. set section's locked tempo if allowed.
2923 possibly disallow if there is an adjacent audio-locked tempo.
2924 XX this check could possibly go. its never actually happened here.
2926 MeterSection* meter_copy = const_cast<MeterSection*>
2927 (&meter_section_at_minute_locked (future_map, section->minute()));
2929 meter_copy->set_minute (minute);
2931 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2932 section->set_minute (minute);
2933 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2934 / prev_m->note_divisor()) + prev_m->pulse());
2935 solve_map_minute (imaginary, meter_locked_tempo, minute);
2940 Metrics::const_iterator d = future_map.begin();
2941 while (d != future_map.end()) {
2951 /* initial (first meter atm) */
2953 tempo_copy->set_minute (minute);
2954 tempo_copy->set_pulse (0.0);
2956 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2957 section->set_minute (minute);
2958 meter_locked_tempo->set_minute (minute);
2959 meter_locked_tempo->set_pulse (0.0);
2960 solve_map_minute (imaginary, meter_locked_tempo, minute);
2965 Metrics::const_iterator d = future_map.begin();
2966 while (d != future_map.end()) {
2975 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2976 section->set_beat (b_bbt);
2977 section->set_pulse (0.0);
2987 MetricSectionFrameSorter fcmp;
2988 imaginary.sort (fcmp);
2990 recompute_meters (imaginary);
2996 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2998 /* disallow setting section to an existing meter's bbt */
2999 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
3001 if (!(*i)->is_tempo()) {
3002 m = static_cast<MeterSection*> (*i);
3003 if (m != section && m->bbt().bars == when.bars) {
3009 MeterSection* prev_m = 0;
3010 MeterSection* section_prev = 0;
3012 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
3014 if (!(*i)->is_tempo()) {
3015 m = static_cast<MeterSection*> (*i);
3021 pair<double, BBT_Time> b_bbt;
3022 double new_pulse = 0.0;
3024 if (prev_m && m->bbt().bars > when.bars && !section_prev){
3025 section_prev = prev_m;
3027 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
3028 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
3029 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
3031 section->set_beat (b_bbt);
3032 section->set_pulse (pulse);
3033 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3037 if (m->position_lock_style() == AudioTime) {
3038 TempoSection* meter_locked_tempo = 0;
3040 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
3042 if ((*ii)->is_tempo()) {
3043 t = static_cast<TempoSection*> (*ii);
3044 if (t->locked_to_meter() && t->frame() == m->frame()) {
3045 meter_locked_tempo = t;
3051 if (!meter_locked_tempo) {
3056 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3058 if (beats + prev_m->beat() != m->beat()) {
3059 /* tempo/ meter change caused a change in beat (bar). */
3061 /* the user has requested that the previous section of music overlaps this one.
3062 we have no choice but to change the bar number here, as being locked to audio means
3063 we must stay where we are on the timeline.
3065 beats = m->beat() - prev_m->beat();
3066 b_bbt = make_pair (beats + prev_m->beat()
3067 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3068 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3070 } else if (!m->initial()) {
3071 b_bbt = make_pair (m->beat(), m->bbt());
3072 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3075 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3078 meter_locked_tempo->set_pulse (new_pulse);
3079 m->set_beat (b_bbt);
3080 m->set_pulse (new_pulse);
3084 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3085 if (beats + prev_m->beat() != m->beat()) {
3086 /* tempo/ meter change caused a change in beat (bar). */
3087 b_bbt = make_pair (beats + prev_m->beat()
3088 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3090 b_bbt = make_pair (beats + prev_m->beat()
3093 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3094 m->set_beat (b_bbt);
3095 m->set_pulse (new_pulse);
3096 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3103 if (!section_prev) {
3105 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3106 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3107 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3109 section->set_beat (b_bbt);
3110 section->set_pulse (pulse);
3111 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3114 MetricSectionSorter cmp;
3115 imaginary.sort (cmp);
3117 recompute_meters (imaginary);
3122 /** places a copy of _metrics into copy and returns a pointer
3123 * to section's equivalent in copy.
3126 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
3128 TempoSection* ret = 0;
3130 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3133 if ((*i)->is_tempo()) {
3134 t = static_cast<TempoSection*> (*i);
3136 ret = new TempoSection (*t);
3137 copy.push_back (ret);
3141 TempoSection* cp = new TempoSection (*t);
3142 copy.push_back (cp);
3144 if (!(*i)->is_tempo()) {
3145 m = static_cast<MeterSection *> (*i);
3146 MeterSection* cp = new MeterSection (*m);
3147 copy.push_back (cp);
3155 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3157 MeterSection* ret = 0;
3159 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3162 if ((*i)->is_tempo()) {
3163 t = static_cast<TempoSection*> (*i);
3164 TempoSection* cp = new TempoSection (*t);
3165 copy.push_back (cp);
3168 if (!(*i)->is_tempo()) {
3169 m = static_cast<MeterSection *> (*i);
3171 ret = new MeterSection (*m);
3172 copy.push_back (ret);
3175 MeterSection* cp = new MeterSection (*m);
3176 copy.push_back (cp);
3183 /** answers the question "is this a valid beat position for this tempo section?".
3184 * it returns true if the tempo section can be moved to the requested bbt position,
3185 * leaving the tempo map in a solved state.
3186 * @param ts the tempo section to be moved
3187 * @param bbt the requested new position for the tempo section
3188 * @return true if the tempo section can be moved to the position, otherwise false.
3191 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3194 TempoSection* tempo_copy = 0;
3197 Glib::Threads::RWLock::ReaderLock lm (lock);
3198 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3204 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3206 Metrics::const_iterator d = copy.begin();
3207 while (d != copy.end()) {
3216 * 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,
3217 * taking any possible reordering as a consequence of this into account.
3218 * @param section - the section to be altered
3219 * @param bbt - the BBT time where the altered tempo will fall
3220 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3222 pair<double, framepos_t>
3223 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3226 pair<double, framepos_t> ret = make_pair (0.0, 0);
3228 Glib::Threads::RWLock::ReaderLock lm (lock);
3230 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3232 const double beat = beat_at_bbt_locked (future_map, bbt);
3234 if (section->position_lock_style() == AudioTime) {
3235 tempo_copy->set_position_lock_style (MusicTime);
3238 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3239 ret.first = tempo_copy->pulse();
3240 ret.second = tempo_copy->frame();
3242 ret.first = section->pulse();
3243 ret.second = section->frame();
3246 Metrics::const_iterator d = future_map.begin();
3247 while (d != future_map.end()) {
3254 /** moves a TempoSection to a specified position.
3255 * @param ts - the section to be moved
3256 * @param frame - the new position in frames for the tempo
3257 * @param sub_num - the snap division to use if using musical time.
3259 * if sub_num is non-zero, the frame position is used to calculate an exact
3262 * -1 | snap to bars (meter-based)
3263 * 0 | no snap - use audio frame for musical position
3264 * 1 | snap to meter-based (BBT) beat
3265 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3267 * this follows the snap convention in the gui.
3268 * if sub_num is zero, the musical position will be taken from the supplied frame.
3271 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3275 if (ts->position_lock_style() == MusicTime) {
3277 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3278 Glib::Threads::RWLock::WriterLock lm (lock);
3279 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3281 tempo_copy->set_position_lock_style (AudioTime);
3283 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3284 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3285 const double pulse = pulse_at_beat_locked (future_map, beat);
3287 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3288 solve_map_pulse (_metrics, ts, pulse);
3289 recompute_meters (_metrics);
3297 Glib::Threads::RWLock::WriterLock lm (lock);
3298 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3300 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3302 /* We're moving the object that defines the grid while snapping to it...
3303 * Placing the ts at the beat corresponding to the requested frame may shift the
3304 * grid in such a way that the mouse is left hovering over a completerly different division,
3305 * causing jittering when the mouse next moves (esp. large tempo deltas).
3307 * This alters the snap behaviour slightly in that we snap to beat divisions
3308 * in the future map rather than the existing one.
3310 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3311 const framepos_t snapped_frame = frame_at_minute (minute_at_pulse_locked (future_map, qn / 4.0));
3313 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3314 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3315 ts->set_pulse (qn / 4.0);
3316 recompute_meters (_metrics);
3319 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3320 recompute_meters (_metrics);
3326 Metrics::const_iterator d = future_map.begin();
3327 while (d != future_map.end()) {
3332 MetricPositionChanged (PropertyChange ()); // Emit Signal
3335 /** moves a MeterSection to a specified position.
3336 * @param ms - the section to be moved
3337 * @param frame - the new position in frames for the meter
3339 * as a meter cannot snap to anything but bars,
3340 * the supplied frame is rounded to the nearest bar, possibly
3341 * leaving the meter position unchanged.
3344 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3348 if (ms->position_lock_style() == AudioTime) {
3351 Glib::Threads::RWLock::WriterLock lm (lock);
3352 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3354 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3355 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3356 recompute_tempi (_metrics);
3361 Glib::Threads::RWLock::WriterLock lm (lock);
3362 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3364 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3365 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3367 if (solve_map_bbt (future_map, copy, bbt)) {
3368 solve_map_bbt (_metrics, ms, bbt);
3369 recompute_tempi (_metrics);
3374 Metrics::const_iterator d = future_map.begin();
3375 while (d != future_map.end()) {
3380 MetricPositionChanged (PropertyChange ()); // Emit Signal
3384 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm, bool change_end)
3387 bool can_solve = false;
3389 Glib::Threads::RWLock::WriterLock lm (lock);
3390 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3392 if (change_end && tempo_copy->type() == TempoSection::Constant) {
3393 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3394 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3395 } else if (change_end) {
3396 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3398 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3401 recompute_tempi (future_map);
3403 if (check_solved (future_map)) {
3404 if (change_end && ts->type() == TempoSection::Constant) {
3405 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3406 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3407 } else if (change_end) {
3408 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3410 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3413 recompute_map (_metrics);
3418 Metrics::const_iterator d = future_map.begin();
3419 while (d != future_map.end()) {
3424 MetricPositionChanged (PropertyChange ()); // Emit Signal
3431 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3434 Ts (future prev_t) Tnext
3437 |----------|----------
3444 Glib::Threads::RWLock::WriterLock lm (lock);
3450 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3456 /* minimum allowed measurement distance in frames */
3457 framepos_t const min_dframe = 2;
3461 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3463 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
3464 / (double) (end_frame - prev_t->frame()));
3466 new_bpm = prev_t->note_types_per_minute();
3469 std::cout << "new bpm : " << new_bpm << std::endl;
3470 new_bpm = min (new_bpm, (double) 1000.0);
3472 /* don't clamp and proceed here.
3473 testing has revealed that this can go negative,
3474 which is an entirely different thing to just being too low.
3477 if (new_bpm < 0.5) {
3481 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3482 prev_t->set_note_types_per_minute (new_bpm);
3484 prev_t->set_end_note_types_per_minute (new_bpm);
3485 prev_t->set_note_types_per_minute (new_bpm);
3488 recompute_tempi (future_map);
3489 recompute_meters (future_map);
3491 if (check_solved (future_map)) {
3492 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3493 ts->set_note_types_per_minute (new_bpm);
3495 ts->set_end_note_types_per_minute (new_bpm);
3496 ts->set_note_types_per_minute (new_bpm);
3498 recompute_tempi (_metrics);
3499 recompute_meters (_metrics);
3503 MetricPositionChanged (PropertyChange ()); // Emit Signal
3506 Metrics::const_iterator d = future_map.begin();
3507 while (d != future_map.end()) {
3514 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3517 Ts (future prev_t) Tnext
3520 |----------|----------
3527 Glib::Threads::RWLock::WriterLock lm (lock);
3533 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3536 TempoSection* next_t = 0;
3537 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3538 if ((*i)->is_tempo() && (*i)->minute() > prev_t->minute()) {
3539 next_t = static_cast<TempoSection*> (*i);
3553 /* minimum allowed measurement distance in frames */
3554 framepos_t const min_dframe = 2;
3557 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3558 new_bpm = prev_t->end_note_types_per_minute() * ((frame - prev_t->frame())
3559 / (double) (end_frame - prev_t->frame()));
3561 new_bpm = prev_t->end_note_types_per_minute();
3564 new_bpm = min (new_bpm, (double) 1000.0);
3566 if (new_bpm < 0.5) {
3570 prev_t->set_end_note_types_per_minute (new_bpm);
3572 recompute_tempi (future_map);
3573 recompute_meters (future_map);
3575 if (check_solved (future_map)) {
3576 ts->set_end_note_types_per_minute (new_bpm);
3578 recompute_tempi (_metrics);
3579 recompute_meters (_metrics);
3583 MetricPositionChanged (PropertyChange ()); // Emit Signal
3586 Metrics::const_iterator d = future_map.begin();
3587 while (d != future_map.end()) {
3594 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
3596 TempoSection* next_t = 0;
3597 TempoSection* next_to_next_t = 0;
3599 bool can_solve = false;
3601 /* minimum allowed measurement distance in frames */
3602 framepos_t const min_dframe = 2;
3605 Glib::Threads::RWLock::WriterLock lm (lock);
3610 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3611 TempoSection* prev_to_prev_t = 0;
3612 const frameoffset_t fr_off = end_frame - frame;
3618 if (tempo_copy->pulse() > 0.0) {
3619 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
3622 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3623 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3624 next_t = static_cast<TempoSection*> (*i);
3633 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3634 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3635 next_to_next_t = static_cast<TempoSection*> (*i);
3640 if (!next_to_next_t) {
3641 std::cout << "no next to next t" << std::endl;
3645 double prev_contribution = 0.0;
3647 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3648 prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3651 const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
3654 framepos_t old_tc_pos = tempo_copy->frame();
3655 framepos_t old_next_pos = next_t->frame();
3656 double old_next_minute = next_t->minute();
3657 framepos_t old_next_to_next_pos = next_to_next_t->frame();
3658 double old_next_to_next_minute = next_to_next_t->minute();
3661 double new_next_bpm;
3662 double new_copy_end_bpm;
3664 if (frame > tempo_copy->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > tempo_copy->frame() + min_dframe) {
3665 new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
3666 / (double) (end_frame - tempo_copy->frame()));
3668 new_bpm = tempo_copy->note_types_per_minute();
3671 /* don't clamp and proceed here.
3672 testing has revealed that this can go negative,
3673 which is an entirely different thing to just being too low.
3675 if (new_bpm < 0.5) {
3679 new_bpm = min (new_bpm, (double) 1000.0);
3681 tempo_copy->set_note_types_per_minute (new_bpm);
3682 if (tempo_copy->type() == TempoSection::Constant) {
3683 tempo_copy->set_end_note_types_per_minute (new_bpm);
3685 recompute_tempi (future_map);
3687 if (check_solved (future_map)) {
3692 ts->set_note_types_per_minute (new_bpm);
3693 if (ts->type() == TempoSection::Constant) {
3694 ts->set_end_note_types_per_minute (new_bpm);
3696 recompute_map (_metrics);
3700 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3701 if (frame > tempo_copy->frame() + min_dframe && end_frame > tempo_copy->frame() + min_dframe) {
3703 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3704 / (double) ((old_next_to_next_minute) - old_next_minute));
3707 new_next_bpm = next_t->note_types_per_minute();
3710 next_t->set_note_types_per_minute (new_next_bpm);
3711 recompute_tempi (future_map);
3713 if (check_solved (future_map)) {
3714 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3715 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3716 next_t = static_cast<TempoSection*> (*i);
3724 next_t->set_note_types_per_minute (new_next_bpm);
3725 recompute_map (_metrics);
3729 double next_frame_ratio = 1.0;
3730 double copy_frame_ratio = 1.0;
3732 if (next_to_next_t) {
3733 next_frame_ratio = (next_to_next_t->frame() - old_next_pos) / (double) (old_next_to_next_pos - old_next_pos);
3735 copy_frame_ratio = ((old_tc_pos - next_t->frame()) / (double) (old_tc_pos - old_next_pos));
3738 //next_frame_ratio = (((next_to_next_pos - fr_off) - next_t->frame()) / (double) ((next_to_next_pos) - next_t->frame()));
3739 //next_pulse_ratio = (start_pulse / end_pulse);
3742 new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
3743 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
3745 next_t->set_note_types_per_minute (new_next_bpm);
3746 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3747 recompute_tempi (future_map);
3749 if (check_solved (future_map)) {
3750 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3751 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3752 next_t = static_cast<TempoSection*> (*i);
3760 next_t->set_note_types_per_minute (new_next_bpm);
3761 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3762 recompute_map (_metrics);
3768 Metrics::const_iterator d = future_map.begin();
3769 while (d != future_map.end()) {
3774 MetricPositionChanged (PropertyChange ()); // Emit Signal
3779 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3780 * the supplied frame, possibly returning a negative value.
3782 * @param frame The session frame position.
3783 * @param sub_num The subdivision to use when rounding the beat.
3784 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3785 * Positive integers indicate quarter note (non BBT) divisions.
3786 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3787 * @return The beat position of the supplied frame.
3789 * when working to a musical grid, the use of sub_nom indicates that
3790 * the position should be interpreted musically.
3792 * it effectively snaps to meter bars, meter beats or quarter note divisions
3793 * (as per current gui convention) and returns a musical position independent of frame rate.
3795 * If the supplied frame lies before the first meter, the return will be negative,
3796 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3797 * the continuation of the tempo curve (backwards).
3799 * This function is sensitive to tempo and meter.
3802 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3804 Glib::Threads::RWLock::ReaderLock lm (lock);
3806 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3810 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3812 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3815 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3816 * the supplied frame, possibly returning a negative value.
3818 * @param frame The session frame position.
3819 * @param sub_num The subdivision to use when rounding the quarter note.
3820 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3821 * Positive integers indicate quarter note (non BBT) divisions.
3822 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3823 * @return The quarter note position of the supplied frame.
3825 * When working to a musical grid, the use of sub_nom indicates that
3826 * the frame position should be interpreted musically.
3828 * it effectively snaps to meter bars, meter beats or quarter note divisions
3829 * (as per current gui convention) and returns a musical position independent of frame rate.
3831 * If the supplied frame lies before the first meter, the return will be negative,
3832 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3833 * the continuation of the tempo curve (backwards).
3835 * This function is tempo-sensitive.
3838 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3840 Glib::Threads::RWLock::ReaderLock lm (lock);
3842 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3846 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3848 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3851 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3852 } else if (sub_num == 1) {
3853 /* the gui requested exact musical (BBT) beat */
3854 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3855 } else if (sub_num == -1) {
3857 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3861 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3863 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3865 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3875 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3876 * @param pos the frame position in the tempo map.
3877 * @param bbt the distance in BBT time from pos to calculate.
3878 * @param dir the rounding direction..
3879 * @return the duration in frames between pos and bbt
3882 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3884 Glib::Threads::RWLock::ReaderLock lm (lock);
3886 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3888 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3891 pos_bbt.bars += bbt.bars;
3893 pos_bbt.ticks += bbt.ticks;
3894 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3896 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3899 pos_bbt.beats += bbt.beats;
3900 if ((double) pos_bbt.beats > divisions) {
3902 pos_bbt.beats -= divisions;
3904 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3906 return pos_bbt_frame - pos;
3910 if (pos_bbt.bars <= bbt.bars) {
3913 pos_bbt.bars -= bbt.bars;
3916 if (pos_bbt.ticks < bbt.ticks) {
3917 if (pos_bbt.bars > 1) {
3918 if (pos_bbt.beats == 1) {
3920 pos_bbt.beats = divisions;
3924 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3930 pos_bbt.ticks -= bbt.ticks;
3933 if (pos_bbt.beats <= bbt.beats) {
3934 if (pos_bbt.bars > 1) {
3936 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3941 pos_bbt.beats -= bbt.beats;
3944 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3951 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3953 return round_to_type (fr, dir, Bar);
3957 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3959 return round_to_type (fr, dir, Beat);
3963 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3965 Glib::Threads::RWLock::ReaderLock lm (lock);
3966 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);
3967 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3968 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3970 ticks -= beats * BBT_Time::ticks_per_beat;
3973 /* round to next (or same iff dir == RoundUpMaybe) */
3975 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3977 if (mod == 0 && dir == RoundUpMaybe) {
3978 /* right on the subdivision, which is fine, so do nothing */
3980 } else if (mod == 0) {
3981 /* right on the subdivision, so the difference is just the subdivision ticks */
3982 ticks += ticks_one_subdivisions_worth;
3985 /* not on subdivision, compute distance to next subdivision */
3987 ticks += ticks_one_subdivisions_worth - mod;
3990 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3991 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3992 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3993 // But I'm keeping it around, until we determine there are no terrible consequences.
3994 // if (ticks >= BBT_Time::ticks_per_beat) {
3995 // ticks -= BBT_Time::ticks_per_beat;
3998 } else if (dir < 0) {
4000 /* round to previous (or same iff dir == RoundDownMaybe) */
4002 uint32_t difference = ticks % ticks_one_subdivisions_worth;
4004 if (difference == 0 && dir == RoundDownAlways) {
4005 /* right on the subdivision, but force-rounding down,
4006 so the difference is just the subdivision ticks */
4007 difference = ticks_one_subdivisions_worth;
4010 if (ticks < difference) {
4011 ticks = BBT_Time::ticks_per_beat - ticks;
4013 ticks -= difference;
4017 /* round to nearest */
4020 /* compute the distance to the previous and next subdivision */
4022 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
4024 /* closer to the next subdivision, so shift forward */
4026 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
4028 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
4030 if (ticks > BBT_Time::ticks_per_beat) {
4032 ticks -= BBT_Time::ticks_per_beat;
4033 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
4036 } else if (rem > 0) {
4038 /* closer to previous subdivision, so shift backward */
4042 /* can't go backwards past zero, so ... */
4043 return MusicFrame (0, 0);
4045 /* step back to previous beat */
4047 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4048 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4050 ticks = lrint (ticks - rem);
4051 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4054 /* on the subdivision, do nothing */
4058 MusicFrame ret (0, 0);
4059 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4060 ret.division = sub_num;
4066 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
4068 Glib::Threads::RWLock::ReaderLock lm (lock);
4069 const double minute = minute_at_frame (frame);
4070 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4071 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
4072 MusicFrame ret (0, 0);
4079 /* find bar previous to 'frame' */
4085 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4089 } else if (dir > 0) {
4090 /* find bar following 'frame' */
4095 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4099 /* true rounding: find nearest bar */
4100 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4103 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4105 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4107 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4108 ret.frame = next_ft;
4113 ret.frame = prev_ft;
4125 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
4128 } else if (dir > 0) {
4129 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
4133 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
4140 return MusicFrame (0, 0);
4144 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4145 framepos_t lower, framepos_t upper, uint32_t bar_mod)
4147 Glib::Threads::RWLock::ReaderLock lm (lock);
4148 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
4150 /* although the map handles negative beats, bbt doesn't. */
4155 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
4159 while (pos >= 0 && pos < upper) {
4160 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
4161 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4162 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4163 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4165 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4169 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
4174 bbt.bars -= bbt.bars % bar_mod;
4178 while (pos >= 0 && pos < upper) {
4179 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4180 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4181 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4182 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4183 bbt.bars += bar_mod;
4189 TempoMap::tempo_section_at_frame (framepos_t frame) const
4191 Glib::Threads::RWLock::ReaderLock lm (lock);
4193 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4197 TempoMap::tempo_section_at_frame (framepos_t frame)
4199 Glib::Threads::RWLock::ReaderLock lm (lock);
4201 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4205 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4207 TempoSection* prev = 0;
4211 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4213 if ((*i)->is_tempo()) {
4214 t = static_cast<TempoSection*> (*i);
4218 if (prev && t->minute() > minute) {
4228 abort(); /*NOTREACHED*/
4234 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4236 TempoSection* prev = 0;
4240 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4242 if ((*i)->is_tempo()) {
4243 t = static_cast<TempoSection*> (*i);
4247 if (prev && t->minute() > minute) {
4257 abort(); /*NOTREACHED*/
4263 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4265 TempoSection* prev_t = 0;
4266 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4270 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4271 if ((*i)->is_tempo()) {
4272 t = static_cast<TempoSection*> (*i);
4278 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4289 TempoMap::next_tempo_section (TempoSection* ts) const
4295 Glib::Threads::RWLock::ReaderLock lm (lock);
4297 TempoSection* prev = 0;
4299 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4301 if ((*i)->is_tempo()) {
4302 TempoSection* t = static_cast<TempoSection*> (*i);
4308 if (prev && prev == ts) {
4319 abort(); /*NOTREACHED*/
4324 /* don't use this to calculate length (the tempo is only correct for this frame).
4325 do that stuff based on the beat_at_frame and frame_at_beat api
4328 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4330 Glib::Threads::RWLock::ReaderLock lm (lock);
4332 const TempoSection* ts_at = 0;
4333 const TempoSection* ts_after = 0;
4334 Metrics::const_iterator i;
4337 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4339 if ((*i)->is_tempo()) {
4340 t = static_cast<TempoSection*> (*i);
4344 if (ts_at && (*i)->frame() > frame) {
4354 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4356 /* must be treated as constant tempo */
4357 return ts_at->frames_per_quarter_note (_frame_rate);
4361 TempoMap::meter_section_at_frame (framepos_t frame) const
4363 Glib::Threads::RWLock::ReaderLock lm (lock);
4364 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4368 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4370 Metrics::const_iterator i;
4371 MeterSection* prev = 0;
4375 for (i = metrics.begin(); i != metrics.end(); ++i) {
4377 if (!(*i)->is_tempo()) {
4378 m = static_cast<MeterSection*> (*i);
4380 if (prev && (*i)->minute() > minute) {
4390 abort(); /*NOTREACHED*/
4397 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4399 MeterSection* prev_m = 0;
4401 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4403 if (!(*i)->is_tempo()) {
4404 m = static_cast<MeterSection*> (*i);
4405 if (prev_m && m->beat() > beat) {
4416 TempoMap::meter_section_at_beat (double beat) const
4418 Glib::Threads::RWLock::ReaderLock lm (lock);
4419 return meter_section_at_beat_locked (_metrics, beat);
4423 TempoMap::meter_at_frame (framepos_t frame) const
4425 TempoMetric m (metric_at (frame));
4430 TempoMap::fix_legacy_session ()
4432 MeterSection* prev_m = 0;
4433 TempoSection* prev_t = 0;
4434 bool have_initial_t = false;
4436 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4440 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4442 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4445 m->set_minute (0.0);
4446 m->set_position_lock_style (AudioTime);
4451 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4452 + (m->bbt().beats - 1)
4453 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4455 m->set_beat (start);
4456 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4457 + (m->bbt().beats - 1)
4458 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4459 m->set_pulse (start_beat / prev_m->note_divisor());
4462 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4470 t->set_minute (0.0);
4471 t->set_position_lock_style (AudioTime);
4473 have_initial_t = true;
4478 /* some 4.x sessions have no initial (non-movable) tempo. */
4479 if (!have_initial_t) {
4480 prev_t->set_pulse (0.0);
4481 prev_t->set_minute (0.0);
4482 prev_t->set_position_lock_style (AudioTime);
4483 prev_t->set_initial (true);
4484 prev_t->set_locked_to_meter (true);
4485 have_initial_t = true;
4488 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4489 + (t->legacy_bbt().beats - 1)
4490 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4492 t->set_pulse (beat / prev_m->note_divisor());
4494 /* really shouldn't happen but.. */
4495 t->set_pulse (beat / 4.0);
4503 TempoMap::fix_legacy_end_session ()
4505 TempoSection* prev_t = 0;
4507 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4510 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4517 if (prev_t->type() != TempoSection::Constant) {
4518 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4528 TempoMap::get_state ()
4530 Metrics::const_iterator i;
4531 XMLNode *root = new XMLNode ("TempoMap");
4534 Glib::Threads::RWLock::ReaderLock lm (lock);
4535 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4536 root->add_child_nocopy ((*i)->get_state());
4544 TempoMap::set_state (const XMLNode& node, int /*version*/)
4547 Glib::Threads::RWLock::WriterLock lm (lock);
4550 XMLNodeConstIterator niter;
4551 Metrics old_metrics (_metrics);
4554 nlist = node.children();
4556 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4557 XMLNode* child = *niter;
4559 if (child->name() == TempoSection::xml_state_node_name) {
4562 TempoSection* ts = new TempoSection (*child, _frame_rate);
4563 _metrics.push_back (ts);
4566 catch (failed_constructor& err){
4567 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4568 _metrics = old_metrics;
4569 old_metrics.clear();
4573 } else if (child->name() == MeterSection::xml_state_node_name) {
4576 MeterSection* ms = new MeterSection (*child, _frame_rate);
4577 _metrics.push_back (ms);
4580 catch (failed_constructor& err) {
4581 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4582 _metrics = old_metrics;
4583 old_metrics.clear();
4589 if (niter == nlist.end()) {
4590 MetricSectionSorter cmp;
4591 _metrics.sort (cmp);
4594 /* check for legacy sessions where bbt was the base musical unit for tempo */
4595 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4597 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4598 if (t->legacy_bbt().bars != 0) {
4599 fix_legacy_session();
4603 if (t->legacy_end()) {
4604 fix_legacy_end_session();
4612 /* check for multiple tempo/meters at the same location, which
4613 ardour2 somehow allowed.
4616 Metrics::iterator prev = _metrics.end();
4617 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4618 if (prev != _metrics.end()) {
4620 MeterSection* prev_m;
4622 TempoSection* prev_t;
4623 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4624 if (prev_m->pulse() == ms->pulse()) {
4625 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4626 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4629 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4630 if (prev_t->pulse() == ts->pulse()) {
4631 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4632 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4640 recompute_map (_metrics);
4642 Metrics::const_iterator d = old_metrics.begin();
4643 while (d != old_metrics.end()) {
4647 old_metrics.clear ();
4650 PropertyChanged (PropertyChange ());
4656 TempoMap::dump (std::ostream& o) const
4658 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4659 const MeterSection* m;
4660 const TempoSection* t;
4661 const TempoSection* prev_t = 0;
4663 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4665 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4666 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4667 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4668 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4669 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4671 o << " current start : " << t->note_types_per_minute()
4672 << " current end : " << t->end_note_types_per_minute()
4673 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4674 o << " previous : " << prev_t->note_types_per_minute()
4675 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4676 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4677 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4678 << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4679 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4682 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4683 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4684 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4685 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4688 o << "------" << std::endl;
4692 TempoMap::n_tempos() const
4694 Glib::Threads::RWLock::ReaderLock lm (lock);
4697 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4698 if ((*i)->is_tempo()) {
4707 TempoMap::n_meters() const
4709 Glib::Threads::RWLock::ReaderLock lm (lock);
4712 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4713 if (!(*i)->is_tempo()) {
4722 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4724 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4725 if ((*i)->frame() >= where && !(*i)->initial ()) {
4729 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4730 gui_set_meter_position (ms, (*i)->frame() + amount);
4733 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4734 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4739 PropertyChanged (PropertyChange ());
4743 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4747 std::list<MetricSection*> metric_kill_list;
4749 TempoSection* last_tempo = NULL;
4750 MeterSection* last_meter = NULL;
4751 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4752 bool meter_after = false; // is there a meter marker likewise?
4754 Glib::Threads::RWLock::WriterLock lm (lock);
4755 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4756 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4757 metric_kill_list.push_back(*i);
4758 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4761 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4765 else if ((*i)->frame() >= where) {
4766 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4767 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4768 if ((*i)->frame() == where) {
4769 // marker was immediately after end of range
4770 tempo_after = dynamic_cast<TempoSection*> (*i);
4771 meter_after = dynamic_cast<MeterSection*> (*i);
4777 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4778 if (last_tempo && !tempo_after) {
4779 metric_kill_list.remove(last_tempo);
4780 last_tempo->set_minute (minute_at_frame (where));
4783 if (last_meter && !meter_after) {
4784 metric_kill_list.remove(last_meter);
4785 last_meter->set_minute (minute_at_frame (where));
4789 //remove all the remaining metrics
4790 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4791 _metrics.remove(*i);
4796 recompute_map (_metrics);
4799 PropertyChanged (PropertyChange ());
4803 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4804 * pos can be -ve, if required.
4807 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4809 Glib::Threads::RWLock::ReaderLock lm (lock);
4810 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4812 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4816 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4818 Glib::Threads::RWLock::ReaderLock lm (lock);
4820 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4821 pos_bbt.ticks += op.ticks;
4822 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4824 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4826 pos_bbt.beats += op.beats;
4827 /* the meter in effect will start on the bar */
4828 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();
4829 while (pos_bbt.beats >= divisions_per_bar + 1) {
4831 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4832 pos_bbt.beats -= divisions_per_bar;
4834 pos_bbt.bars += op.bars;
4836 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4839 /** Count the number of beats that are equivalent to distance when going forward,
4843 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4845 Glib::Threads::RWLock::ReaderLock lm (lock);
4847 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4851 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4857 operator<< (std::ostream& o, const Meter& m) {
4858 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4862 operator<< (std::ostream& o, const Tempo& t) {
4863 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4867 operator<< (std::ostream& o, const MetricSection& section) {
4869 o << "MetricSection @ " << section.frame() << ' ';
4871 const TempoSection* ts;
4872 const MeterSection* ms;
4874 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4875 o << *((const Tempo*) ts);
4876 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4877 o << *((const Meter*) ms);