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;
160 if ((prop = node.property ("movable")) == 0) {
161 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
162 throw failed_constructor();
165 set_initial (!string_is_affirmative (prop->value()));
167 if ((prop = node.property ("active")) == 0) {
168 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
171 set_active (string_is_affirmative (prop->value()));
174 if ((prop = node.property ("tempo-type")) == 0) {
177 _type = Type (string_2_enum (prop->value(), _type));
180 if ((prop = node.property ("lock-style")) == 0) {
182 set_position_lock_style (MusicTime);
184 set_position_lock_style (AudioTime);
187 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
190 if ((prop = node.property ("locked-to-meter")) == 0) {
192 set_locked_to_meter (true);
194 set_locked_to_meter (false);
197 set_locked_to_meter (string_is_affirmative (prop->value()));
200 /* 5.5 marked initial tempo as not locked to meter. this should always be true anyway */
202 set_locked_to_meter (true);
207 TempoSection::get_state() const
209 XMLNode *root = new XMLNode (xml_state_node_name);
213 snprintf (buf, sizeof (buf), "%lf", pulse());
214 root->add_property ("pulse", buf);
215 snprintf (buf, sizeof (buf), "%li", frame());
216 root->add_property ("frame", buf);
217 snprintf (buf, sizeof (buf), "%lf", _note_types_per_minute);
218 root->add_property ("beats-per-minute", buf);
219 snprintf (buf, sizeof (buf), "%lf", _note_type);
220 root->add_property ("note-type", buf);
221 snprintf (buf, sizeof (buf), "%lf", _end_note_types_per_minute);
222 root->add_property ("end-beats-per-minute", buf);
223 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
224 root->add_property ("movable", buf);
225 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
226 root->add_property ("active", buf);
227 root->add_property ("tempo-type", enum_2_string (_type));
228 root->add_property ("lock-style", enum_2_string (position_lock_style()));
229 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
235 TempoSection::set_type (Type type)
240 /** returns the Tempo at the session-relative minute.
243 TempoSection::tempo_at_minute (const double& m) const
245 const bool constant = _type == Constant || _c == 0.0 || (initial() && m < minute());
247 return Tempo (note_types_per_minute(), note_type());
250 return Tempo (_tempo_at_time (m - minute()), _note_type, _end_note_types_per_minute);
253 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
254 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
255 * @param p the pulse used to calculate the returned minute for constant tempi
256 * @return the minute at the supplied tempo
258 * note that the note_type is currently ignored in this function. see below.
262 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
263 * there should be no ramp between the two even if we are ramped.
264 * in other words a ramp should only place a curve on note_types_per_minute.
265 * we should be able to use Tempo note type here, but the above
266 * complicates things a bit.
269 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
271 const bool constant = _type == Constant || _c == 0.0 || (initial() && p < pulse());
273 return ((p - pulse()) / pulses_per_minute()) + minute();
276 return _time_at_tempo (ntpm) + minute();
279 /** returns the Tempo at the supplied whole-note pulse.
282 TempoSection::tempo_at_pulse (const double& p) const
284 const bool constant = _type == Constant || _c == 0.0 || (initial() && p < pulse());
287 return Tempo (note_types_per_minute(), note_type());
290 return Tempo (_tempo_at_pulse (p - pulse()), _note_type, _end_note_types_per_minute);
293 /** returns the whole-note pulse where a tempo in note types per minute occurs.
294 * constant tempi require minute m.
295 * @param ntpm the note types per minute value used to calculate the returned pulse
296 * @param m the minute used to calculate the returned pulse if the tempo is constant
297 * @return the whole-note pulse at the supplied tempo
299 * note that note_type is currently ignored in this function. see minute_at_tempo().
301 * for constant tempi, this is anaologous to pulse_at_minute().
304 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
306 const bool constant = _type == Constant || _c == 0.0 || (initial() && m < minute());
308 return ((m - minute()) * pulses_per_minute()) + pulse();
311 return _pulse_at_tempo (ntpm) + pulse();
314 /** returns the whole-note pulse at the supplied session-relative minute.
317 TempoSection::pulse_at_minute (const double& m) const
319 const bool constant = _type == Constant || _c == 0.0 || (initial() && m < minute());
321 return ((m - minute()) * pulses_per_minute()) + pulse();
324 return _pulse_at_time (m - minute()) + pulse();
327 /** returns the session-relative minute at the supplied whole-note pulse.
330 TempoSection::minute_at_pulse (const double& p) const
332 const bool constant = _type == Constant || _c == 0.0 || (initial() && p < pulse());
334 return ((p - pulse()) / pulses_per_minute()) + minute();
337 return _time_at_pulse (p - pulse()) + minute();
340 /** returns thw whole-note pulse at session frame position f.
341 * @param f the frame position.
342 * @return the position in whole-note pulses corresponding to f
344 * for use with musical units whose granularity is coarser than frames (e.g. ticks)
347 TempoSection::pulse_at_frame (const framepos_t& f) const
349 const bool constant = _type == Constant || _c == 0.0 || (initial() && f < frame());
351 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
354 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
358 TempoSection::frame_at_pulse (const double& p) const
360 const bool constant = _type == Constant || _c == 0.0 || (initial() && p < pulse());
362 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
365 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
373 Tt----|-----------------*|
374 Ta----|--------------|* |
380 _______________|___|____
381 time a t (next tempo)
384 Duration in beats at time a is the integral of some Tempo function.
385 In our case, the Tempo function (Tempo at time t) is
388 with function constant
393 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
394 b(t) = T0(e^(ct) - 1) / c
396 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:
397 t(b) = log((c.b / T0) + 1) / c
399 The time t at which Tempo T occurs is a as above:
400 t(T) = log(T / T0) / c
402 The beat at which a Tempo T occurs is:
405 The Tempo at which beat b occurs is:
408 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
409 Our problem is that we usually don't know t.
410 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.
411 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
412 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
414 By substituting our expanded t as a in the c function above, our problem is reduced to:
415 c = T0 (e^(log (Ta / T0)) - 1) / b
417 Of course the word 'beat' has been left loosely defined above.
418 In music, a beat is defined by the musical pulse (which comes from the tempo)
419 and the meter in use at a particular time (how many pulse divisions there are in one bar).
420 It would be more accurate to substitute the work 'pulse' for 'beat' above.
424 We can now store c for future time calculations.
425 If the following tempo section (the one that defines c in conjunction with this one)
426 is changed or moved, c is no longer valid.
428 The public methods are session-relative.
430 Most of this stuff is taken from this paper:
433 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
436 Zurich University of Arts
437 Institute for Computer Music and Sound Technology
439 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
443 /** compute this ramp's function constant from some tempo-pulse point
444 * @param end_npm end tempo (in note types per minute)
445 * @param end_pulse duration (pulses into global start) of some other position.
446 * @return the calculated function constant
449 TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const
451 if (note_types_per_minute() == end_npm || _type == Constant) {
455 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
456 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
459 /** compute the function constant from some tempo-time point.
460 * @param end_npm tempo (note types/min.)
461 * @param end_minute distance (in minutes) from session origin
462 * @return the calculated function constant
465 TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const
467 if (note_types_per_minute() == end_npm || _type == Constant) {
471 return c_func (end_npm, end_minute - minute());
474 /* position function */
476 TempoSection::a_func (double end_npm, double c) const
478 return log (end_npm / note_types_per_minute()) / c;
481 /*function constant*/
483 TempoSection::c_func (double end_npm, double end_time) const
485 return log (end_npm / note_types_per_minute()) / end_time;
488 /* tempo in note types per minute at time in minutes */
490 TempoSection::_tempo_at_time (const double& time) const
492 return exp (_c * time) * note_types_per_minute();
495 /* time in minutes at tempo in note types per minute */
497 TempoSection::_time_at_tempo (const double& npm) const
499 return log (npm / note_types_per_minute()) / _c;
502 /* pulse at tempo in note types per minute */
504 TempoSection::_pulse_at_tempo (const double& npm) const
506 return ((npm - note_types_per_minute()) / _c) / _note_type;
509 /* tempo in note types per minute at pulse */
511 TempoSection::_tempo_at_pulse (const double& pulse) const
513 return (pulse * _note_type * _c) + note_types_per_minute();
516 /* pulse at time in minutes */
518 TempoSection::_pulse_at_time (const double& time) const
520 return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
523 /* time in minutes at pulse */
525 TempoSection::_time_at_pulse (const double& pulse) const
527 return log1p ((_c * pulse * _note_type) / note_types_per_minute()) / _c;
530 /***********************************************************************/
532 const string MeterSection::xml_state_node_name = "Meter";
534 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
535 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
537 XMLProperty const * prop;
542 framepos_t frame = 0;
543 pair<double, BBT_Time> start;
545 if ((prop = node.property ("start")) != 0) {
546 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
550 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
552 /* legacy session - start used to be in bbt*/
553 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
558 if ((prop = node.property ("pulse")) != 0) {
559 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
560 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
565 if ((prop = node.property ("beat")) != 0) {
566 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
567 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
573 if ((prop = node.property ("bbt")) == 0) {
574 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
575 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
579 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
580 throw failed_constructor();
586 if ((prop = node.property ("frame")) != 0) {
587 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
588 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
589 throw failed_constructor();
591 set_minute (minute_at_frame (frame));
595 /* beats-per-bar is old; divisions-per-bar is new */
597 if ((prop = node.property ("divisions-per-bar")) == 0) {
598 if ((prop = node.property ("beats-per-bar")) == 0) {
599 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
600 throw failed_constructor();
603 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
604 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
605 throw failed_constructor();
608 if ((prop = node.property ("note-type")) == 0) {
609 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
610 throw failed_constructor();
612 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
613 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
614 throw failed_constructor();
617 if ((prop = node.property ("movable")) == 0) {
618 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
619 throw failed_constructor();
622 set_initial (!string_is_affirmative (prop->value()));
624 if ((prop = node.property ("lock-style")) == 0) {
625 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
627 set_position_lock_style (MusicTime);
629 set_position_lock_style (AudioTime);
632 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
637 MeterSection::get_state() const
639 XMLNode *root = new XMLNode (xml_state_node_name);
643 snprintf (buf, sizeof (buf), "%lf", pulse());
644 root->add_property ("pulse", buf);
645 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
649 root->add_property ("bbt", buf);
650 snprintf (buf, sizeof (buf), "%lf", beat());
651 root->add_property ("beat", buf);
652 snprintf (buf, sizeof (buf), "%lf", _note_type);
653 root->add_property ("note-type", buf);
654 snprintf (buf, sizeof (buf), "%li", frame());
655 root->add_property ("frame", buf);
656 root->add_property ("lock-style", enum_2_string (position_lock_style()));
657 snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
658 root->add_property ("divisions-per-bar", buf);
659 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
660 root->add_property ("movable", buf);
665 /***********************************************************************/
669 Tempo determines the rate of musical pulse determined by its components
670 note types per minute - the rate per minute of the whole note divisor _note_type
671 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
672 Meter divides the musical pulse into measures and beats according to its components
676 TempoSection - translates between time, musical pulse and tempo.
677 has a musical location in whole notes (pulses).
678 has a time location in minutes.
679 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
680 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
682 MeterSection - translates between BBT, meter-based beat and musical pulse.
683 has a musical location in whole notes (pulses)
684 has a musical location in meter-based beats
685 has a musical location in BBT time
686 has a time location expressed in minutes.
688 TempoSection and MeterSection may be locked to either audio or music (position lock style).
689 The lock style determines the location type to be kept as a reference when location is recalculated.
691 The first tempo and meter are special. they must move together, and are locked to audio.
692 Audio locked tempi which lie before the first meter are made inactive.
694 Recomputing the map is the process where the 'missing' location types are calculated.
695 We construct the tempo map by first using the locked location type of each section
696 to determine non-locked location types (pulse or minute position).
697 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
699 Having done this, we can now traverse the Metrics list by pulse or minute
700 to query its relevant meter/tempo.
702 It is important to keep the _metrics in an order that makes sense.
703 Because ramped MusicTime and AudioTime tempos can interact with each other,
704 reordering is frequent. Care must be taken to keep _metrics in a solved state.
705 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
709 Music and audio-locked objects may seem interchangeable on the surface, but when translating
710 between audio samples and beat, remember that a sample is only a quantised approximation
711 of the actual time (in minutes) of a beat.
712 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
713 mean that this frame is the actual location in time of 1|3|0.
715 You cannot use a frame measurement to determine beat distance except under special circumstances
716 (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).
718 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
719 sample space the user is operating at to be translated correctly to the object.
721 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
722 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
723 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
725 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
727 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
728 The result is rounded to audio frames.
729 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
732 frame_at_beat (beat_at_frame (frame)) == frame
734 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
736 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
737 frames_between_quarter_notes () eliminats this effect when determining time duration
738 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
740 The above pointless example could instead do:
741 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
743 The Shaggs - Things I Wonder
744 https://www.youtube.com/watch?v=9wQK6zMJOoQ
747 struct MetricSectionSorter {
748 bool operator() (const MetricSection* a, const MetricSection* b) {
749 return a->pulse() < b->pulse();
753 struct MetricSectionFrameSorter {
754 bool operator() (const MetricSection* a, const MetricSection* b) {
755 return a->frame() < b->frame();
759 TempoMap::TempoMap (framecnt_t fr)
762 BBT_Time start (1, 1, 0);
764 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo, TempoSection::Ramp, AudioTime, fr);
765 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
767 t->set_initial (true);
768 t->set_locked_to_meter (true);
770 m->set_initial (true);
772 /* note: frame time is correct (zero) for both of these */
774 _metrics.push_back (t);
775 _metrics.push_back (m);
779 TempoMap::TempoMap (TempoMap const & other)
781 _frame_rate = other._frame_rate;
782 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
783 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
784 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
787 TempoSection* new_section = new TempoSection (*ts);
788 _metrics.push_back (new_section);
790 MeterSection* new_section = new MeterSection (*ms);
791 _metrics.push_back (new_section);
797 TempoMap::operator= (TempoMap const & other)
799 if (&other != this) {
800 _frame_rate = other._frame_rate;
802 Metrics::const_iterator d = _metrics.begin();
803 while (d != _metrics.end()) {
809 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
810 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
811 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
814 TempoSection* new_section = new TempoSection (*ts);
815 _metrics.push_back (new_section);
817 MeterSection* new_section = new MeterSection (*ms);
818 _metrics.push_back (new_section);
823 PropertyChanged (PropertyChange());
828 TempoMap::~TempoMap ()
830 Metrics::const_iterator d = _metrics.begin();
831 while (d != _metrics.end()) {
839 TempoMap::frame_at_minute (const double time) const
841 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
845 TempoMap::minute_at_frame (const framepos_t frame) const
847 return (frame / (double) _frame_rate) / 60.0;
851 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
853 bool removed = false;
856 Glib::Threads::RWLock::WriterLock lm (lock);
857 if ((removed = remove_tempo_locked (tempo))) {
858 if (complete_operation) {
859 recompute_map (_metrics);
864 if (removed && complete_operation) {
865 PropertyChanged (PropertyChange ());
870 TempoMap::remove_tempo_locked (const TempoSection& tempo)
874 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
875 if (dynamic_cast<TempoSection*> (*i) != 0) {
876 if (tempo.frame() == (*i)->frame()) {
877 if (!(*i)->initial()) {
890 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
892 bool removed = false;
895 Glib::Threads::RWLock::WriterLock lm (lock);
896 if ((removed = remove_meter_locked (tempo))) {
897 if (complete_operation) {
898 recompute_map (_metrics);
903 if (removed && complete_operation) {
904 PropertyChanged (PropertyChange ());
909 TempoMap::remove_meter_locked (const MeterSection& meter)
912 if (meter.position_lock_style() == AudioTime) {
913 /* remove meter-locked tempo */
914 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
916 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
917 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
926 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
927 if (dynamic_cast<MeterSection*> (*i) != 0) {
928 if (meter.frame() == (*i)->frame()) {
929 if (!(*i)->initial()) {
942 TempoMap::do_insert (MetricSection* section)
944 bool need_add = true;
945 /* we only allow new meters to be inserted on beat 1 of an existing
949 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
951 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
953 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
954 corrected.second.beats = 1;
955 corrected.second.ticks = 0;
956 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
957 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
958 m->bbt(), corrected.second) << endmsg;
959 //m->set_pulse (corrected);
963 /* Look for any existing MetricSection that is of the same type and
964 in the same bar as the new one, and remove it before adding
965 the new one. Note that this means that if we find a matching,
966 existing section, we can break out of the loop since we're
967 guaranteed that there is only one such match.
970 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
972 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
973 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
974 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
975 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
977 if (tempo && insert_tempo) {
980 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
981 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
983 if (tempo->initial()) {
985 /* can't (re)move this section, so overwrite
986 * its data content (but not its properties as
990 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
991 (*i)->set_position_lock_style (AudioTime);
993 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
994 t->set_type (insert_tempo->type());
1004 } else if (meter && insert_meter) {
1006 /* Meter Sections */
1008 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1010 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
1012 if (meter->initial()) {
1014 /* can't (re)move this section, so overwrite
1015 * its data content (but not its properties as
1019 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
1020 (*i)->set_position_lock_style (AudioTime);
1030 /* non-matching types, so we don't care */
1034 /* Add the given MetricSection, if we didn't just reset an existing
1039 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
1040 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
1041 Metrics::iterator i;
1044 TempoSection* prev_t = 0;
1046 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1047 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
1048 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1051 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
1055 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
1059 prev_t = dynamic_cast<TempoSection*> (*i);
1062 } else if (insert_tempo) {
1063 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1064 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1067 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1068 const bool lm = insert_tempo->locked_to_meter();
1069 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1070 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1077 _metrics.insert (i, section);
1081 /* user supplies the exact pulse if pls == MusicTime */
1083 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
1085 if (tempo.note_types_per_minute() <= 0.0) {
1086 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1090 TempoSection* ts = 0;
1091 TempoSection* prev_tempo = 0;
1093 Glib::Threads::RWLock::WriterLock lm (lock);
1094 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
1095 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1097 if ((*i)->is_tempo()) {
1098 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1100 bool const ipm = ts->position_lock_style() == MusicTime;
1101 bool const lm = ts->locked_to_meter();
1102 if ((ipm && this_t->pulse() == ts->pulse()) || (!ipm && this_t->frame() == ts->frame())
1103 || (lm && this_t->pulse() == ts->pulse())) {
1104 if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1105 prev_tempo->set_end_note_types_per_minute (ts->note_types_per_minute());
1109 prev_tempo = this_t;
1112 recompute_map (_metrics);
1115 PropertyChanged (PropertyChange ());
1121 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
1123 if (tempo.note_types_per_minute() <= 0.0) {
1124 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1128 const bool locked_to_meter = ts.locked_to_meter();
1129 TempoSection* new_ts = 0;
1132 Glib::Threads::RWLock::WriterLock lm (lock);
1133 TempoSection& first (first_tempo());
1134 if (!ts.initial()) {
1135 if (locked_to_meter) {
1138 /* cannot move a meter-locked tempo section */
1139 *static_cast<Tempo*>(&ts) = tempo;
1140 recompute_map (_metrics);
1143 remove_tempo_locked (ts);
1144 new_ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter);
1146 if (new_ts && new_ts->type() == TempoSection::Constant) {
1147 new_ts->set_end_note_types_per_minute (new_ts->note_types_per_minute());
1149 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1151 if ((*i)->is_tempo()) {
1152 TempoSection* const this_t = static_cast<TempoSection*> (*i);
1154 bool const ipm = new_ts->position_lock_style() == MusicTime;
1155 bool const lm = new_ts->locked_to_meter();
1156 if ((ipm && this_t->pulse() > new_ts->pulse()) || (!ipm && this_t->frame() > new_ts->frame())
1157 || (lm && this_t->pulse() > new_ts->pulse())) {
1158 new_ts->set_end_note_types_per_minute (tempo.end_note_types_per_minute());
1168 first.set_type (type);
1169 first.set_pulse (0.0);
1170 first.set_minute (minute_at_frame (frame));
1171 first.set_position_lock_style (AudioTime);
1172 first.set_locked_to_meter (true);
1174 /* cannot move the first tempo section */
1175 *static_cast<Tempo*>(&first) = tempo;
1178 recompute_map (_metrics);
1181 PropertyChanged (PropertyChange ());
1185 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1186 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1188 TempoSection* t = new TempoSection (pulse, minute, tempo, type, pls, _frame_rate);
1189 t->set_locked_to_meter (locked_to_meter);
1194 if (pls == AudioTime) {
1195 solve_map_minute (_metrics, t, t->minute());
1197 solve_map_pulse (_metrics, t, t->pulse());
1199 recompute_meters (_metrics);
1206 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1208 MeterSection* m = 0;
1210 Glib::Threads::RWLock::WriterLock lm (lock);
1211 m = add_meter_locked (meter, beat, where, frame, pls, true);
1216 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1221 PropertyChanged (PropertyChange ());
1226 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1229 Glib::Threads::RWLock::WriterLock lm (lock);
1230 const double beat = beat_at_bbt_locked (_metrics, where);
1232 if (!ms.initial()) {
1233 remove_meter_locked (ms);
1234 add_meter_locked (meter, beat, where, frame, pls, true);
1236 MeterSection& first (first_meter());
1237 TempoSection& first_t (first_tempo());
1238 /* cannot move the first meter section */
1239 *static_cast<Meter*>(&first) = meter;
1240 first.set_position_lock_style (AudioTime);
1241 first.set_pulse (0.0);
1242 first.set_minute (minute_at_frame (frame));
1243 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1244 first.set_beat (beat);
1245 first_t.set_minute (first.minute());
1246 first_t.set_locked_to_meter (true);
1247 first_t.set_pulse (0.0);
1248 first_t.set_position_lock_style (AudioTime);
1249 recompute_map (_metrics);
1253 PropertyChanged (PropertyChange ());
1257 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1259 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1260 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1261 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1262 TempoSection* mlt = 0;
1264 if (pls == AudioTime) {
1265 /* add meter-locked tempo */
1266 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), TempoSection::Ramp, AudioTime, true, true);
1274 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1276 bool solved = false;
1278 do_insert (new_meter);
1282 if (pls == AudioTime) {
1283 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1284 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1285 fudge frame so that the meter ends up at its BBT position instead.
1288 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1291 solved = solve_map_bbt (_metrics, new_meter, where);
1292 /* required due to resetting the pulse of meter-locked tempi above.
1293 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1294 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1296 recompute_map (_metrics);
1300 if (!solved && recompute) {
1301 /* if this has failed to solve, there is little we can do other than to ensure that
1302 the new map is recalculated.
1304 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1305 recompute_map (_metrics);
1312 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1314 Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1317 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1318 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1323 Glib::Threads::RWLock::WriterLock lm (lock);
1324 *((Tempo*) t) = newtempo;
1325 recompute_map (_metrics);
1327 PropertyChanged (PropertyChange ());
1334 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1336 Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1339 TempoSection* first;
1340 Metrics::iterator i;
1342 /* find the TempoSection immediately preceding "where"
1345 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1347 if ((*i)->frame() > where) {
1353 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1366 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1376 Glib::Threads::RWLock::WriterLock lm (lock);
1377 /* cannot move the first tempo section */
1378 *((Tempo*)prev) = newtempo;
1379 recompute_map (_metrics);
1382 PropertyChanged (PropertyChange ());
1386 TempoMap::first_meter () const
1388 const MeterSection *m = 0;
1390 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1391 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1396 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1397 abort(); /*NOTREACHED*/
1402 TempoMap::first_meter ()
1404 MeterSection *m = 0;
1406 /* CALLER MUST HOLD LOCK */
1408 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1409 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1414 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1415 abort(); /*NOTREACHED*/
1420 TempoMap::first_tempo () const
1422 const TempoSection *t = 0;
1424 /* CALLER MUST HOLD LOCK */
1426 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1427 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1437 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1438 abort(); /*NOTREACHED*/
1443 TempoMap::first_tempo ()
1445 TempoSection *t = 0;
1447 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1448 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1458 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1459 abort(); /*NOTREACHED*/
1463 TempoMap::recompute_tempi (Metrics& metrics)
1465 TempoSection* prev_t = 0;
1467 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1470 if ((*i)->is_tempo()) {
1471 t = static_cast<TempoSection*> (*i);
1483 if (t->position_lock_style() == AudioTime) {
1484 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1485 if (!t->locked_to_meter()) {
1486 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1490 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1491 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1499 prev_t->set_c (0.0);
1502 /* tempos must be positioned correctly.
1503 the current approach is to use a meter's bbt time as its base position unit.
1504 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1505 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1508 TempoMap::recompute_meters (Metrics& metrics)
1510 MeterSection* meter = 0;
1511 MeterSection* prev_m = 0;
1513 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1514 if (!(*mi)->is_tempo()) {
1515 meter = static_cast<MeterSection*> (*mi);
1516 if (meter->position_lock_style() == AudioTime) {
1518 pair<double, BBT_Time> b_bbt;
1519 TempoSection* meter_locked_tempo = 0;
1520 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1522 if ((*ii)->is_tempo()) {
1523 t = static_cast<TempoSection*> (*ii);
1524 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1525 meter_locked_tempo = t;
1532 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1533 if (beats + prev_m->beat() != meter->beat()) {
1534 /* reordering caused a bbt change */
1536 beats = meter->beat() - prev_m->beat();
1537 b_bbt = make_pair (beats + prev_m->beat()
1538 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1539 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1541 } else if (!meter->initial()) {
1542 b_bbt = make_pair (meter->beat(), meter->bbt());
1543 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1546 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1548 if (meter_locked_tempo) {
1549 meter_locked_tempo->set_pulse (pulse);
1551 meter->set_beat (b_bbt);
1552 meter->set_pulse (pulse);
1557 pair<double, BBT_Time> b_bbt;
1559 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1560 if (beats + prev_m->beat() != meter->beat()) {
1561 /* reordering caused a bbt change */
1562 b_bbt = make_pair (beats + prev_m->beat()
1563 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1565 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1567 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1569 /* shouldn't happen - the first is audio-locked */
1570 pulse = pulse_at_beat_locked (metrics, meter->beat());
1571 b_bbt = make_pair (meter->beat(), meter->bbt());
1574 meter->set_beat (b_bbt);
1575 meter->set_pulse (pulse);
1576 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1585 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1587 /* CALLER MUST HOLD WRITE LOCK */
1589 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1592 /* silly call from Session::process() during startup
1597 recompute_tempi (metrics);
1598 recompute_meters (metrics);
1602 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1604 Glib::Threads::RWLock::ReaderLock lm (lock);
1605 TempoMetric m (first_meter(), first_tempo());
1607 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1608 at something, because we insert the default tempo and meter during
1609 TempoMap construction.
1611 now see if we can find better candidates.
1614 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1616 if ((*i)->frame() > frame) {
1630 /* XX meters only */
1632 TempoMap::metric_at (BBT_Time bbt) const
1634 Glib::Threads::RWLock::ReaderLock lm (lock);
1635 TempoMetric m (first_meter(), first_tempo());
1637 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1638 at something, because we insert the default tempo and meter during
1639 TempoMap construction.
1641 now see if we can find better candidates.
1644 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1646 if (!(*i)->is_tempo()) {
1647 mw = static_cast<MeterSection*> (*i);
1648 BBT_Time section_start (mw->bbt());
1650 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1661 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1662 * @param frame The session frame position.
1663 * @return The beat duration according to the tempo map at the supplied frame.
1665 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1666 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1668 * This function uses both tempo and meter.
1671 TempoMap::beat_at_frame (const framecnt_t& frame) const
1673 Glib::Threads::RWLock::ReaderLock lm (lock);
1675 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1678 /* This function uses both tempo and meter.*/
1680 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1682 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1683 MeterSection* prev_m = 0;
1684 MeterSection* next_m = 0;
1686 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1687 if (!(*i)->is_tempo()) {
1688 if (prev_m && (*i)->minute() > minute) {
1689 next_m = static_cast<MeterSection*> (*i);
1692 prev_m = static_cast<MeterSection*> (*i);
1696 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1698 /* audio locked meters fake their beat */
1699 if (next_m && next_m->beat() < beat) {
1700 return next_m->beat();
1706 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1707 * @param beat The BBT (meter-based) beat.
1708 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1710 * This function uses both tempo and meter.
1713 TempoMap::frame_at_beat (const double& beat) const
1715 Glib::Threads::RWLock::ReaderLock lm (lock);
1717 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1720 /* meter & tempo section based */
1722 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1724 MeterSection* prev_m = 0;
1725 TempoSection* prev_t = 0;
1729 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1730 if (!(*i)->is_tempo()) {
1731 m = static_cast<MeterSection*> (*i);
1732 if (prev_m && m->beat() > beat) {
1742 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1743 if ((*i)->is_tempo()) {
1744 t = static_cast<TempoSection*> (*i);
1750 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1759 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1762 /** Returns a Tempo corresponding to the supplied frame position.
1763 * @param frame The audio frame.
1764 * @return a Tempo according to the tempo map at the supplied frame.
1768 TempoMap::tempo_at_frame (const framepos_t& frame) const
1770 Glib::Threads::RWLock::ReaderLock lm (lock);
1772 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1776 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1778 TempoSection* prev_t = 0;
1782 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1783 if ((*i)->is_tempo()) {
1784 t = static_cast<TempoSection*> (*i);
1788 if ((prev_t) && t->minute() > minute) {
1789 /* t is the section past frame */
1790 return prev_t->tempo_at_minute (minute);
1796 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1799 /** returns the frame at which the supplied tempo occurs, or
1800 * the frame of the last tempo section (search exhausted)
1801 * only the position of the first occurence will be returned
1805 TempoMap::frame_at_tempo (const Tempo& tempo) const
1807 Glib::Threads::RWLock::ReaderLock lm (lock);
1809 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1813 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1815 TempoSection* prev_t = 0;
1816 const double tempo_bpm = tempo.note_types_per_minute();
1818 Metrics::const_iterator i;
1820 for (i = metrics.begin(); i != metrics.end(); ++i) {
1822 if ((*i)->is_tempo()) {
1823 t = static_cast<TempoSection*> (*i);
1829 const double t_bpm = t->note_types_per_minute();
1831 if (t_bpm == tempo_bpm) {
1836 const double prev_t_bpm = prev_t->note_types_per_minute();
1838 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1839 return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse());
1846 return prev_t->minute();
1850 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1852 TempoSection* prev_t = 0;
1856 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1857 if ((*i)->is_tempo()) {
1858 t = static_cast<TempoSection*> (*i);
1862 if ((prev_t) && t->pulse() > pulse) {
1863 /* t is the section past frame */
1864 return prev_t->tempo_at_pulse (pulse);
1870 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1874 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1876 TempoSection* prev_t = 0;
1877 const double tempo_bpm = tempo.note_types_per_minute();
1879 Metrics::const_iterator i;
1881 for (i = metrics.begin(); i != metrics.end(); ++i) {
1883 if ((*i)->is_tempo()) {
1884 t = static_cast<TempoSection*> (*i);
1890 const double t_bpm = t->note_types_per_minute();
1892 if (t_bpm == tempo_bpm) {
1897 const double prev_t_bpm = prev_t->note_types_per_minute();
1899 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1900 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1907 return prev_t->pulse();
1910 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1911 * @param qn the position in quarter note beats.
1912 * @return the Tempo at the supplied quarter-note.
1915 TempoMap::tempo_at_quarter_note (const double& qn) const
1917 Glib::Threads::RWLock::ReaderLock lm (lock);
1919 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1922 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1923 * @param tempo the tempo.
1924 * @return the position in quarter-note beats where the map bpm
1925 * is equal to that of the Tempo. currently ignores note_type.
1928 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1930 Glib::Threads::RWLock::ReaderLock lm (lock);
1932 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1935 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1936 * @param metrics the list of metric sections used to calculate the pulse.
1937 * @param beat The BBT (meter-based) beat.
1938 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1940 * a pulse or whole note is the base musical position of a MetricSection.
1941 * it is equivalent to four quarter notes.
1945 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1947 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1949 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1952 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1953 * @param metrics the list of metric sections used to calculate the beat.
1954 * @param pulse the whole-note pulse.
1955 * @return the meter-based beat at the supplied whole-note pulse.
1957 * a pulse or whole note is the base musical position of a MetricSection.
1958 * it is equivalent to four quarter notes.
1961 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1963 MeterSection* prev_m = 0;
1965 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1967 if (!(*i)->is_tempo()) {
1968 m = static_cast<MeterSection*> (*i);
1969 if (prev_m && m->pulse() > pulse) {
1977 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1981 /* tempo section based */
1983 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1985 /* HOLD (at least) THE READER LOCK */
1986 TempoSection* prev_t = 0;
1988 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1990 if ((*i)->is_tempo()) {
1991 t = static_cast<TempoSection*> (*i);
1995 if (prev_t && t->minute() > minute) {
1996 /*the previous ts is the one containing the frame */
1997 const double ret = prev_t->pulse_at_minute (minute);
1998 /* audio locked section in new meter*/
1999 if (t->pulse() < ret) {
2008 /* treated as constant for this ts */
2009 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
2011 return pulses_in_section + prev_t->pulse();
2014 /* tempo section based */
2016 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2018 /* HOLD THE READER LOCK */
2020 const TempoSection* prev_t = 0;
2022 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2025 if ((*i)->is_tempo()) {
2026 t = static_cast<TempoSection*> (*i);
2030 if (prev_t && t->pulse() > pulse) {
2031 return prev_t->minute_at_pulse (pulse);
2037 /* must be treated as constant, irrespective of _type */
2038 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
2040 return dtime + prev_t->minute();
2043 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
2044 * @param bbt The BBT time (meter-based).
2045 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
2049 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
2051 Glib::Threads::RWLock::ReaderLock lm (lock);
2052 return beat_at_bbt_locked (_metrics, bbt);
2057 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2059 /* CALLER HOLDS READ LOCK */
2061 MeterSection* prev_m = 0;
2063 /* because audio-locked meters have 'fake' integral beats,
2064 there is no pulse offset here.
2068 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2069 if (!(*i)->is_tempo()) {
2070 m = static_cast<MeterSection*> (*i);
2072 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2073 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2081 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2082 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2083 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2088 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2089 * @param beat The BBT (meter-based) beat.
2090 * @return The BBT time (meter-based) at the supplied meter-based beat.
2094 TempoMap::bbt_at_beat (const double& beat)
2096 Glib::Threads::RWLock::ReaderLock lm (lock);
2097 return bbt_at_beat_locked (_metrics, beat);
2101 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2103 /* CALLER HOLDS READ LOCK */
2104 MeterSection* prev_m = 0;
2105 const double beats = max (0.0, b);
2107 MeterSection* m = 0;
2109 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2110 if (!(*i)->is_tempo()) {
2111 m = static_cast<MeterSection*> (*i);
2113 if (m->beat() > beats) {
2114 /* this is the meter after the one our beat is on*/
2124 const double beats_in_ms = beats - prev_m->beat();
2125 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2126 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2127 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2128 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2132 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2133 ret.beats = (uint32_t) floor (remaining_beats);
2134 ret.bars = total_bars;
2136 /* 0 0 0 to 1 1 0 - based mapping*/
2140 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2142 ret.ticks -= BBT_Time::ticks_per_beat;
2145 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2153 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2154 * @param bbt The BBT time (meter-based).
2155 * @return the quarter note beat at the supplied BBT time
2157 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2159 * while the input uses meter, the output does not.
2162 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2164 Glib::Threads::RWLock::ReaderLock lm (lock);
2166 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2170 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2172 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2175 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2178 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2182 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2184 /* CALLER HOLDS READ LOCK */
2186 MeterSection* prev_m = 0;
2188 /* because audio-locked meters have 'fake' integral beats,
2189 there is no pulse offset here.
2193 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2194 if (!(*i)->is_tempo()) {
2195 m = static_cast<MeterSection*> (*i);
2197 if (m->bbt().bars > bbt.bars) {
2205 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2206 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2207 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2212 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2213 * @param qn the quarter-note beat.
2214 * @return The BBT time (meter-based) at the supplied meter-based beat.
2216 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2220 TempoMap::bbt_at_quarter_note (const double& qn)
2222 Glib::Threads::RWLock::ReaderLock lm (lock);
2224 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2227 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2228 * @param metrics The list of metric sections used to determine the result.
2229 * @param pulse The whole-note pulse.
2230 * @return The BBT time at the supplied whole-note pulse.
2232 * a pulse or whole note is the basic musical position of a MetricSection.
2233 * it is equivalent to four quarter notes.
2234 * while the output uses meter, the input does not.
2237 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2239 MeterSection* prev_m = 0;
2241 MeterSection* m = 0;
2243 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2245 if (!(*i)->is_tempo()) {
2246 m = static_cast<MeterSection*> (*i);
2249 double const pulses_to_m = m->pulse() - prev_m->pulse();
2250 if (prev_m->pulse() + pulses_to_m > pulse) {
2251 /* this is the meter after the one our beat is on*/
2262 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2263 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2264 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2265 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2266 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2270 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2271 ret.beats = (uint32_t) floor (remaining_beats);
2272 ret.bars = total_bars;
2274 /* 0 0 0 to 1 1 0 mapping*/
2278 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2280 ret.ticks -= BBT_Time::ticks_per_beat;
2283 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2291 /** Returns the BBT time corresponding to the supplied frame position.
2292 * @param frame the position in audio samples.
2293 * @return the BBT time at the frame position .
2297 TempoMap::bbt_at_frame (framepos_t frame)
2305 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2310 const double minute = minute_at_frame (frame);
2312 Glib::Threads::RWLock::ReaderLock lm (lock);
2314 return bbt_at_minute_locked (_metrics, minute);
2318 TempoMap::bbt_at_frame_rt (framepos_t frame)
2320 const double minute = minute_at_frame (frame);
2322 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2325 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2328 return bbt_at_minute_locked (_metrics, minute);
2332 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2342 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2343 MeterSection* prev_m = 0;
2344 MeterSection* next_m = 0;
2348 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2349 if (!(*i)->is_tempo()) {
2350 m = static_cast<MeterSection*> (*i);
2351 if (prev_m && m->minute() > minute) {
2359 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2361 /* handle frame before first meter */
2362 if (minute < prev_m->minute()) {
2365 /* audio locked meters fake their beat */
2366 if (next_m && next_m->beat() < beat) {
2367 beat = next_m->beat();
2370 beat = max (0.0, beat);
2372 const double beats_in_ms = beat - prev_m->beat();
2373 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2374 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2375 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2376 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2380 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2381 ret.beats = (uint32_t) floor (remaining_beats);
2382 ret.bars = total_bars;
2384 /* 0 0 0 to 1 1 0 - based mapping*/
2388 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2390 ret.ticks -= BBT_Time::ticks_per_beat;
2393 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2401 /** Returns the frame position corresponding to the supplied BBT time.
2402 * @param bbt the position in BBT time.
2403 * @return the frame position at bbt.
2407 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2411 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2416 if (bbt.beats < 1) {
2417 throw std::logic_error ("beats are counted from one");
2422 Glib::Threads::RWLock::ReaderLock lm (lock);
2423 minute = minute_at_bbt_locked (_metrics, bbt);
2426 return frame_at_minute (minute);
2429 /* meter & tempo section based */
2431 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2433 /* HOLD THE READER LOCK */
2435 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2440 * Returns the quarter-note beat position corresponding to the supplied frame.
2442 * @param frame the position in frames.
2443 * @return The quarter-note position of the supplied frame. Ignores meter.
2447 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2449 const double minute = minute_at_frame (frame);
2451 Glib::Threads::RWLock::ReaderLock lm (lock);
2453 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2457 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2459 const double minute = minute_at_frame (frame);
2461 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2464 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2467 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2471 * Returns the frame position corresponding to the supplied quarter-note beat.
2473 * @param quarter_note the quarter-note position.
2474 * @return the frame position of the supplied quarter-note. Ignores meter.
2479 TempoMap::frame_at_quarter_note (const double quarter_note) const
2483 Glib::Threads::RWLock::ReaderLock lm (lock);
2485 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2488 return frame_at_minute (minute);
2491 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2492 * @param beat The BBT (meter-based) beat.
2493 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2495 * a quarter-note may be compared with and assigned to Evoral::Beats.
2499 TempoMap::quarter_note_at_beat (const double beat) const
2501 Glib::Threads::RWLock::ReaderLock lm (lock);
2503 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2506 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2507 * @param quarter_note The position in quarter-note beats.
2508 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2510 * a quarter-note is the musical unit of Evoral::Beats.
2514 TempoMap::beat_at_quarter_note (const double quarter_note) const
2516 Glib::Threads::RWLock::ReaderLock lm (lock);
2518 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2521 /** Returns the duration in frames between two supplied quarter-note beat positions.
2522 * @param start the first position in quarter-note beats.
2523 * @param end the end position in quarter-note beats.
2524 * @return the frame distance ober the quarter-note beats duration.
2526 * use this rather than e.g.
2527 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2528 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2532 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2537 Glib::Threads::RWLock::ReaderLock lm (lock);
2538 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2541 return frame_at_minute (minutes);
2545 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2548 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2552 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2554 Glib::Threads::RWLock::ReaderLock lm (lock);
2556 return quarter_notes_between_frames_locked (_metrics, start, end);
2560 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2562 const TempoSection* prev_t = 0;
2564 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2567 if ((*i)->is_tempo()) {
2568 t = static_cast<TempoSection*> (*i);
2572 if (prev_t && t->frame() > start) {
2579 const double start_qn = prev_t->pulse_at_frame (start);
2581 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2584 if ((*i)->is_tempo()) {
2585 t = static_cast<TempoSection*> (*i);
2589 if (prev_t && t->frame() > end) {
2595 const double end_qn = prev_t->pulse_at_frame (end);
2597 return (end_qn - start_qn) * 4.0;
2601 TempoMap::check_solved (const Metrics& metrics) const
2603 TempoSection* prev_t = 0;
2604 MeterSection* prev_m = 0;
2606 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2609 if ((*i)->is_tempo()) {
2610 t = static_cast<TempoSection*> (*i);
2615 /* check ordering */
2616 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2620 /* precision check ensures tempo and frames align.*/
2621 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2622 if (!t->locked_to_meter()) {
2627 /* gradient limit - who knows what it should be?
2628 things are also ok (if a little chaotic) without this
2630 if (fabs (prev_t->c()) > 1000.0) {
2631 //std::cout << "c : " << prev_t->c() << std::endl;
2638 if (!(*i)->is_tempo()) {
2639 m = static_cast<MeterSection*> (*i);
2640 if (prev_m && m->position_lock_style() == AudioTime) {
2641 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2642 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2643 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2645 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2659 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2661 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2663 if ((*i)->is_tempo()) {
2664 t = static_cast<TempoSection*> (*i);
2665 if (t->locked_to_meter()) {
2666 t->set_active (true);
2667 } else if (t->position_lock_style() == AudioTime) {
2668 if (t->frame() < frame) {
2669 t->set_active (false);
2670 t->set_pulse (-1.0);
2671 } else if (t->frame() > frame) {
2672 t->set_active (true);
2673 } else if (t->frame() == frame) {
2683 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2685 TempoSection* prev_t = 0;
2686 TempoSection* section_prev = 0;
2687 double first_m_minute = 0.0;
2688 const bool sml = section->locked_to_meter();
2690 /* can't move a tempo before the first meter */
2691 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2693 if (!(*i)->is_tempo()) {
2694 m = static_cast<MeterSection*> (*i);
2696 first_m_minute = m->minute();
2701 if (!section->initial() && minute <= first_m_minute) {
2705 section->set_active (true);
2706 section->set_minute (minute);
2708 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2710 if ((*i)->is_tempo()) {
2711 t = static_cast<TempoSection*> (*i);
2723 if (t->frame() == frame_at_minute (minute)) {
2727 const bool tlm = t->position_lock_style() == MusicTime;
2729 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2730 section_prev = prev_t;
2732 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2733 if (!section->locked_to_meter()) {
2734 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2739 if (t->position_lock_style() == MusicTime) {
2740 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2741 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2743 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2744 if (!t->locked_to_meter()) {
2745 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2754 recompute_tempi (imaginary);
2756 if (check_solved (imaginary)) {
2759 dunp (imaginary, std::cout);
2763 MetricSectionFrameSorter fcmp;
2764 imaginary.sort (fcmp);
2766 recompute_tempi (imaginary);
2768 if (check_solved (imaginary)) {
2776 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2778 TempoSection* prev_t = 0;
2779 TempoSection* section_prev = 0;
2781 section->set_pulse (pulse);
2783 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2785 if ((*i)->is_tempo()) {
2786 t = static_cast<TempoSection*> (*i);
2797 section_prev = prev_t;
2801 if (t->position_lock_style() == MusicTime) {
2802 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2803 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2805 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2806 if (!t->locked_to_meter()) {
2807 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2816 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2817 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2821 recompute_tempi (imaginary);
2823 if (check_solved (imaginary)) {
2826 dunp (imaginary, std::cout);
2830 MetricSectionSorter cmp;
2831 imaginary.sort (cmp);
2833 recompute_tempi (imaginary);
2835 * XX need a restriction here, but only for this case,
2836 * as audio locked tempos don't interact in the same way.
2838 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2840 * |50 bpm |250 bpm |60 bpm
2841 * drag 250 to the pulse after 60->
2842 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2844 if (check_solved (imaginary)) {
2852 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2854 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2855 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2856 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2860 if (section->initial()) {
2861 /* lock the first tempo to our first meter */
2862 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2867 TempoSection* meter_locked_tempo = 0;
2869 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2871 if ((*ii)->is_tempo()) {
2872 t = static_cast<TempoSection*> (*ii);
2873 if (t->locked_to_meter() && t->frame() == section->frame()) {
2874 meter_locked_tempo = t;
2880 if (!meter_locked_tempo) {
2884 MeterSection* prev_m = 0;
2886 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2887 bool solved = false;
2889 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2891 if (!(*i)->is_tempo()) {
2892 m = static_cast<MeterSection*> (*i);
2894 if (prev_m && !section->initial()) {
2895 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2896 if (beats + prev_m->beat() < section->beat()) {
2897 /* set the section pulse according to its musical position,
2898 * as an earlier time than this has been requested.
2900 const double new_pulse = ((section->beat() - prev_m->beat())
2901 / prev_m->note_divisor()) + prev_m->pulse();
2903 tempo_copy->set_position_lock_style (MusicTime);
2904 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2905 meter_locked_tempo->set_position_lock_style (MusicTime);
2906 section->set_position_lock_style (MusicTime);
2907 section->set_pulse (new_pulse);
2908 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2909 meter_locked_tempo->set_position_lock_style (AudioTime);
2910 section->set_position_lock_style (AudioTime);
2911 section->set_minute (meter_locked_tempo->minute());
2917 Metrics::const_iterator d = future_map.begin();
2918 while (d != future_map.end()) {
2927 /* all is ok. set section's locked tempo if allowed.
2928 possibly disallow if there is an adjacent audio-locked tempo.
2929 XX this check could possibly go. its never actually happened here.
2931 MeterSection* meter_copy = const_cast<MeterSection*>
2932 (&meter_section_at_minute_locked (future_map, section->minute()));
2934 meter_copy->set_minute (minute);
2936 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2937 section->set_minute (minute);
2938 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2939 / prev_m->note_divisor()) + prev_m->pulse());
2940 solve_map_minute (imaginary, meter_locked_tempo, minute);
2945 Metrics::const_iterator d = future_map.begin();
2946 while (d != future_map.end()) {
2956 /* initial (first meter atm) */
2958 tempo_copy->set_minute (minute);
2959 tempo_copy->set_pulse (0.0);
2961 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2962 section->set_minute (minute);
2963 meter_locked_tempo->set_minute (minute);
2964 meter_locked_tempo->set_pulse (0.0);
2965 solve_map_minute (imaginary, meter_locked_tempo, minute);
2970 Metrics::const_iterator d = future_map.begin();
2971 while (d != future_map.end()) {
2980 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2981 section->set_beat (b_bbt);
2982 section->set_pulse (0.0);
2992 MetricSectionFrameSorter fcmp;
2993 imaginary.sort (fcmp);
2995 recompute_meters (imaginary);
3001 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
3003 /* disallow setting section to an existing meter's bbt */
3004 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
3006 if (!(*i)->is_tempo()) {
3007 m = static_cast<MeterSection*> (*i);
3008 if (m != section && m->bbt().bars == when.bars) {
3014 MeterSection* prev_m = 0;
3015 MeterSection* section_prev = 0;
3017 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
3019 if (!(*i)->is_tempo()) {
3020 m = static_cast<MeterSection*> (*i);
3026 pair<double, BBT_Time> b_bbt;
3027 double new_pulse = 0.0;
3029 if (prev_m && m->bbt().bars > when.bars && !section_prev){
3030 section_prev = prev_m;
3032 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
3033 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
3034 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
3036 section->set_beat (b_bbt);
3037 section->set_pulse (pulse);
3038 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3042 if (m->position_lock_style() == AudioTime) {
3043 TempoSection* meter_locked_tempo = 0;
3045 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
3047 if ((*ii)->is_tempo()) {
3048 t = static_cast<TempoSection*> (*ii);
3049 if (t->locked_to_meter() && t->frame() == m->frame()) {
3050 meter_locked_tempo = t;
3056 if (!meter_locked_tempo) {
3061 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3063 if (beats + prev_m->beat() != m->beat()) {
3064 /* tempo/ meter change caused a change in beat (bar). */
3066 /* the user has requested that the previous section of music overlaps this one.
3067 we have no choice but to change the bar number here, as being locked to audio means
3068 we must stay where we are on the timeline.
3070 beats = m->beat() - prev_m->beat();
3071 b_bbt = make_pair (beats + prev_m->beat()
3072 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3073 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3075 } else if (!m->initial()) {
3076 b_bbt = make_pair (m->beat(), m->bbt());
3077 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3080 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3083 meter_locked_tempo->set_pulse (new_pulse);
3084 m->set_beat (b_bbt);
3085 m->set_pulse (new_pulse);
3089 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3090 if (beats + prev_m->beat() != m->beat()) {
3091 /* tempo/ meter change caused a change in beat (bar). */
3092 b_bbt = make_pair (beats + prev_m->beat()
3093 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3095 b_bbt = make_pair (beats + prev_m->beat()
3098 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3099 m->set_beat (b_bbt);
3100 m->set_pulse (new_pulse);
3101 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3108 if (!section_prev) {
3110 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3111 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3112 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3114 section->set_beat (b_bbt);
3115 section->set_pulse (pulse);
3116 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3119 MetricSectionSorter cmp;
3120 imaginary.sort (cmp);
3122 recompute_meters (imaginary);
3127 /** places a copy of _metrics into copy and returns a pointer
3128 * to section's equivalent in copy.
3131 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
3133 TempoSection* ret = 0;
3135 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3138 if ((*i)->is_tempo()) {
3139 t = static_cast<TempoSection*> (*i);
3141 ret = new TempoSection (*t);
3142 copy.push_back (ret);
3146 TempoSection* cp = new TempoSection (*t);
3147 copy.push_back (cp);
3149 if (!(*i)->is_tempo()) {
3150 m = static_cast<MeterSection *> (*i);
3151 MeterSection* cp = new MeterSection (*m);
3152 copy.push_back (cp);
3160 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3162 MeterSection* ret = 0;
3164 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3167 if ((*i)->is_tempo()) {
3168 t = static_cast<TempoSection*> (*i);
3169 TempoSection* cp = new TempoSection (*t);
3170 copy.push_back (cp);
3173 if (!(*i)->is_tempo()) {
3174 m = static_cast<MeterSection *> (*i);
3176 ret = new MeterSection (*m);
3177 copy.push_back (ret);
3180 MeterSection* cp = new MeterSection (*m);
3181 copy.push_back (cp);
3188 /** answers the question "is this a valid beat position for this tempo section?".
3189 * it returns true if the tempo section can be moved to the requested bbt position,
3190 * leaving the tempo map in a solved state.
3191 * @param ts the tempo section to be moved
3192 * @param bbt the requested new position for the tempo section
3193 * @return true if the tempo section can be moved to the position, otherwise false.
3196 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3199 TempoSection* tempo_copy = 0;
3202 Glib::Threads::RWLock::ReaderLock lm (lock);
3203 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3209 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3211 Metrics::const_iterator d = copy.begin();
3212 while (d != copy.end()) {
3221 * 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,
3222 * taking any possible reordering as a consequence of this into account.
3223 * @param section - the section to be altered
3224 * @param bbt - the BBT time where the altered tempo will fall
3225 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3227 pair<double, framepos_t>
3228 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3231 pair<double, framepos_t> ret = make_pair (0.0, 0);
3233 Glib::Threads::RWLock::ReaderLock lm (lock);
3235 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3237 const double beat = beat_at_bbt_locked (future_map, bbt);
3239 if (section->position_lock_style() == AudioTime) {
3240 tempo_copy->set_position_lock_style (MusicTime);
3243 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3244 ret.first = tempo_copy->pulse();
3245 ret.second = tempo_copy->frame();
3247 ret.first = section->pulse();
3248 ret.second = section->frame();
3251 Metrics::const_iterator d = future_map.begin();
3252 while (d != future_map.end()) {
3259 /** moves a TempoSection to a specified position.
3260 * @param ts - the section to be moved
3261 * @param frame - the new position in frames for the tempo
3262 * @param sub_num - the snap division to use if using musical time.
3264 * if sub_num is non-zero, the frame position is used to calculate an exact
3267 * -1 | snap to bars (meter-based)
3268 * 0 | no snap - use audio frame for musical position
3269 * 1 | snap to meter-based (BBT) beat
3270 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3272 * this follows the snap convention in the gui.
3273 * if sub_num is zero, the musical position will be taken from the supplied frame.
3276 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3280 if (ts->position_lock_style() == MusicTime) {
3282 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3283 Glib::Threads::RWLock::WriterLock lm (lock);
3284 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3286 tempo_copy->set_position_lock_style (AudioTime);
3288 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3289 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3290 const double pulse = pulse_at_beat_locked (future_map, beat);
3292 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3293 solve_map_pulse (_metrics, ts, pulse);
3294 recompute_meters (_metrics);
3302 Glib::Threads::RWLock::WriterLock lm (lock);
3303 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3305 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3307 /* We're moving the object that defines the grid while snapping to it...
3308 * Placing the ts at the beat corresponding to the requested frame may shift the
3309 * grid in such a way that the mouse is left hovering over a completerly different division,
3310 * causing jittering when the mouse next moves (esp. large tempo deltas).
3312 * This alters the snap behaviour slightly in that we snap to beat divisions
3313 * in the future map rather than the existing one.
3315 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3316 const framepos_t snapped_frame = frame_at_minute (minute_at_pulse_locked (future_map, qn / 4.0));
3318 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3319 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3320 ts->set_pulse (qn / 4.0);
3321 recompute_meters (_metrics);
3324 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3325 recompute_meters (_metrics);
3331 Metrics::const_iterator d = future_map.begin();
3332 while (d != future_map.end()) {
3337 MetricPositionChanged (PropertyChange ()); // Emit Signal
3340 /** moves a MeterSection to a specified position.
3341 * @param ms - the section to be moved
3342 * @param frame - the new position in frames for the meter
3344 * as a meter cannot snap to anything but bars,
3345 * the supplied frame is rounded to the nearest bar, possibly
3346 * leaving the meter position unchanged.
3349 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3353 if (ms->position_lock_style() == AudioTime) {
3356 Glib::Threads::RWLock::WriterLock lm (lock);
3357 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3359 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3360 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3361 recompute_tempi (_metrics);
3366 Glib::Threads::RWLock::WriterLock lm (lock);
3367 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3369 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3370 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3372 if (solve_map_bbt (future_map, copy, bbt)) {
3373 solve_map_bbt (_metrics, ms, bbt);
3374 recompute_tempi (_metrics);
3379 Metrics::const_iterator d = future_map.begin();
3380 while (d != future_map.end()) {
3385 MetricPositionChanged (PropertyChange ()); // Emit Signal
3389 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm, bool change_end)
3392 bool can_solve = false;
3394 Glib::Threads::RWLock::WriterLock lm (lock);
3395 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3397 if (change_end && tempo_copy->type() == TempoSection::Constant) {
3398 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3399 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3400 } else if (change_end) {
3401 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3403 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3406 recompute_tempi (future_map);
3408 if (check_solved (future_map)) {
3409 if (change_end && ts->type() == TempoSection::Constant) {
3410 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3411 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3412 } else if (change_end) {
3413 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3415 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3418 recompute_map (_metrics);
3423 Metrics::const_iterator d = future_map.begin();
3424 while (d != future_map.end()) {
3429 MetricPositionChanged (PropertyChange ()); // Emit Signal
3436 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3439 Ts (future prev_t) Tnext
3442 |----------|----------
3449 Glib::Threads::RWLock::WriterLock lm (lock);
3455 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3461 /* minimum allowed measurement distance in frames */
3462 framepos_t const min_dframe = 2;
3466 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3468 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
3469 / (double) (end_frame - prev_t->frame()));
3471 new_bpm = prev_t->note_types_per_minute();
3474 std::cout << "new bpm : " << new_bpm << std::endl;
3475 new_bpm = min (new_bpm, (double) 1000.0);
3477 /* don't clamp and proceed here.
3478 testing has revealed that this can go negative,
3479 which is an entirely different thing to just being too low.
3482 if (new_bpm < 0.5) {
3486 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3487 prev_t->set_note_types_per_minute (new_bpm);
3489 prev_t->set_end_note_types_per_minute (new_bpm);
3490 prev_t->set_note_types_per_minute (new_bpm);
3493 recompute_tempi (future_map);
3494 recompute_meters (future_map);
3496 if (check_solved (future_map)) {
3497 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3498 ts->set_note_types_per_minute (new_bpm);
3500 ts->set_end_note_types_per_minute (new_bpm);
3501 ts->set_note_types_per_minute (new_bpm);
3503 recompute_tempi (_metrics);
3504 recompute_meters (_metrics);
3508 MetricPositionChanged (PropertyChange ()); // Emit Signal
3511 Metrics::const_iterator d = future_map.begin();
3512 while (d != future_map.end()) {
3519 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3522 Ts (future prev_t) Tnext
3525 |----------|----------
3532 Glib::Threads::RWLock::WriterLock lm (lock);
3538 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3541 TempoSection* next_t = 0;
3542 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3543 if ((*i)->is_tempo() && (*i)->minute() > prev_t->minute()) {
3544 next_t = static_cast<TempoSection*> (*i);
3558 /* minimum allowed measurement distance in frames */
3559 framepos_t const min_dframe = 2;
3562 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3563 new_bpm = prev_t->end_note_types_per_minute() * ((frame - prev_t->frame())
3564 / (double) (end_frame - prev_t->frame()));
3566 new_bpm = prev_t->end_note_types_per_minute();
3569 new_bpm = min (new_bpm, (double) 1000.0);
3571 if (new_bpm < 0.5) {
3575 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3576 prev_t->set_end_note_types_per_minute (new_bpm);
3579 prev_t->set_note_types_per_minute (new_bpm);
3580 prev_t->set_end_note_types_per_minute (prev_t->note_types_per_minute());
3584 recompute_tempi (future_map);
3585 recompute_meters (future_map);
3587 if (check_solved (future_map)) {
3588 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3589 ts->set_end_note_types_per_minute (new_bpm);
3592 ts->set_end_note_types_per_minute (prev_t->note_types_per_minute());
3593 ts->set_note_types_per_minute (prev_t->note_types_per_minute());
3596 recompute_tempi (_metrics);
3597 recompute_meters (_metrics);
3601 MetricPositionChanged (PropertyChange ()); // Emit Signal
3604 Metrics::const_iterator d = future_map.begin();
3605 while (d != future_map.end()) {
3612 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
3614 TempoSection* next_t = 0;
3615 TempoSection* next_to_next_t = 0;
3617 bool can_solve = false;
3619 /* minimum allowed measurement distance in frames */
3620 framepos_t const min_dframe = 2;
3623 Glib::Threads::RWLock::WriterLock lm (lock);
3628 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3629 TempoSection* prev_to_prev_t = 0;
3630 const frameoffset_t fr_off = end_frame - frame;
3636 if (tempo_copy->pulse() > 0.0) {
3637 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
3640 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3641 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3642 next_t = static_cast<TempoSection*> (*i);
3651 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3652 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3653 next_to_next_t = static_cast<TempoSection*> (*i);
3658 if (!next_to_next_t) {
3659 std::cout << "no next to next t" << std::endl;
3663 double prev_contribution = 0.0;
3665 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3666 prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3669 const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
3672 framepos_t old_tc_pos = tempo_copy->frame();
3673 framepos_t old_next_pos = next_t->frame();
3674 double old_next_minute = next_t->minute();
3675 framepos_t old_next_to_next_pos = next_to_next_t->frame();
3676 double old_next_to_next_minute = next_to_next_t->minute();
3679 double new_next_bpm;
3680 double new_copy_end_bpm;
3682 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3683 new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
3684 / (double) (end_frame - tempo_copy->frame()));
3686 new_bpm = tempo_copy->note_types_per_minute();
3689 /* don't clamp and proceed here.
3690 testing has revealed that this can go negative,
3691 which is an entirely different thing to just being too low.
3693 if (new_bpm < 0.5) {
3697 new_bpm = min (new_bpm, (double) 1000.0);
3699 tempo_copy->set_note_types_per_minute (new_bpm);
3700 if (tempo_copy->type() == TempoSection::Constant) {
3701 tempo_copy->set_end_note_types_per_minute (new_bpm);
3703 recompute_tempi (future_map);
3705 if (check_solved (future_map)) {
3710 ts->set_note_types_per_minute (new_bpm);
3711 if (ts->type() == TempoSection::Constant) {
3712 ts->set_end_note_types_per_minute (new_bpm);
3714 recompute_map (_metrics);
3718 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3719 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3721 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3722 / (double) ((old_next_to_next_minute) - old_next_minute));
3725 new_next_bpm = next_t->note_types_per_minute();
3728 next_t->set_note_types_per_minute (new_next_bpm);
3729 recompute_tempi (future_map);
3731 if (check_solved (future_map)) {
3732 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3733 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3734 next_t = static_cast<TempoSection*> (*i);
3742 next_t->set_note_types_per_minute (new_next_bpm);
3743 recompute_map (_metrics);
3747 double next_frame_ratio = 1.0;
3748 double copy_frame_ratio = 1.0;
3750 if (next_to_next_t) {
3751 next_frame_ratio = (next_to_next_t->frame() - old_next_pos) / (double) (old_next_to_next_pos - old_next_pos);
3753 copy_frame_ratio = ((old_tc_pos - next_t->frame()) / (double) (old_tc_pos - old_next_pos));
3756 //next_frame_ratio = (((next_to_next_pos - fr_off) - next_t->frame()) / (double) ((next_to_next_pos) - next_t->frame()));
3757 //next_pulse_ratio = (start_pulse / end_pulse);
3760 new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
3761 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
3763 next_t->set_note_types_per_minute (new_next_bpm);
3764 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3765 recompute_tempi (future_map);
3767 if (check_solved (future_map)) {
3768 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3769 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3770 next_t = static_cast<TempoSection*> (*i);
3778 next_t->set_note_types_per_minute (new_next_bpm);
3779 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3780 recompute_map (_metrics);
3786 Metrics::const_iterator d = future_map.begin();
3787 while (d != future_map.end()) {
3792 MetricPositionChanged (PropertyChange ()); // Emit Signal
3797 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3798 * the supplied frame, possibly returning a negative value.
3800 * @param frame The session frame position.
3801 * @param sub_num The subdivision to use when rounding the beat.
3802 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3803 * Positive integers indicate quarter note (non BBT) divisions.
3804 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3805 * @return The beat position of the supplied frame.
3807 * when working to a musical grid, the use of sub_nom indicates that
3808 * the position should be interpreted musically.
3810 * it effectively snaps to meter bars, meter beats or quarter note divisions
3811 * (as per current gui convention) and returns a musical position independent of frame rate.
3813 * If the supplied frame lies before the first meter, the return will be negative,
3814 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3815 * the continuation of the tempo curve (backwards).
3817 * This function is sensitive to tempo and meter.
3820 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3822 Glib::Threads::RWLock::ReaderLock lm (lock);
3824 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3828 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3830 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3833 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3834 * the supplied frame, possibly returning a negative value.
3836 * @param frame The session frame position.
3837 * @param sub_num The subdivision to use when rounding the quarter note.
3838 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3839 * Positive integers indicate quarter note (non BBT) divisions.
3840 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3841 * @return The quarter note position of the supplied frame.
3843 * When working to a musical grid, the use of sub_nom indicates that
3844 * the frame position should be interpreted musically.
3846 * it effectively snaps to meter bars, meter beats or quarter note divisions
3847 * (as per current gui convention) and returns a musical position independent of frame rate.
3849 * If the supplied frame lies before the first meter, the return will be negative,
3850 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3851 * the continuation of the tempo curve (backwards).
3853 * This function is tempo-sensitive.
3856 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3858 Glib::Threads::RWLock::ReaderLock lm (lock);
3860 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3864 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3866 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3869 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3870 } else if (sub_num == 1) {
3871 /* the gui requested exact musical (BBT) beat */
3872 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3873 } else if (sub_num == -1) {
3875 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3879 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3881 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3883 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3893 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3894 * @param pos the frame position in the tempo map.
3895 * @param bbt the distance in BBT time from pos to calculate.
3896 * @param dir the rounding direction..
3897 * @return the duration in frames between pos and bbt
3900 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3902 Glib::Threads::RWLock::ReaderLock lm (lock);
3904 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3906 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3909 pos_bbt.bars += bbt.bars;
3911 pos_bbt.ticks += bbt.ticks;
3912 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3914 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3917 pos_bbt.beats += bbt.beats;
3918 if ((double) pos_bbt.beats > divisions) {
3920 pos_bbt.beats -= divisions;
3922 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3924 return pos_bbt_frame - pos;
3928 if (pos_bbt.bars <= bbt.bars) {
3931 pos_bbt.bars -= bbt.bars;
3934 if (pos_bbt.ticks < bbt.ticks) {
3935 if (pos_bbt.bars > 1) {
3936 if (pos_bbt.beats == 1) {
3938 pos_bbt.beats = divisions;
3942 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3948 pos_bbt.ticks -= bbt.ticks;
3951 if (pos_bbt.beats <= bbt.beats) {
3952 if (pos_bbt.bars > 1) {
3954 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3959 pos_bbt.beats -= bbt.beats;
3962 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3969 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3971 return round_to_type (fr, dir, Bar);
3975 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3977 return round_to_type (fr, dir, Beat);
3981 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3983 Glib::Threads::RWLock::ReaderLock lm (lock);
3984 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);
3985 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3986 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3988 ticks -= beats * BBT_Time::ticks_per_beat;
3991 /* round to next (or same iff dir == RoundUpMaybe) */
3993 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3995 if (mod == 0 && dir == RoundUpMaybe) {
3996 /* right on the subdivision, which is fine, so do nothing */
3998 } else if (mod == 0) {
3999 /* right on the subdivision, so the difference is just the subdivision ticks */
4000 ticks += ticks_one_subdivisions_worth;
4003 /* not on subdivision, compute distance to next subdivision */
4005 ticks += ticks_one_subdivisions_worth - mod;
4008 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
4009 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
4010 // And since the "prev" direction DOES move beats, I assume this code is unintended.
4011 // But I'm keeping it around, until we determine there are no terrible consequences.
4012 // if (ticks >= BBT_Time::ticks_per_beat) {
4013 // ticks -= BBT_Time::ticks_per_beat;
4016 } else if (dir < 0) {
4018 /* round to previous (or same iff dir == RoundDownMaybe) */
4020 uint32_t difference = ticks % ticks_one_subdivisions_worth;
4022 if (difference == 0 && dir == RoundDownAlways) {
4023 /* right on the subdivision, but force-rounding down,
4024 so the difference is just the subdivision ticks */
4025 difference = ticks_one_subdivisions_worth;
4028 if (ticks < difference) {
4029 ticks = BBT_Time::ticks_per_beat - ticks;
4031 ticks -= difference;
4035 /* round to nearest */
4038 /* compute the distance to the previous and next subdivision */
4040 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
4042 /* closer to the next subdivision, so shift forward */
4044 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
4046 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
4048 if (ticks > BBT_Time::ticks_per_beat) {
4050 ticks -= BBT_Time::ticks_per_beat;
4051 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
4054 } else if (rem > 0) {
4056 /* closer to previous subdivision, so shift backward */
4060 /* can't go backwards past zero, so ... */
4061 return MusicFrame (0, 0);
4063 /* step back to previous beat */
4065 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4066 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4068 ticks = lrint (ticks - rem);
4069 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4072 /* on the subdivision, do nothing */
4076 MusicFrame ret (0, 0);
4077 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4078 ret.division = sub_num;
4084 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
4086 Glib::Threads::RWLock::ReaderLock lm (lock);
4087 const double minute = minute_at_frame (frame);
4088 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4089 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
4090 MusicFrame ret (0, 0);
4097 /* find bar previous to 'frame' */
4103 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4107 } else if (dir > 0) {
4108 /* find bar following 'frame' */
4113 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4117 /* true rounding: find nearest bar */
4118 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4121 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4123 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4125 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4126 ret.frame = next_ft;
4131 ret.frame = prev_ft;
4143 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
4146 } else if (dir > 0) {
4147 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
4151 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
4158 return MusicFrame (0, 0);
4162 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4163 framepos_t lower, framepos_t upper, uint32_t bar_mod)
4165 Glib::Threads::RWLock::ReaderLock lm (lock);
4166 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
4168 /* although the map handles negative beats, bbt doesn't. */
4173 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
4177 while (pos >= 0 && pos < upper) {
4178 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
4179 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4180 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4181 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4183 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4187 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
4192 bbt.bars -= bbt.bars % bar_mod;
4196 while (pos >= 0 && pos < upper) {
4197 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4198 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4199 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4200 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4201 bbt.bars += bar_mod;
4207 TempoMap::tempo_section_at_frame (framepos_t frame) const
4209 Glib::Threads::RWLock::ReaderLock lm (lock);
4211 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4215 TempoMap::tempo_section_at_frame (framepos_t frame)
4217 Glib::Threads::RWLock::ReaderLock lm (lock);
4219 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4223 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4225 TempoSection* prev = 0;
4229 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4231 if ((*i)->is_tempo()) {
4232 t = static_cast<TempoSection*> (*i);
4236 if (prev && t->minute() > minute) {
4246 abort(); /*NOTREACHED*/
4252 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4254 TempoSection* prev = 0;
4258 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4260 if ((*i)->is_tempo()) {
4261 t = static_cast<TempoSection*> (*i);
4265 if (prev && t->minute() > minute) {
4275 abort(); /*NOTREACHED*/
4281 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4283 TempoSection* prev_t = 0;
4284 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4288 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4289 if ((*i)->is_tempo()) {
4290 t = static_cast<TempoSection*> (*i);
4296 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4306 /* don't use this to calculate length (the tempo is only correct for this frame).
4307 do that stuff based on the beat_at_frame and frame_at_beat api
4310 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4312 Glib::Threads::RWLock::ReaderLock lm (lock);
4314 const TempoSection* ts_at = 0;
4315 const TempoSection* ts_after = 0;
4316 Metrics::const_iterator i;
4319 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4321 if ((*i)->is_tempo()) {
4322 t = static_cast<TempoSection*> (*i);
4326 if (ts_at && (*i)->frame() > frame) {
4336 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4338 /* must be treated as constant tempo */
4339 return ts_at->frames_per_quarter_note (_frame_rate);
4343 TempoMap::meter_section_at_frame (framepos_t frame) const
4345 Glib::Threads::RWLock::ReaderLock lm (lock);
4346 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4350 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4352 Metrics::const_iterator i;
4353 MeterSection* prev = 0;
4357 for (i = metrics.begin(); i != metrics.end(); ++i) {
4359 if (!(*i)->is_tempo()) {
4360 m = static_cast<MeterSection*> (*i);
4362 if (prev && (*i)->minute() > minute) {
4372 abort(); /*NOTREACHED*/
4379 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4381 MeterSection* prev_m = 0;
4383 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4385 if (!(*i)->is_tempo()) {
4386 m = static_cast<MeterSection*> (*i);
4387 if (prev_m && m->beat() > beat) {
4398 TempoMap::meter_section_at_beat (double beat) const
4400 Glib::Threads::RWLock::ReaderLock lm (lock);
4401 return meter_section_at_beat_locked (_metrics, beat);
4405 TempoMap::meter_at_frame (framepos_t frame) const
4407 TempoMetric m (metric_at (frame));
4412 TempoMap::fix_legacy_session ()
4414 MeterSection* prev_m = 0;
4415 TempoSection* prev_t = 0;
4416 bool have_initial_t = false;
4418 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4422 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4424 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4427 m->set_minute (0.0);
4428 m->set_position_lock_style (AudioTime);
4433 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4434 + (m->bbt().beats - 1)
4435 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4437 m->set_beat (start);
4438 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4439 + (m->bbt().beats - 1)
4440 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4441 m->set_pulse (start_beat / prev_m->note_divisor());
4444 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4452 t->set_minute (0.0);
4453 t->set_position_lock_style (AudioTime);
4455 have_initial_t = true;
4460 /* some 4.x sessions have no initial (non-movable) tempo. */
4461 if (!have_initial_t) {
4462 prev_t->set_pulse (0.0);
4463 prev_t->set_minute (0.0);
4464 prev_t->set_position_lock_style (AudioTime);
4465 prev_t->set_initial (true);
4466 prev_t->set_locked_to_meter (true);
4467 have_initial_t = true;
4470 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4471 + (t->legacy_bbt().beats - 1)
4472 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4474 t->set_pulse (beat / prev_m->note_divisor());
4476 /* really shouldn't happen but.. */
4477 t->set_pulse (beat / 4.0);
4485 TempoMap::fix_legacy_end_session ()
4487 TempoSection* prev_t = 0;
4489 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4492 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4499 if (prev_t->type() == TempoSection::Ramp) {
4500 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4502 prev_t->set_end_note_types_per_minute (prev_t->note_types_per_minute());
4512 TempoMap::get_state ()
4514 Metrics::const_iterator i;
4515 XMLNode *root = new XMLNode ("TempoMap");
4518 Glib::Threads::RWLock::ReaderLock lm (lock);
4519 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4520 root->add_child_nocopy ((*i)->get_state());
4528 TempoMap::set_state (const XMLNode& node, int /*version*/)
4531 Glib::Threads::RWLock::WriterLock lm (lock);
4534 XMLNodeConstIterator niter;
4535 Metrics old_metrics (_metrics);
4538 nlist = node.children();
4540 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4541 XMLNode* child = *niter;
4543 if (child->name() == TempoSection::xml_state_node_name) {
4546 TempoSection* ts = new TempoSection (*child, _frame_rate);
4547 _metrics.push_back (ts);
4550 catch (failed_constructor& err){
4551 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4552 _metrics = old_metrics;
4553 old_metrics.clear();
4557 } else if (child->name() == MeterSection::xml_state_node_name) {
4560 MeterSection* ms = new MeterSection (*child, _frame_rate);
4561 _metrics.push_back (ms);
4564 catch (failed_constructor& err) {
4565 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4566 _metrics = old_metrics;
4567 old_metrics.clear();
4573 if (niter == nlist.end()) {
4574 MetricSectionSorter cmp;
4575 _metrics.sort (cmp);
4578 /* check for legacy sessions where bbt was the base musical unit for tempo */
4579 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4581 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4582 if (t->legacy_bbt().bars != 0) {
4583 fix_legacy_session();
4587 if (t->legacy_end()) {
4588 fix_legacy_end_session();
4596 /* check for multiple tempo/meters at the same location, which
4597 ardour2 somehow allowed.
4600 Metrics::iterator prev = _metrics.end();
4601 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4602 if (prev != _metrics.end()) {
4604 MeterSection* prev_m;
4606 TempoSection* prev_t;
4607 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4608 if (prev_m->pulse() == ms->pulse()) {
4609 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4610 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4613 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4614 if (prev_t->pulse() == ts->pulse()) {
4615 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4616 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4624 recompute_map (_metrics);
4626 Metrics::const_iterator d = old_metrics.begin();
4627 while (d != old_metrics.end()) {
4631 old_metrics.clear ();
4634 PropertyChanged (PropertyChange ());
4640 TempoMap::dump (std::ostream& o) const
4642 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4643 const MeterSection* m;
4644 const TempoSection* t;
4645 const TempoSection* prev_t = 0;
4647 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4649 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4650 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4651 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4652 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4653 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4655 o << " current start : " << t->note_types_per_minute()
4656 << " current end : " << t->end_note_types_per_minute()
4657 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4658 o << " previous : " << prev_t->note_types_per_minute()
4659 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4660 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4661 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4662 << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4663 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4666 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4667 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4668 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4669 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4672 o << "------" << std::endl;
4676 TempoMap::n_tempos() const
4678 Glib::Threads::RWLock::ReaderLock lm (lock);
4681 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4682 if ((*i)->is_tempo()) {
4691 TempoMap::n_meters() const
4693 Glib::Threads::RWLock::ReaderLock lm (lock);
4696 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4697 if (!(*i)->is_tempo()) {
4706 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4708 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4709 if ((*i)->frame() >= where && !(*i)->initial ()) {
4713 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4714 gui_set_meter_position (ms, (*i)->frame() + amount);
4717 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4718 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4723 PropertyChanged (PropertyChange ());
4727 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4731 std::list<MetricSection*> metric_kill_list;
4733 TempoSection* last_tempo = NULL;
4734 MeterSection* last_meter = NULL;
4735 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4736 bool meter_after = false; // is there a meter marker likewise?
4738 Glib::Threads::RWLock::WriterLock lm (lock);
4739 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4740 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4741 metric_kill_list.push_back(*i);
4742 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4745 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4749 else if ((*i)->frame() >= where) {
4750 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4751 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4752 if ((*i)->frame() == where) {
4753 // marker was immediately after end of range
4754 tempo_after = dynamic_cast<TempoSection*> (*i);
4755 meter_after = dynamic_cast<MeterSection*> (*i);
4761 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4762 if (last_tempo && !tempo_after) {
4763 metric_kill_list.remove(last_tempo);
4764 last_tempo->set_minute (minute_at_frame (where));
4767 if (last_meter && !meter_after) {
4768 metric_kill_list.remove(last_meter);
4769 last_meter->set_minute (minute_at_frame (where));
4773 //remove all the remaining metrics
4774 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4775 _metrics.remove(*i);
4780 recompute_map (_metrics);
4783 PropertyChanged (PropertyChange ());
4787 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4788 * pos can be -ve, if required.
4791 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4793 Glib::Threads::RWLock::ReaderLock lm (lock);
4794 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4796 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4800 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4802 Glib::Threads::RWLock::ReaderLock lm (lock);
4804 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4805 pos_bbt.ticks += op.ticks;
4806 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4808 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4810 pos_bbt.beats += op.beats;
4811 /* the meter in effect will start on the bar */
4812 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();
4813 while (pos_bbt.beats >= divisions_per_bar + 1) {
4815 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4816 pos_bbt.beats -= divisions_per_bar;
4818 pos_bbt.bars += op.bars;
4820 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4823 /** Count the number of beats that are equivalent to distance when going forward,
4827 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4829 Glib::Threads::RWLock::ReaderLock lm (lock);
4831 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4835 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4841 operator<< (std::ostream& o, const Meter& m) {
4842 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4846 operator<< (std::ostream& o, const Tempo& t) {
4847 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4851 operator<< (std::ostream& o, const MetricSection& section) {
4853 o << "MetricSection @ " << section.frame() << ' ';
4855 const TempoSection* ts;
4856 const MeterSection* ms;
4858 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4859 o << *((const Tempo*) ts);
4860 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4861 o << *((const Meter*) ms);