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 //if (prev_tempo && prev_tempo->type() == TempoSection::Ramp) {
1159 new_ts->set_end_note_types_per_minute (this_t->note_types_per_minute());
1163 //prev_tempo = this_t;
1170 first.set_type (type);
1171 first.set_pulse (0.0);
1172 first.set_minute (minute_at_frame (frame));
1173 first.set_position_lock_style (AudioTime);
1174 first.set_locked_to_meter (true);
1176 /* cannot move the first tempo section */
1177 *static_cast<Tempo*>(&first) = tempo;
1180 recompute_map (_metrics);
1183 PropertyChanged (PropertyChange ());
1187 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1188 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1190 TempoSection* t = new TempoSection (pulse, minute, tempo, type, pls, _frame_rate);
1191 t->set_locked_to_meter (locked_to_meter);
1196 if (pls == AudioTime) {
1197 solve_map_minute (_metrics, t, t->minute());
1199 solve_map_pulse (_metrics, t, t->pulse());
1201 recompute_meters (_metrics);
1208 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1210 MeterSection* m = 0;
1212 Glib::Threads::RWLock::WriterLock lm (lock);
1213 m = add_meter_locked (meter, beat, where, frame, pls, true);
1218 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1223 PropertyChanged (PropertyChange ());
1228 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1231 Glib::Threads::RWLock::WriterLock lm (lock);
1232 const double beat = beat_at_bbt_locked (_metrics, where);
1234 if (!ms.initial()) {
1235 remove_meter_locked (ms);
1236 add_meter_locked (meter, beat, where, frame, pls, true);
1238 MeterSection& first (first_meter());
1239 TempoSection& first_t (first_tempo());
1240 /* cannot move the first meter section */
1241 *static_cast<Meter*>(&first) = meter;
1242 first.set_position_lock_style (AudioTime);
1243 first.set_pulse (0.0);
1244 first.set_minute (minute_at_frame (frame));
1245 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1246 first.set_beat (beat);
1247 first_t.set_minute (first.minute());
1248 first_t.set_locked_to_meter (true);
1249 first_t.set_pulse (0.0);
1250 first_t.set_position_lock_style (AudioTime);
1251 recompute_map (_metrics);
1255 PropertyChanged (PropertyChange ());
1259 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1261 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1262 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1263 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1264 TempoSection* mlt = 0;
1266 if (pls == AudioTime) {
1267 /* add meter-locked tempo */
1268 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), TempoSection::Ramp, AudioTime, true, true);
1276 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1278 bool solved = false;
1280 do_insert (new_meter);
1284 if (pls == AudioTime) {
1285 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1286 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1287 fudge frame so that the meter ends up at its BBT position instead.
1290 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1293 solved = solve_map_bbt (_metrics, new_meter, where);
1294 /* required due to resetting the pulse of meter-locked tempi above.
1295 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1296 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1298 recompute_map (_metrics);
1302 if (!solved && recompute) {
1303 /* if this has failed to solve, there is little we can do other than to ensure that
1304 the new map is recalculated.
1306 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1307 recompute_map (_metrics);
1314 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type, double end_note_types_per_minute)
1316 Tempo newtempo (note_types_per_minute, note_type, end_note_types_per_minute);
1319 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1320 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1325 Glib::Threads::RWLock::WriterLock lm (lock);
1326 *((Tempo*) t) = newtempo;
1327 recompute_map (_metrics);
1329 PropertyChanged (PropertyChange ());
1336 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type, double end_ntpm)
1338 Tempo newtempo (note_types_per_minute, note_type, end_ntpm);
1341 TempoSection* first;
1342 Metrics::iterator i;
1344 /* find the TempoSection immediately preceding "where"
1347 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1349 if ((*i)->frame() > where) {
1355 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1368 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1378 Glib::Threads::RWLock::WriterLock lm (lock);
1379 /* cannot move the first tempo section */
1380 *((Tempo*)prev) = newtempo;
1381 recompute_map (_metrics);
1384 PropertyChanged (PropertyChange ());
1388 TempoMap::first_meter () const
1390 const MeterSection *m = 0;
1392 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1393 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1398 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1399 abort(); /*NOTREACHED*/
1404 TempoMap::first_meter ()
1406 MeterSection *m = 0;
1408 /* CALLER MUST HOLD LOCK */
1410 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1411 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1416 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1417 abort(); /*NOTREACHED*/
1422 TempoMap::first_tempo () const
1424 const TempoSection *t = 0;
1426 /* CALLER MUST HOLD LOCK */
1428 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1429 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1439 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1440 abort(); /*NOTREACHED*/
1445 TempoMap::first_tempo ()
1447 TempoSection *t = 0;
1449 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1450 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1460 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1461 abort(); /*NOTREACHED*/
1465 TempoMap::recompute_tempi (Metrics& metrics)
1467 TempoSection* prev_t = 0;
1469 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1472 if ((*i)->is_tempo()) {
1473 t = static_cast<TempoSection*> (*i);
1485 if (t->position_lock_style() == AudioTime) {
1486 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
1487 if (!t->locked_to_meter()) {
1488 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
1492 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
1493 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
1501 prev_t->set_c (0.0);
1504 /* tempos must be positioned correctly.
1505 the current approach is to use a meter's bbt time as its base position unit.
1506 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1507 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1510 TempoMap::recompute_meters (Metrics& metrics)
1512 MeterSection* meter = 0;
1513 MeterSection* prev_m = 0;
1515 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1516 if (!(*mi)->is_tempo()) {
1517 meter = static_cast<MeterSection*> (*mi);
1518 if (meter->position_lock_style() == AudioTime) {
1520 pair<double, BBT_Time> b_bbt;
1521 TempoSection* meter_locked_tempo = 0;
1522 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1524 if ((*ii)->is_tempo()) {
1525 t = static_cast<TempoSection*> (*ii);
1526 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1527 meter_locked_tempo = t;
1534 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1535 if (beats + prev_m->beat() != meter->beat()) {
1536 /* reordering caused a bbt change */
1538 beats = meter->beat() - prev_m->beat();
1539 b_bbt = make_pair (beats + prev_m->beat()
1540 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1541 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1543 } else if (!meter->initial()) {
1544 b_bbt = make_pair (meter->beat(), meter->bbt());
1545 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1548 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1550 if (meter_locked_tempo) {
1551 meter_locked_tempo->set_pulse (pulse);
1553 meter->set_beat (b_bbt);
1554 meter->set_pulse (pulse);
1559 pair<double, BBT_Time> b_bbt;
1561 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1562 if (beats + prev_m->beat() != meter->beat()) {
1563 /* reordering caused a bbt change */
1564 b_bbt = make_pair (beats + prev_m->beat()
1565 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1567 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1569 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1571 /* shouldn't happen - the first is audio-locked */
1572 pulse = pulse_at_beat_locked (metrics, meter->beat());
1573 b_bbt = make_pair (meter->beat(), meter->bbt());
1576 meter->set_beat (b_bbt);
1577 meter->set_pulse (pulse);
1578 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1587 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1589 /* CALLER MUST HOLD WRITE LOCK */
1591 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1594 /* silly call from Session::process() during startup
1599 recompute_tempi (metrics);
1600 recompute_meters (metrics);
1604 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1606 Glib::Threads::RWLock::ReaderLock lm (lock);
1607 TempoMetric m (first_meter(), first_tempo());
1609 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1610 at something, because we insert the default tempo and meter during
1611 TempoMap construction.
1613 now see if we can find better candidates.
1616 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1618 if ((*i)->frame() > frame) {
1632 /* XX meters only */
1634 TempoMap::metric_at (BBT_Time bbt) const
1636 Glib::Threads::RWLock::ReaderLock lm (lock);
1637 TempoMetric m (first_meter(), first_tempo());
1639 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1640 at something, because we insert the default tempo and meter during
1641 TempoMap construction.
1643 now see if we can find better candidates.
1646 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1648 if (!(*i)->is_tempo()) {
1649 mw = static_cast<MeterSection*> (*i);
1650 BBT_Time section_start (mw->bbt());
1652 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1663 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1664 * @param frame The session frame position.
1665 * @return The beat duration according to the tempo map at the supplied frame.
1667 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1668 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1670 * This function uses both tempo and meter.
1673 TempoMap::beat_at_frame (const framecnt_t& frame) const
1675 Glib::Threads::RWLock::ReaderLock lm (lock);
1677 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1680 /* This function uses both tempo and meter.*/
1682 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1684 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1685 MeterSection* prev_m = 0;
1686 MeterSection* next_m = 0;
1688 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1689 if (!(*i)->is_tempo()) {
1690 if (prev_m && (*i)->minute() > minute) {
1691 next_m = static_cast<MeterSection*> (*i);
1694 prev_m = static_cast<MeterSection*> (*i);
1698 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1700 /* audio locked meters fake their beat */
1701 if (next_m && next_m->beat() < beat) {
1702 return next_m->beat();
1708 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1709 * @param beat The BBT (meter-based) beat.
1710 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1712 * This function uses both tempo and meter.
1715 TempoMap::frame_at_beat (const double& beat) const
1717 Glib::Threads::RWLock::ReaderLock lm (lock);
1719 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1722 /* meter & tempo section based */
1724 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1726 MeterSection* prev_m = 0;
1727 TempoSection* prev_t = 0;
1731 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1732 if (!(*i)->is_tempo()) {
1733 m = static_cast<MeterSection*> (*i);
1734 if (prev_m && m->beat() > beat) {
1744 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1745 if ((*i)->is_tempo()) {
1746 t = static_cast<TempoSection*> (*i);
1752 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1761 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1764 /** Returns a Tempo corresponding to the supplied frame position.
1765 * @param frame The audio frame.
1766 * @return a Tempo according to the tempo map at the supplied frame.
1770 TempoMap::tempo_at_frame (const framepos_t& frame) const
1772 Glib::Threads::RWLock::ReaderLock lm (lock);
1774 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1778 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1780 TempoSection* prev_t = 0;
1784 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1785 if ((*i)->is_tempo()) {
1786 t = static_cast<TempoSection*> (*i);
1790 if ((prev_t) && t->minute() > minute) {
1791 /* t is the section past frame */
1792 return prev_t->tempo_at_minute (minute);
1798 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1801 /** returns the frame at which the supplied tempo occurs, or
1802 * the frame of the last tempo section (search exhausted)
1803 * only the position of the first occurence will be returned
1807 TempoMap::frame_at_tempo (const Tempo& tempo) const
1809 Glib::Threads::RWLock::ReaderLock lm (lock);
1811 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1815 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1817 TempoSection* prev_t = 0;
1818 const double tempo_bpm = tempo.note_types_per_minute();
1820 Metrics::const_iterator i;
1822 for (i = metrics.begin(); i != metrics.end(); ++i) {
1824 if ((*i)->is_tempo()) {
1825 t = static_cast<TempoSection*> (*i);
1831 const double t_bpm = t->note_types_per_minute();
1833 if (t_bpm == tempo_bpm) {
1838 const double prev_t_bpm = prev_t->note_types_per_minute();
1840 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1841 return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse());
1848 return prev_t->minute();
1852 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1854 TempoSection* prev_t = 0;
1858 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1859 if ((*i)->is_tempo()) {
1860 t = static_cast<TempoSection*> (*i);
1864 if ((prev_t) && t->pulse() > pulse) {
1865 /* t is the section past frame */
1866 return prev_t->tempo_at_pulse (pulse);
1872 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type(), prev_t->end_note_types_per_minute());
1876 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1878 TempoSection* prev_t = 0;
1879 const double tempo_bpm = tempo.note_types_per_minute();
1881 Metrics::const_iterator i;
1883 for (i = metrics.begin(); i != metrics.end(); ++i) {
1885 if ((*i)->is_tempo()) {
1886 t = static_cast<TempoSection*> (*i);
1892 const double t_bpm = t->note_types_per_minute();
1894 if (t_bpm == tempo_bpm) {
1899 const double prev_t_bpm = prev_t->note_types_per_minute();
1901 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1902 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1909 return prev_t->pulse();
1912 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1913 * @param qn the position in quarter note beats.
1914 * @return the Tempo at the supplied quarter-note.
1917 TempoMap::tempo_at_quarter_note (const double& qn) const
1919 Glib::Threads::RWLock::ReaderLock lm (lock);
1921 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1924 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1925 * @param tempo the tempo.
1926 * @return the position in quarter-note beats where the map bpm
1927 * is equal to that of the Tempo. currently ignores note_type.
1930 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1932 Glib::Threads::RWLock::ReaderLock lm (lock);
1934 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1937 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1938 * @param metrics the list of metric sections used to calculate the pulse.
1939 * @param beat The BBT (meter-based) beat.
1940 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1942 * a pulse or whole note is the base musical position of a MetricSection.
1943 * it is equivalent to four quarter notes.
1947 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1949 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1951 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1954 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1955 * @param metrics the list of metric sections used to calculate the beat.
1956 * @param pulse the whole-note pulse.
1957 * @return the meter-based beat at the supplied whole-note pulse.
1959 * a pulse or whole note is the base musical position of a MetricSection.
1960 * it is equivalent to four quarter notes.
1963 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1965 MeterSection* prev_m = 0;
1967 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1969 if (!(*i)->is_tempo()) {
1970 m = static_cast<MeterSection*> (*i);
1971 if (prev_m && m->pulse() > pulse) {
1979 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1983 /* tempo section based */
1985 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1987 /* HOLD (at least) THE READER LOCK */
1988 TempoSection* prev_t = 0;
1990 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1992 if ((*i)->is_tempo()) {
1993 t = static_cast<TempoSection*> (*i);
1997 if (prev_t && t->minute() > minute) {
1998 /*the previous ts is the one containing the frame */
1999 const double ret = prev_t->pulse_at_minute (minute);
2000 /* audio locked section in new meter*/
2001 if (t->pulse() < ret) {
2010 /* treated as constant for this ts */
2011 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
2013 return pulses_in_section + prev_t->pulse();
2016 /* tempo section based */
2018 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2020 /* HOLD THE READER LOCK */
2022 const TempoSection* prev_t = 0;
2024 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2027 if ((*i)->is_tempo()) {
2028 t = static_cast<TempoSection*> (*i);
2032 if (prev_t && t->pulse() > pulse) {
2033 return prev_t->minute_at_pulse (pulse);
2039 /* must be treated as constant, irrespective of _type */
2040 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
2042 return dtime + prev_t->minute();
2045 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
2046 * @param bbt The BBT time (meter-based).
2047 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
2051 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
2053 Glib::Threads::RWLock::ReaderLock lm (lock);
2054 return beat_at_bbt_locked (_metrics, bbt);
2059 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2061 /* CALLER HOLDS READ LOCK */
2063 MeterSection* prev_m = 0;
2065 /* because audio-locked meters have 'fake' integral beats,
2066 there is no pulse offset here.
2070 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2071 if (!(*i)->is_tempo()) {
2072 m = static_cast<MeterSection*> (*i);
2074 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2075 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2083 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2084 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2085 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2090 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2091 * @param beat The BBT (meter-based) beat.
2092 * @return The BBT time (meter-based) at the supplied meter-based beat.
2096 TempoMap::bbt_at_beat (const double& beat)
2098 Glib::Threads::RWLock::ReaderLock lm (lock);
2099 return bbt_at_beat_locked (_metrics, beat);
2103 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2105 /* CALLER HOLDS READ LOCK */
2106 MeterSection* prev_m = 0;
2107 const double beats = max (0.0, b);
2109 MeterSection* m = 0;
2111 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2112 if (!(*i)->is_tempo()) {
2113 m = static_cast<MeterSection*> (*i);
2115 if (m->beat() > beats) {
2116 /* this is the meter after the one our beat is on*/
2126 const double beats_in_ms = beats - prev_m->beat();
2127 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2128 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2129 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2130 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2134 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2135 ret.beats = (uint32_t) floor (remaining_beats);
2136 ret.bars = total_bars;
2138 /* 0 0 0 to 1 1 0 - based mapping*/
2142 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2144 ret.ticks -= BBT_Time::ticks_per_beat;
2147 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2155 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2156 * @param bbt The BBT time (meter-based).
2157 * @return the quarter note beat at the supplied BBT time
2159 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2161 * while the input uses meter, the output does not.
2164 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2166 Glib::Threads::RWLock::ReaderLock lm (lock);
2168 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2172 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2174 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2177 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2180 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2184 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2186 /* CALLER HOLDS READ LOCK */
2188 MeterSection* prev_m = 0;
2190 /* because audio-locked meters have 'fake' integral beats,
2191 there is no pulse offset here.
2195 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2196 if (!(*i)->is_tempo()) {
2197 m = static_cast<MeterSection*> (*i);
2199 if (m->bbt().bars > bbt.bars) {
2207 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2208 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2209 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2214 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2215 * @param qn the quarter-note beat.
2216 * @return The BBT time (meter-based) at the supplied meter-based beat.
2218 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2222 TempoMap::bbt_at_quarter_note (const double& qn)
2224 Glib::Threads::RWLock::ReaderLock lm (lock);
2226 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2229 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2230 * @param metrics The list of metric sections used to determine the result.
2231 * @param pulse The whole-note pulse.
2232 * @return The BBT time at the supplied whole-note pulse.
2234 * a pulse or whole note is the basic musical position of a MetricSection.
2235 * it is equivalent to four quarter notes.
2236 * while the output uses meter, the input does not.
2239 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2241 MeterSection* prev_m = 0;
2243 MeterSection* m = 0;
2245 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2247 if (!(*i)->is_tempo()) {
2248 m = static_cast<MeterSection*> (*i);
2251 double const pulses_to_m = m->pulse() - prev_m->pulse();
2252 if (prev_m->pulse() + pulses_to_m > pulse) {
2253 /* this is the meter after the one our beat is on*/
2264 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2265 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2266 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2267 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2268 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2272 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2273 ret.beats = (uint32_t) floor (remaining_beats);
2274 ret.bars = total_bars;
2276 /* 0 0 0 to 1 1 0 mapping*/
2280 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2282 ret.ticks -= BBT_Time::ticks_per_beat;
2285 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2293 /** Returns the BBT time corresponding to the supplied frame position.
2294 * @param frame the position in audio samples.
2295 * @return the BBT time at the frame position .
2299 TempoMap::bbt_at_frame (framepos_t frame)
2307 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2312 const double minute = minute_at_frame (frame);
2314 Glib::Threads::RWLock::ReaderLock lm (lock);
2316 return bbt_at_minute_locked (_metrics, minute);
2320 TempoMap::bbt_at_frame_rt (framepos_t frame)
2322 const double minute = minute_at_frame (frame);
2324 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2327 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2330 return bbt_at_minute_locked (_metrics, minute);
2334 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2344 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2345 MeterSection* prev_m = 0;
2346 MeterSection* next_m = 0;
2350 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2351 if (!(*i)->is_tempo()) {
2352 m = static_cast<MeterSection*> (*i);
2353 if (prev_m && m->minute() > minute) {
2361 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2363 /* handle frame before first meter */
2364 if (minute < prev_m->minute()) {
2367 /* audio locked meters fake their beat */
2368 if (next_m && next_m->beat() < beat) {
2369 beat = next_m->beat();
2372 beat = max (0.0, beat);
2374 const double beats_in_ms = beat - prev_m->beat();
2375 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2376 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2377 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2378 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2382 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2383 ret.beats = (uint32_t) floor (remaining_beats);
2384 ret.bars = total_bars;
2386 /* 0 0 0 to 1 1 0 - based mapping*/
2390 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2392 ret.ticks -= BBT_Time::ticks_per_beat;
2395 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2403 /** Returns the frame position corresponding to the supplied BBT time.
2404 * @param bbt the position in BBT time.
2405 * @return the frame position at bbt.
2409 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2413 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2418 if (bbt.beats < 1) {
2419 throw std::logic_error ("beats are counted from one");
2424 Glib::Threads::RWLock::ReaderLock lm (lock);
2425 minute = minute_at_bbt_locked (_metrics, bbt);
2428 return frame_at_minute (minute);
2431 /* meter & tempo section based */
2433 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2435 /* HOLD THE READER LOCK */
2437 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2442 * Returns the quarter-note beat position corresponding to the supplied frame.
2444 * @param frame the position in frames.
2445 * @return The quarter-note position of the supplied frame. Ignores meter.
2449 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2451 const double minute = minute_at_frame (frame);
2453 Glib::Threads::RWLock::ReaderLock lm (lock);
2455 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2459 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2461 const double minute = minute_at_frame (frame);
2463 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2466 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2469 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2473 * Returns the frame position corresponding to the supplied quarter-note beat.
2475 * @param quarter_note the quarter-note position.
2476 * @return the frame position of the supplied quarter-note. Ignores meter.
2481 TempoMap::frame_at_quarter_note (const double quarter_note) const
2485 Glib::Threads::RWLock::ReaderLock lm (lock);
2487 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2490 return frame_at_minute (minute);
2493 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2494 * @param beat The BBT (meter-based) beat.
2495 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2497 * a quarter-note may be compared with and assigned to Evoral::Beats.
2501 TempoMap::quarter_note_at_beat (const double beat) const
2503 Glib::Threads::RWLock::ReaderLock lm (lock);
2505 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2508 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2509 * @param quarter_note The position in quarter-note beats.
2510 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2512 * a quarter-note is the musical unit of Evoral::Beats.
2516 TempoMap::beat_at_quarter_note (const double quarter_note) const
2518 Glib::Threads::RWLock::ReaderLock lm (lock);
2520 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2523 /** Returns the duration in frames between two supplied quarter-note beat positions.
2524 * @param start the first position in quarter-note beats.
2525 * @param end the end position in quarter-note beats.
2526 * @return the frame distance ober the quarter-note beats duration.
2528 * use this rather than e.g.
2529 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2530 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2534 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2539 Glib::Threads::RWLock::ReaderLock lm (lock);
2540 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2543 return frame_at_minute (minutes);
2547 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2550 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2554 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2556 Glib::Threads::RWLock::ReaderLock lm (lock);
2558 return quarter_notes_between_frames_locked (_metrics, start, end);
2562 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2564 const TempoSection* prev_t = 0;
2566 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2569 if ((*i)->is_tempo()) {
2570 t = static_cast<TempoSection*> (*i);
2574 if (prev_t && t->frame() > start) {
2581 const double start_qn = prev_t->pulse_at_frame (start);
2583 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2586 if ((*i)->is_tempo()) {
2587 t = static_cast<TempoSection*> (*i);
2591 if (prev_t && t->frame() > end) {
2597 const double end_qn = prev_t->pulse_at_frame (end);
2599 return (end_qn - start_qn) * 4.0;
2603 TempoMap::check_solved (const Metrics& metrics) const
2605 TempoSection* prev_t = 0;
2606 MeterSection* prev_m = 0;
2608 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2611 if ((*i)->is_tempo()) {
2612 t = static_cast<TempoSection*> (*i);
2617 /* check ordering */
2618 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2622 /* precision check ensures tempo and frames align.*/
2623 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))) {
2624 if (!t->locked_to_meter()) {
2629 /* gradient limit - who knows what it should be?
2630 things are also ok (if a little chaotic) without this
2632 if (fabs (prev_t->c()) > 1000.0) {
2633 //std::cout << "c : " << prev_t->c() << std::endl;
2640 if (!(*i)->is_tempo()) {
2641 m = static_cast<MeterSection*> (*i);
2642 if (prev_m && m->position_lock_style() == AudioTime) {
2643 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2644 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2645 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2647 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2661 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2663 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2665 if ((*i)->is_tempo()) {
2666 t = static_cast<TempoSection*> (*i);
2667 if (t->locked_to_meter()) {
2668 t->set_active (true);
2669 } else if (t->position_lock_style() == AudioTime) {
2670 if (t->frame() < frame) {
2671 t->set_active (false);
2672 t->set_pulse (-1.0);
2673 } else if (t->frame() > frame) {
2674 t->set_active (true);
2675 } else if (t->frame() == frame) {
2685 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2687 TempoSection* prev_t = 0;
2688 TempoSection* section_prev = 0;
2689 double first_m_minute = 0.0;
2690 const bool sml = section->locked_to_meter();
2692 /* can't move a tempo before the first meter */
2693 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2695 if (!(*i)->is_tempo()) {
2696 m = static_cast<MeterSection*> (*i);
2698 first_m_minute = m->minute();
2703 if (!section->initial() && minute <= first_m_minute) {
2707 section->set_active (true);
2708 section->set_minute (minute);
2710 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2712 if ((*i)->is_tempo()) {
2713 t = static_cast<TempoSection*> (*i);
2725 if (t->frame() == frame_at_minute (minute)) {
2729 const bool tlm = t->position_lock_style() == MusicTime;
2731 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2732 section_prev = prev_t;
2734 section_prev->set_c (section_prev->compute_c_minute (section_prev->end_note_types_per_minute(), minute));
2735 if (!section->locked_to_meter()) {
2736 section->set_pulse (section_prev->pulse_at_ntpm (section_prev->end_note_types_per_minute(), minute));
2741 if (t->position_lock_style() == MusicTime) {
2742 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2743 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2745 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2746 if (!t->locked_to_meter()) {
2747 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2756 recompute_tempi (imaginary);
2758 if (check_solved (imaginary)) {
2761 dunp (imaginary, std::cout);
2765 MetricSectionFrameSorter fcmp;
2766 imaginary.sort (fcmp);
2768 recompute_tempi (imaginary);
2770 if (check_solved (imaginary)) {
2778 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2780 TempoSection* prev_t = 0;
2781 TempoSection* section_prev = 0;
2783 section->set_pulse (pulse);
2785 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2787 if ((*i)->is_tempo()) {
2788 t = static_cast<TempoSection*> (*i);
2799 section_prev = prev_t;
2803 if (t->position_lock_style() == MusicTime) {
2804 prev_t->set_c (prev_t->compute_c_pulse (prev_t->end_note_types_per_minute(), t->pulse()));
2805 t->set_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()));
2807 prev_t->set_c (prev_t->compute_c_minute (prev_t->end_note_types_per_minute(), t->minute()));
2808 if (!t->locked_to_meter()) {
2809 t->set_pulse (prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute()));
2818 section_prev->set_c (section_prev->compute_c_pulse (section_prev->end_note_types_per_minute(), pulse));
2819 section->set_minute (section_prev->minute_at_ntpm (section_prev->end_note_types_per_minute(), pulse));
2823 recompute_tempi (imaginary);
2825 if (check_solved (imaginary)) {
2828 dunp (imaginary, std::cout);
2832 MetricSectionSorter cmp;
2833 imaginary.sort (cmp);
2835 recompute_tempi (imaginary);
2837 * XX need a restriction here, but only for this case,
2838 * as audio locked tempos don't interact in the same way.
2840 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2842 * |50 bpm |250 bpm |60 bpm
2843 * drag 250 to the pulse after 60->
2844 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2846 if (check_solved (imaginary)) {
2854 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2856 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2857 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2858 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2862 if (section->initial()) {
2863 /* lock the first tempo to our first meter */
2864 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2869 TempoSection* meter_locked_tempo = 0;
2871 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2873 if ((*ii)->is_tempo()) {
2874 t = static_cast<TempoSection*> (*ii);
2875 if (t->locked_to_meter() && t->frame() == section->frame()) {
2876 meter_locked_tempo = t;
2882 if (!meter_locked_tempo) {
2886 MeterSection* prev_m = 0;
2888 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2889 bool solved = false;
2891 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2893 if (!(*i)->is_tempo()) {
2894 m = static_cast<MeterSection*> (*i);
2896 if (prev_m && !section->initial()) {
2897 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2898 if (beats + prev_m->beat() < section->beat()) {
2899 /* set the section pulse according to its musical position,
2900 * as an earlier time than this has been requested.
2902 const double new_pulse = ((section->beat() - prev_m->beat())
2903 / prev_m->note_divisor()) + prev_m->pulse();
2905 tempo_copy->set_position_lock_style (MusicTime);
2906 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2907 meter_locked_tempo->set_position_lock_style (MusicTime);
2908 section->set_position_lock_style (MusicTime);
2909 section->set_pulse (new_pulse);
2910 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2911 meter_locked_tempo->set_position_lock_style (AudioTime);
2912 section->set_position_lock_style (AudioTime);
2913 section->set_minute (meter_locked_tempo->minute());
2919 Metrics::const_iterator d = future_map.begin();
2920 while (d != future_map.end()) {
2929 /* all is ok. set section's locked tempo if allowed.
2930 possibly disallow if there is an adjacent audio-locked tempo.
2931 XX this check could possibly go. its never actually happened here.
2933 MeterSection* meter_copy = const_cast<MeterSection*>
2934 (&meter_section_at_minute_locked (future_map, section->minute()));
2936 meter_copy->set_minute (minute);
2938 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2939 section->set_minute (minute);
2940 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2941 / prev_m->note_divisor()) + prev_m->pulse());
2942 solve_map_minute (imaginary, meter_locked_tempo, minute);
2947 Metrics::const_iterator d = future_map.begin();
2948 while (d != future_map.end()) {
2958 /* initial (first meter atm) */
2960 tempo_copy->set_minute (minute);
2961 tempo_copy->set_pulse (0.0);
2963 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2964 section->set_minute (minute);
2965 meter_locked_tempo->set_minute (minute);
2966 meter_locked_tempo->set_pulse (0.0);
2967 solve_map_minute (imaginary, meter_locked_tempo, minute);
2972 Metrics::const_iterator d = future_map.begin();
2973 while (d != future_map.end()) {
2982 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2983 section->set_beat (b_bbt);
2984 section->set_pulse (0.0);
2994 MetricSectionFrameSorter fcmp;
2995 imaginary.sort (fcmp);
2997 recompute_meters (imaginary);
3003 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
3005 /* disallow setting section to an existing meter's bbt */
3006 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
3008 if (!(*i)->is_tempo()) {
3009 m = static_cast<MeterSection*> (*i);
3010 if (m != section && m->bbt().bars == when.bars) {
3016 MeterSection* prev_m = 0;
3017 MeterSection* section_prev = 0;
3019 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
3021 if (!(*i)->is_tempo()) {
3022 m = static_cast<MeterSection*> (*i);
3028 pair<double, BBT_Time> b_bbt;
3029 double new_pulse = 0.0;
3031 if (prev_m && m->bbt().bars > when.bars && !section_prev){
3032 section_prev = prev_m;
3034 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
3035 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
3036 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
3038 section->set_beat (b_bbt);
3039 section->set_pulse (pulse);
3040 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3044 if (m->position_lock_style() == AudioTime) {
3045 TempoSection* meter_locked_tempo = 0;
3047 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
3049 if ((*ii)->is_tempo()) {
3050 t = static_cast<TempoSection*> (*ii);
3051 if (t->locked_to_meter() && t->frame() == m->frame()) {
3052 meter_locked_tempo = t;
3058 if (!meter_locked_tempo) {
3063 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3065 if (beats + prev_m->beat() != m->beat()) {
3066 /* tempo/ meter change caused a change in beat (bar). */
3068 /* the user has requested that the previous section of music overlaps this one.
3069 we have no choice but to change the bar number here, as being locked to audio means
3070 we must stay where we are on the timeline.
3072 beats = m->beat() - prev_m->beat();
3073 b_bbt = make_pair (beats + prev_m->beat()
3074 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3075 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3077 } else if (!m->initial()) {
3078 b_bbt = make_pair (m->beat(), m->bbt());
3079 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3082 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3085 meter_locked_tempo->set_pulse (new_pulse);
3086 m->set_beat (b_bbt);
3087 m->set_pulse (new_pulse);
3091 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3092 if (beats + prev_m->beat() != m->beat()) {
3093 /* tempo/ meter change caused a change in beat (bar). */
3094 b_bbt = make_pair (beats + prev_m->beat()
3095 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3097 b_bbt = make_pair (beats + prev_m->beat()
3100 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3101 m->set_beat (b_bbt);
3102 m->set_pulse (new_pulse);
3103 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3110 if (!section_prev) {
3112 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3113 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3114 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3116 section->set_beat (b_bbt);
3117 section->set_pulse (pulse);
3118 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3121 MetricSectionSorter cmp;
3122 imaginary.sort (cmp);
3124 recompute_meters (imaginary);
3129 /** places a copy of _metrics into copy and returns a pointer
3130 * to section's equivalent in copy.
3133 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
3135 TempoSection* ret = 0;
3137 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3140 if ((*i)->is_tempo()) {
3141 t = static_cast<TempoSection*> (*i);
3143 ret = new TempoSection (*t);
3144 copy.push_back (ret);
3148 TempoSection* cp = new TempoSection (*t);
3149 copy.push_back (cp);
3151 if (!(*i)->is_tempo()) {
3152 m = static_cast<MeterSection *> (*i);
3153 MeterSection* cp = new MeterSection (*m);
3154 copy.push_back (cp);
3162 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3164 MeterSection* ret = 0;
3166 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3169 if ((*i)->is_tempo()) {
3170 t = static_cast<TempoSection*> (*i);
3171 TempoSection* cp = new TempoSection (*t);
3172 copy.push_back (cp);
3175 if (!(*i)->is_tempo()) {
3176 m = static_cast<MeterSection *> (*i);
3178 ret = new MeterSection (*m);
3179 copy.push_back (ret);
3182 MeterSection* cp = new MeterSection (*m);
3183 copy.push_back (cp);
3190 /** answers the question "is this a valid beat position for this tempo section?".
3191 * it returns true if the tempo section can be moved to the requested bbt position,
3192 * leaving the tempo map in a solved state.
3193 * @param ts the tempo section to be moved
3194 * @param bbt the requested new position for the tempo section
3195 * @return true if the tempo section can be moved to the position, otherwise false.
3198 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3201 TempoSection* tempo_copy = 0;
3204 Glib::Threads::RWLock::ReaderLock lm (lock);
3205 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3211 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3213 Metrics::const_iterator d = copy.begin();
3214 while (d != copy.end()) {
3223 * 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,
3224 * taking any possible reordering as a consequence of this into account.
3225 * @param section - the section to be altered
3226 * @param bbt - the BBT time where the altered tempo will fall
3227 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3229 pair<double, framepos_t>
3230 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3233 pair<double, framepos_t> ret = make_pair (0.0, 0);
3235 Glib::Threads::RWLock::ReaderLock lm (lock);
3237 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3239 const double beat = beat_at_bbt_locked (future_map, bbt);
3241 if (section->position_lock_style() == AudioTime) {
3242 tempo_copy->set_position_lock_style (MusicTime);
3245 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3246 ret.first = tempo_copy->pulse();
3247 ret.second = tempo_copy->frame();
3249 ret.first = section->pulse();
3250 ret.second = section->frame();
3253 Metrics::const_iterator d = future_map.begin();
3254 while (d != future_map.end()) {
3261 /** moves a TempoSection to a specified position.
3262 * @param ts - the section to be moved
3263 * @param frame - the new position in frames for the tempo
3264 * @param sub_num - the snap division to use if using musical time.
3266 * if sub_num is non-zero, the frame position is used to calculate an exact
3269 * -1 | snap to bars (meter-based)
3270 * 0 | no snap - use audio frame for musical position
3271 * 1 | snap to meter-based (BBT) beat
3272 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3274 * this follows the snap convention in the gui.
3275 * if sub_num is zero, the musical position will be taken from the supplied frame.
3278 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3282 if (ts->position_lock_style() == MusicTime) {
3284 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3285 Glib::Threads::RWLock::WriterLock lm (lock);
3286 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3288 tempo_copy->set_position_lock_style (AudioTime);
3290 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3291 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3292 const double pulse = pulse_at_beat_locked (future_map, beat);
3294 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3295 solve_map_pulse (_metrics, ts, pulse);
3296 recompute_meters (_metrics);
3304 Glib::Threads::RWLock::WriterLock lm (lock);
3305 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3307 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3309 /* We're moving the object that defines the grid while snapping to it...
3310 * Placing the ts at the beat corresponding to the requested frame may shift the
3311 * grid in such a way that the mouse is left hovering over a completerly different division,
3312 * causing jittering when the mouse next moves (esp. large tempo deltas).
3314 * This alters the snap behaviour slightly in that we snap to beat divisions
3315 * in the future map rather than the existing one.
3317 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3318 const framepos_t snapped_frame = frame_at_minute (minute_at_pulse_locked (future_map, qn / 4.0));
3320 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3321 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3322 ts->set_pulse (qn / 4.0);
3323 recompute_meters (_metrics);
3326 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3327 recompute_meters (_metrics);
3333 Metrics::const_iterator d = future_map.begin();
3334 while (d != future_map.end()) {
3339 MetricPositionChanged (PropertyChange ()); // Emit Signal
3342 /** moves a MeterSection to a specified position.
3343 * @param ms - the section to be moved
3344 * @param frame - the new position in frames for the meter
3346 * as a meter cannot snap to anything but bars,
3347 * the supplied frame is rounded to the nearest bar, possibly
3348 * leaving the meter position unchanged.
3351 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3355 if (ms->position_lock_style() == AudioTime) {
3358 Glib::Threads::RWLock::WriterLock lm (lock);
3359 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3361 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3362 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3363 recompute_tempi (_metrics);
3368 Glib::Threads::RWLock::WriterLock lm (lock);
3369 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3371 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3372 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3374 if (solve_map_bbt (future_map, copy, bbt)) {
3375 solve_map_bbt (_metrics, ms, bbt);
3376 recompute_tempi (_metrics);
3381 Metrics::const_iterator d = future_map.begin();
3382 while (d != future_map.end()) {
3387 MetricPositionChanged (PropertyChange ()); // Emit Signal
3391 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm, bool change_end)
3394 bool can_solve = false;
3396 Glib::Threads::RWLock::WriterLock lm (lock);
3397 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3399 if (change_end && tempo_copy->type() == TempoSection::Constant) {
3400 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3401 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3402 } else if (change_end) {
3403 tempo_copy->set_end_note_types_per_minute (bpm.note_types_per_minute());
3405 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3408 recompute_tempi (future_map);
3410 if (check_solved (future_map)) {
3411 if (change_end && ts->type() == TempoSection::Constant) {
3412 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3413 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3414 } else if (change_end) {
3415 ts->set_end_note_types_per_minute (bpm.note_types_per_minute());
3417 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3420 recompute_map (_metrics);
3425 Metrics::const_iterator d = future_map.begin();
3426 while (d != future_map.end()) {
3431 MetricPositionChanged (PropertyChange ()); // Emit Signal
3438 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3441 Ts (future prev_t) Tnext
3444 |----------|----------
3451 Glib::Threads::RWLock::WriterLock lm (lock);
3457 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3463 /* minimum allowed measurement distance in frames */
3464 framepos_t const min_dframe = 2;
3468 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3470 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame())
3471 / (double) (end_frame - prev_t->frame()));
3473 new_bpm = prev_t->note_types_per_minute();
3476 std::cout << "new bpm : " << new_bpm << std::endl;
3477 new_bpm = min (new_bpm, (double) 1000.0);
3479 /* don't clamp and proceed here.
3480 testing has revealed that this can go negative,
3481 which is an entirely different thing to just being too low.
3484 if (new_bpm < 0.5) {
3488 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3489 prev_t->set_note_types_per_minute (new_bpm);
3491 prev_t->set_end_note_types_per_minute (new_bpm);
3492 prev_t->set_note_types_per_minute (new_bpm);
3495 recompute_tempi (future_map);
3496 recompute_meters (future_map);
3498 if (check_solved (future_map)) {
3499 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3500 ts->set_note_types_per_minute (new_bpm);
3502 ts->set_end_note_types_per_minute (new_bpm);
3503 ts->set_note_types_per_minute (new_bpm);
3505 recompute_tempi (_metrics);
3506 recompute_meters (_metrics);
3510 MetricPositionChanged (PropertyChange ()); // Emit Signal
3513 Metrics::const_iterator d = future_map.begin();
3514 while (d != future_map.end()) {
3521 TempoMap::gui_stretch_tempo_end (TempoSection* ts, const framepos_t frame, const framepos_t end_frame)
3524 Ts (future prev_t) Tnext
3527 |----------|----------
3534 Glib::Threads::RWLock::WriterLock lm (lock);
3540 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3543 TempoSection* next_t = 0;
3544 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3545 if ((*i)->is_tempo() && (*i)->minute() > prev_t->minute()) {
3546 next_t = static_cast<TempoSection*> (*i);
3560 /* minimum allowed measurement distance in frames */
3561 framepos_t const min_dframe = 2;
3564 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3565 new_bpm = prev_t->end_note_types_per_minute() * ((frame - prev_t->frame())
3566 / (double) (end_frame - prev_t->frame()));
3568 new_bpm = prev_t->end_note_types_per_minute();
3571 new_bpm = min (new_bpm, (double) 1000.0);
3573 if (new_bpm < 0.5) {
3577 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3578 prev_t->set_end_note_types_per_minute (new_bpm);
3581 prev_t->set_note_types_per_minute (new_bpm);
3582 prev_t->set_end_note_types_per_minute (prev_t->note_types_per_minute());
3586 recompute_tempi (future_map);
3587 recompute_meters (future_map);
3589 if (check_solved (future_map)) {
3590 if (prev_t && prev_t->type() == TempoSection::Ramp) {
3591 ts->set_end_note_types_per_minute (new_bpm);
3594 ts->set_end_note_types_per_minute (prev_t->note_types_per_minute());
3595 ts->set_note_types_per_minute (prev_t->note_types_per_minute());
3598 recompute_tempi (_metrics);
3599 recompute_meters (_metrics);
3603 MetricPositionChanged (PropertyChange ()); // Emit Signal
3606 Metrics::const_iterator d = future_map.begin();
3607 while (d != future_map.end()) {
3614 TempoMap::gui_twist_tempi (TempoSection* ts, const Tempo& bpm, const framepos_t frame, const framepos_t end_frame)
3616 TempoSection* next_t = 0;
3617 TempoSection* next_to_next_t = 0;
3619 bool can_solve = false;
3621 /* minimum allowed measurement distance in frames */
3622 framepos_t const min_dframe = 2;
3625 Glib::Threads::RWLock::WriterLock lm (lock);
3630 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3631 TempoSection* prev_to_prev_t = 0;
3632 const frameoffset_t fr_off = end_frame - frame;
3638 if (tempo_copy->pulse() > 0.0) {
3639 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (tempo_copy->frame() - 1)));
3642 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3643 if ((*i)->is_tempo() && (*i)->minute() > tempo_copy->minute()) {
3644 next_t = static_cast<TempoSection*> (*i);
3653 for (Metrics::const_iterator i = future_map.begin(); i != future_map.end(); ++i) {
3654 if ((*i)->is_tempo() && (*i)->minute() > next_t->minute()) {
3655 next_to_next_t = static_cast<TempoSection*> (*i);
3660 if (!next_to_next_t) {
3661 std::cout << "no next to next t" << std::endl;
3665 double prev_contribution = 0.0;
3667 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3668 prev_contribution = (tempo_copy->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3671 const frameoffset_t tempo_copy_frame_contribution = fr_off - (prev_contribution * (double) fr_off);
3674 framepos_t old_tc_pos = tempo_copy->frame();
3675 framepos_t old_next_pos = next_t->frame();
3676 double old_next_minute = next_t->minute();
3677 framepos_t old_next_to_next_pos = next_to_next_t->frame();
3678 double old_next_to_next_minute = next_to_next_t->minute();
3681 double new_next_bpm;
3682 double new_copy_end_bpm;
3684 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + tempo_copy_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3685 new_bpm = tempo_copy->note_types_per_minute() * ((frame - tempo_copy->frame())
3686 / (double) (end_frame - tempo_copy->frame()));
3688 new_bpm = tempo_copy->note_types_per_minute();
3691 /* don't clamp and proceed here.
3692 testing has revealed that this can go negative,
3693 which is an entirely different thing to just being too low.
3695 if (new_bpm < 0.5) {
3699 new_bpm = min (new_bpm, (double) 1000.0);
3701 tempo_copy->set_note_types_per_minute (new_bpm);
3702 if (tempo_copy->type() == TempoSection::Constant) {
3703 tempo_copy->set_end_note_types_per_minute (new_bpm);
3705 recompute_tempi (future_map);
3707 if (check_solved (future_map)) {
3712 ts->set_note_types_per_minute (new_bpm);
3713 if (ts->type() == TempoSection::Constant) {
3714 ts->set_end_note_types_per_minute (new_bpm);
3716 recompute_map (_metrics);
3720 if (next_t->type() == TempoSection::Constant || next_t->c() == 0.0) {
3721 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3723 new_next_bpm = next_t->note_types_per_minute() * ((next_to_next_t->minute() - old_next_minute)
3724 / (double) ((old_next_to_next_minute) - old_next_minute));
3727 new_next_bpm = next_t->note_types_per_minute();
3730 next_t->set_note_types_per_minute (new_next_bpm);
3731 recompute_tempi (future_map);
3733 if (check_solved (future_map)) {
3734 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3735 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3736 next_t = static_cast<TempoSection*> (*i);
3744 next_t->set_note_types_per_minute (new_next_bpm);
3745 recompute_map (_metrics);
3749 double next_frame_ratio = 1.0;
3750 double copy_frame_ratio = 1.0;
3752 if (next_to_next_t) {
3753 next_frame_ratio = (next_to_next_t->frame() - old_next_pos) / (double) (old_next_to_next_pos - old_next_pos);
3755 copy_frame_ratio = ((old_tc_pos - next_t->frame()) / (double) (old_tc_pos - old_next_pos));
3758 //next_frame_ratio = (((next_to_next_pos - fr_off) - next_t->frame()) / (double) ((next_to_next_pos) - next_t->frame()));
3759 //next_pulse_ratio = (start_pulse / end_pulse);
3762 new_next_bpm = next_t->note_types_per_minute() * next_frame_ratio;
3763 new_copy_end_bpm = tempo_copy->end_note_types_per_minute() * copy_frame_ratio;
3765 next_t->set_note_types_per_minute (new_next_bpm);
3766 tempo_copy->set_end_note_types_per_minute (new_copy_end_bpm);
3767 recompute_tempi (future_map);
3769 if (check_solved (future_map)) {
3770 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3771 if ((*i)->is_tempo() && (*i)->minute() > ts->minute()) {
3772 next_t = static_cast<TempoSection*> (*i);
3780 next_t->set_note_types_per_minute (new_next_bpm);
3781 ts->set_end_note_types_per_minute (new_copy_end_bpm);
3782 recompute_map (_metrics);
3788 Metrics::const_iterator d = future_map.begin();
3789 while (d != future_map.end()) {
3794 MetricPositionChanged (PropertyChange ()); // Emit Signal
3799 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3800 * the supplied frame, possibly returning a negative value.
3802 * @param frame The session frame position.
3803 * @param sub_num The subdivision to use when rounding the beat.
3804 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3805 * Positive integers indicate quarter note (non BBT) divisions.
3806 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3807 * @return The beat position of the supplied frame.
3809 * when working to a musical grid, the use of sub_nom indicates that
3810 * the position should be interpreted musically.
3812 * it effectively snaps to meter bars, meter beats or quarter note divisions
3813 * (as per current gui convention) and returns a musical position independent of frame rate.
3815 * If the supplied frame lies before the first meter, the return will be negative,
3816 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3817 * the continuation of the tempo curve (backwards).
3819 * This function is sensitive to tempo and meter.
3822 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3824 Glib::Threads::RWLock::ReaderLock lm (lock);
3826 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3830 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3832 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3835 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3836 * the supplied frame, possibly returning a negative value.
3838 * @param frame The session frame position.
3839 * @param sub_num The subdivision to use when rounding the quarter note.
3840 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3841 * Positive integers indicate quarter note (non BBT) divisions.
3842 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3843 * @return The quarter note position of the supplied frame.
3845 * When working to a musical grid, the use of sub_nom indicates that
3846 * the frame position should be interpreted musically.
3848 * it effectively snaps to meter bars, meter beats or quarter note divisions
3849 * (as per current gui convention) and returns a musical position independent of frame rate.
3851 * If the supplied frame lies before the first meter, the return will be negative,
3852 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3853 * the continuation of the tempo curve (backwards).
3855 * This function is tempo-sensitive.
3858 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3860 Glib::Threads::RWLock::ReaderLock lm (lock);
3862 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3866 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3868 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3871 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3872 } else if (sub_num == 1) {
3873 /* the gui requested exact musical (BBT) beat */
3874 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5))) * 4.0;
3875 } else if (sub_num == -1) {
3877 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3881 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3883 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3885 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3895 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3896 * @param pos the frame position in the tempo map.
3897 * @param bbt the distance in BBT time from pos to calculate.
3898 * @param dir the rounding direction..
3899 * @return the duration in frames between pos and bbt
3902 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3904 Glib::Threads::RWLock::ReaderLock lm (lock);
3906 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3908 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3911 pos_bbt.bars += bbt.bars;
3913 pos_bbt.ticks += bbt.ticks;
3914 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3916 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3919 pos_bbt.beats += bbt.beats;
3920 if ((double) pos_bbt.beats > divisions) {
3922 pos_bbt.beats -= divisions;
3924 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3926 return pos_bbt_frame - pos;
3930 if (pos_bbt.bars <= bbt.bars) {
3933 pos_bbt.bars -= bbt.bars;
3936 if (pos_bbt.ticks < bbt.ticks) {
3937 if (pos_bbt.bars > 1) {
3938 if (pos_bbt.beats == 1) {
3940 pos_bbt.beats = divisions;
3944 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3950 pos_bbt.ticks -= bbt.ticks;
3953 if (pos_bbt.beats <= bbt.beats) {
3954 if (pos_bbt.bars > 1) {
3956 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3961 pos_bbt.beats -= bbt.beats;
3964 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3971 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3973 return round_to_type (fr, dir, Bar);
3977 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3979 return round_to_type (fr, dir, Beat);
3983 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3985 Glib::Threads::RWLock::ReaderLock lm (lock);
3986 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);
3987 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3988 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3990 ticks -= beats * BBT_Time::ticks_per_beat;
3993 /* round to next (or same iff dir == RoundUpMaybe) */
3995 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3997 if (mod == 0 && dir == RoundUpMaybe) {
3998 /* right on the subdivision, which is fine, so do nothing */
4000 } else if (mod == 0) {
4001 /* right on the subdivision, so the difference is just the subdivision ticks */
4002 ticks += ticks_one_subdivisions_worth;
4005 /* not on subdivision, compute distance to next subdivision */
4007 ticks += ticks_one_subdivisions_worth - mod;
4010 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
4011 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
4012 // And since the "prev" direction DOES move beats, I assume this code is unintended.
4013 // But I'm keeping it around, until we determine there are no terrible consequences.
4014 // if (ticks >= BBT_Time::ticks_per_beat) {
4015 // ticks -= BBT_Time::ticks_per_beat;
4018 } else if (dir < 0) {
4020 /* round to previous (or same iff dir == RoundDownMaybe) */
4022 uint32_t difference = ticks % ticks_one_subdivisions_worth;
4024 if (difference == 0 && dir == RoundDownAlways) {
4025 /* right on the subdivision, but force-rounding down,
4026 so the difference is just the subdivision ticks */
4027 difference = ticks_one_subdivisions_worth;
4030 if (ticks < difference) {
4031 ticks = BBT_Time::ticks_per_beat - ticks;
4033 ticks -= difference;
4037 /* round to nearest */
4040 /* compute the distance to the previous and next subdivision */
4042 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
4044 /* closer to the next subdivision, so shift forward */
4046 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
4048 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
4050 if (ticks > BBT_Time::ticks_per_beat) {
4052 ticks -= BBT_Time::ticks_per_beat;
4053 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
4056 } else if (rem > 0) {
4058 /* closer to previous subdivision, so shift backward */
4062 /* can't go backwards past zero, so ... */
4063 return MusicFrame (0, 0);
4065 /* step back to previous beat */
4067 ticks = lrint (BBT_Time::ticks_per_beat - rem);
4068 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
4070 ticks = lrint (ticks - rem);
4071 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
4074 /* on the subdivision, do nothing */
4078 MusicFrame ret (0, 0);
4079 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
4080 ret.division = sub_num;
4086 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
4088 Glib::Threads::RWLock::ReaderLock lm (lock);
4089 const double minute = minute_at_frame (frame);
4090 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
4091 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
4092 MusicFrame ret (0, 0);
4099 /* find bar previous to 'frame' */
4105 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4109 } else if (dir > 0) {
4110 /* find bar following 'frame' */
4115 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4119 /* true rounding: find nearest bar */
4120 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4123 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4125 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4127 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
4128 ret.frame = next_ft;
4133 ret.frame = prev_ft;
4145 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
4148 } else if (dir > 0) {
4149 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
4153 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
4160 return MusicFrame (0, 0);
4164 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
4165 framepos_t lower, framepos_t upper, uint32_t bar_mod)
4167 Glib::Threads::RWLock::ReaderLock lm (lock);
4168 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
4170 /* although the map handles negative beats, bbt doesn't. */
4175 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
4179 while (pos >= 0 && pos < upper) {
4180 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
4181 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4182 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4183 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
4185 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4189 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
4194 bbt.bars -= bbt.bars % bar_mod;
4198 while (pos >= 0 && pos < upper) {
4199 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
4200 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
4201 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
4202 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
4203 bbt.bars += bar_mod;
4209 TempoMap::tempo_section_at_frame (framepos_t frame) const
4211 Glib::Threads::RWLock::ReaderLock lm (lock);
4213 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4217 TempoMap::tempo_section_at_frame (framepos_t frame)
4219 Glib::Threads::RWLock::ReaderLock lm (lock);
4221 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4225 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4227 TempoSection* prev = 0;
4231 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4233 if ((*i)->is_tempo()) {
4234 t = static_cast<TempoSection*> (*i);
4238 if (prev && t->minute() > minute) {
4248 abort(); /*NOTREACHED*/
4254 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4256 TempoSection* prev = 0;
4260 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4262 if ((*i)->is_tempo()) {
4263 t = static_cast<TempoSection*> (*i);
4267 if (prev && t->minute() > minute) {
4277 abort(); /*NOTREACHED*/
4283 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4285 TempoSection* prev_t = 0;
4286 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4290 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4291 if ((*i)->is_tempo()) {
4292 t = static_cast<TempoSection*> (*i);
4298 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4308 /* don't use this to calculate length (the tempo is only correct for this frame).
4309 do that stuff based on the beat_at_frame and frame_at_beat api
4312 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4314 Glib::Threads::RWLock::ReaderLock lm (lock);
4316 const TempoSection* ts_at = 0;
4317 const TempoSection* ts_after = 0;
4318 Metrics::const_iterator i;
4321 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4323 if ((*i)->is_tempo()) {
4324 t = static_cast<TempoSection*> (*i);
4328 if (ts_at && (*i)->frame() > frame) {
4338 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4340 /* must be treated as constant tempo */
4341 return ts_at->frames_per_quarter_note (_frame_rate);
4345 TempoMap::meter_section_at_frame (framepos_t frame) const
4347 Glib::Threads::RWLock::ReaderLock lm (lock);
4348 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4352 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4354 Metrics::const_iterator i;
4355 MeterSection* prev = 0;
4359 for (i = metrics.begin(); i != metrics.end(); ++i) {
4361 if (!(*i)->is_tempo()) {
4362 m = static_cast<MeterSection*> (*i);
4364 if (prev && (*i)->minute() > minute) {
4374 abort(); /*NOTREACHED*/
4381 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4383 MeterSection* prev_m = 0;
4385 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4387 if (!(*i)->is_tempo()) {
4388 m = static_cast<MeterSection*> (*i);
4389 if (prev_m && m->beat() > beat) {
4400 TempoMap::meter_section_at_beat (double beat) const
4402 Glib::Threads::RWLock::ReaderLock lm (lock);
4403 return meter_section_at_beat_locked (_metrics, beat);
4407 TempoMap::meter_at_frame (framepos_t frame) const
4409 TempoMetric m (metric_at (frame));
4414 TempoMap::fix_legacy_session ()
4416 MeterSection* prev_m = 0;
4417 TempoSection* prev_t = 0;
4418 bool have_initial_t = false;
4420 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4424 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4426 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4429 m->set_minute (0.0);
4430 m->set_position_lock_style (AudioTime);
4435 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4436 + (m->bbt().beats - 1)
4437 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4439 m->set_beat (start);
4440 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4441 + (m->bbt().beats - 1)
4442 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4443 m->set_pulse (start_beat / prev_m->note_divisor());
4446 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4454 t->set_minute (0.0);
4455 t->set_position_lock_style (AudioTime);
4457 have_initial_t = true;
4462 /* some 4.x sessions have no initial (non-movable) tempo. */
4463 if (!have_initial_t) {
4464 prev_t->set_pulse (0.0);
4465 prev_t->set_minute (0.0);
4466 prev_t->set_position_lock_style (AudioTime);
4467 prev_t->set_initial (true);
4468 prev_t->set_locked_to_meter (true);
4469 have_initial_t = true;
4472 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4473 + (t->legacy_bbt().beats - 1)
4474 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4476 t->set_pulse (beat / prev_m->note_divisor());
4478 /* really shouldn't happen but.. */
4479 t->set_pulse (beat / 4.0);
4487 TempoMap::fix_legacy_end_session ()
4489 TempoSection* prev_t = 0;
4491 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4494 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4501 if (prev_t->type() == TempoSection::Ramp) {
4502 prev_t->set_end_note_types_per_minute (t->note_types_per_minute());
4504 prev_t->set_end_note_types_per_minute (prev_t->note_types_per_minute());
4514 TempoMap::get_state ()
4516 Metrics::const_iterator i;
4517 XMLNode *root = new XMLNode ("TempoMap");
4520 Glib::Threads::RWLock::ReaderLock lm (lock);
4521 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4522 root->add_child_nocopy ((*i)->get_state());
4530 TempoMap::set_state (const XMLNode& node, int /*version*/)
4533 Glib::Threads::RWLock::WriterLock lm (lock);
4536 XMLNodeConstIterator niter;
4537 Metrics old_metrics (_metrics);
4540 nlist = node.children();
4542 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4543 XMLNode* child = *niter;
4545 if (child->name() == TempoSection::xml_state_node_name) {
4548 TempoSection* ts = new TempoSection (*child, _frame_rate);
4549 _metrics.push_back (ts);
4552 catch (failed_constructor& err){
4553 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4554 _metrics = old_metrics;
4555 old_metrics.clear();
4559 } else if (child->name() == MeterSection::xml_state_node_name) {
4562 MeterSection* ms = new MeterSection (*child, _frame_rate);
4563 _metrics.push_back (ms);
4566 catch (failed_constructor& err) {
4567 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4568 _metrics = old_metrics;
4569 old_metrics.clear();
4575 if (niter == nlist.end()) {
4576 MetricSectionSorter cmp;
4577 _metrics.sort (cmp);
4580 /* check for legacy sessions where bbt was the base musical unit for tempo */
4581 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4583 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4584 if (t->legacy_bbt().bars != 0) {
4585 fix_legacy_session();
4589 if (t->legacy_end()) {
4590 fix_legacy_end_session();
4598 /* check for multiple tempo/meters at the same location, which
4599 ardour2 somehow allowed.
4602 Metrics::iterator prev = _metrics.end();
4603 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4604 if (prev != _metrics.end()) {
4606 MeterSection* prev_m;
4608 TempoSection* prev_t;
4609 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4610 if (prev_m->pulse() == ms->pulse()) {
4611 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4612 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4615 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4616 if (prev_t->pulse() == ts->pulse()) {
4617 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4618 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4626 recompute_map (_metrics);
4628 Metrics::const_iterator d = old_metrics.begin();
4629 while (d != old_metrics.end()) {
4633 old_metrics.clear ();
4636 PropertyChanged (PropertyChange ());
4642 TempoMap::dump (std::ostream& o) const
4644 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4645 const MeterSection* m;
4646 const TempoSection* t;
4647 const TempoSection* prev_t = 0;
4649 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4651 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4652 o << "Tempo @ " << *i << " start : " << t->note_types_per_minute() << " end : " << t->end_note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4653 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4654 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4655 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4657 o << " current start : " << t->note_types_per_minute()
4658 << " current end : " << t->end_note_types_per_minute()
4659 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4660 o << " previous : " << prev_t->note_types_per_minute()
4661 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4662 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4663 << " | " << prev_t->pulse_at_ntpm (prev_t->end_note_types_per_minute(), t->minute())
4664 << " | " << frame_at_minute (prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()))
4665 << " | " << prev_t->minute_at_ntpm (prev_t->end_note_types_per_minute(), t->pulse()) << std::endl;
4668 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4669 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4670 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4671 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4674 o << "------" << std::endl;
4678 TempoMap::n_tempos() const
4680 Glib::Threads::RWLock::ReaderLock lm (lock);
4683 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4684 if ((*i)->is_tempo()) {
4693 TempoMap::n_meters() const
4695 Glib::Threads::RWLock::ReaderLock lm (lock);
4698 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4699 if (!(*i)->is_tempo()) {
4708 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4710 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4711 if ((*i)->frame() >= where && !(*i)->initial ()) {
4715 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4716 gui_set_meter_position (ms, (*i)->frame() + amount);
4719 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4720 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4725 PropertyChanged (PropertyChange ());
4729 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4733 std::list<MetricSection*> metric_kill_list;
4735 TempoSection* last_tempo = NULL;
4736 MeterSection* last_meter = NULL;
4737 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4738 bool meter_after = false; // is there a meter marker likewise?
4740 Glib::Threads::RWLock::WriterLock lm (lock);
4741 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4742 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4743 metric_kill_list.push_back(*i);
4744 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4747 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4751 else if ((*i)->frame() >= where) {
4752 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4753 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4754 if ((*i)->frame() == where) {
4755 // marker was immediately after end of range
4756 tempo_after = dynamic_cast<TempoSection*> (*i);
4757 meter_after = dynamic_cast<MeterSection*> (*i);
4763 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4764 if (last_tempo && !tempo_after) {
4765 metric_kill_list.remove(last_tempo);
4766 last_tempo->set_minute (minute_at_frame (where));
4769 if (last_meter && !meter_after) {
4770 metric_kill_list.remove(last_meter);
4771 last_meter->set_minute (minute_at_frame (where));
4775 //remove all the remaining metrics
4776 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4777 _metrics.remove(*i);
4782 recompute_map (_metrics);
4785 PropertyChanged (PropertyChange ());
4789 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4790 * pos can be -ve, if required.
4793 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4795 Glib::Threads::RWLock::ReaderLock lm (lock);
4796 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4798 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4802 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4804 Glib::Threads::RWLock::ReaderLock lm (lock);
4806 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4807 pos_bbt.ticks += op.ticks;
4808 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4810 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4812 pos_bbt.beats += op.beats;
4813 /* the meter in effect will start on the bar */
4814 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();
4815 while (pos_bbt.beats >= divisions_per_bar + 1) {
4817 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4818 pos_bbt.beats -= divisions_per_bar;
4820 pos_bbt.bars += op.bars;
4822 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4825 /** Count the number of beats that are equivalent to distance when going forward,
4829 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4831 Glib::Threads::RWLock::ReaderLock lm (lock);
4833 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4837 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4843 operator<< (std::ostream& o, const Meter& m) {
4844 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4848 operator<< (std::ostream& o, const Tempo& t) {
4849 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4853 operator<< (std::ostream& o, const MetricSection& section) {
4855 o << "MetricSection @ " << section.frame() << ' ';
4857 const TempoSection* ts;
4858 const MeterSection* ms;
4860 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4861 o << *((const Tempo*) ts);
4862 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4863 o << *((const Meter*) ms);