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);
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)
94 XMLProperty const * prop;
100 _legacy_bbt = BBT_Time (0, 0, 0);
102 if ((prop = node.property ("start")) != 0) {
103 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
107 /* legacy session - start used to be in bbt*/
110 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
114 if ((prop = node.property ("pulse")) != 0) {
115 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
116 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
122 if ((prop = node.property ("frame")) != 0) {
123 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
124 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
125 throw failed_constructor();
127 set_minute (minute_at_frame (frame));
131 /* XX replace old beats-per-minute name with note-types-per-minute */
132 if ((prop = node.property ("beats-per-minute")) != 0) {
133 if (sscanf (prop->value().c_str(), "%lf", &_note_types_per_minute) != 1 || _note_types_per_minute < 0.0) {
134 error << _("TempoSection XML node has an illegal \"beats-per-minute\" value") << endmsg;
135 throw failed_constructor();
139 if ((prop = node.property ("note-type")) == 0) {
140 /* older session, make note type be quarter by default */
143 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
144 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
145 throw failed_constructor();
149 if ((prop = node.property ("movable")) == 0) {
150 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
151 throw failed_constructor();
154 set_initial (!string_is_affirmative (prop->value()));
156 if ((prop = node.property ("active")) == 0) {
157 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
160 set_active (string_is_affirmative (prop->value()));
163 if ((prop = node.property ("tempo-type")) == 0) {
166 _type = Type (string_2_enum (prop->value(), _type));
169 if ((prop = node.property ("lock-style")) == 0) {
171 set_position_lock_style (MusicTime);
173 set_position_lock_style (AudioTime);
176 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
179 if ((prop = node.property ("locked-to-meter")) == 0) {
180 set_locked_to_meter (false);
182 set_locked_to_meter (string_is_affirmative (prop->value()));
187 TempoSection::get_state() const
189 XMLNode *root = new XMLNode (xml_state_node_name);
193 snprintf (buf, sizeof (buf), "%lf", pulse());
194 root->add_property ("pulse", buf);
195 snprintf (buf, sizeof (buf), "%li", frame());
196 root->add_property ("frame", buf);
197 snprintf (buf, sizeof (buf), "%lf", _note_types_per_minute);
198 root->add_property ("beats-per-minute", buf);
199 snprintf (buf, sizeof (buf), "%lf", _note_type);
200 root->add_property ("note-type", buf);
201 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
202 root->add_property ("movable", buf);
203 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
204 root->add_property ("active", buf);
205 root->add_property ("tempo-type", enum_2_string (_type));
206 root->add_property ("lock-style", enum_2_string (position_lock_style()));
207 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
213 TempoSection::set_type (Type type)
218 /** returns the Tempo at the session-relative minute.
221 TempoSection::tempo_at_minute (const double& m) const
223 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && m < minute());
225 return Tempo (note_types_per_minute(), note_type());
228 return Tempo (_tempo_at_time (m - minute()), _note_type);
231 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
232 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
233 * @param p the pulse used to calculate the returned minute for constant tempi
234 * @return the minute at the supplied tempo
236 * note that the note_type is currently ignored in this function. see below.
240 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
241 * there should be no ramp between the two even if we are ramped.
242 * in other words a ramp should only place a curve on note_types_per_minute.
243 * we should be able to use Tempo note type here, but the above
244 * complicates things a bit.
247 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
249 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && p < pulse());
251 return ((p - pulse()) / pulses_per_minute()) + minute();
254 return _time_at_tempo (ntpm) + minute();
257 /** returns the Tempo at the supplied whole-note pulse.
260 TempoSection::tempo_at_pulse (const double& p) const
262 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && p < pulse());
265 return Tempo (note_types_per_minute(), note_type());
268 return Tempo (_tempo_at_pulse (p - pulse()), _note_type);
271 /** returns the whole-note pulse where a tempo in note types per minute occurs.
272 * constant tempi require minute m.
273 * @param ntpm the note types per minute value used to calculate the returned pulse
274 * @param m the minute used to calculate the returned pulse if the tempo is constant
275 * @return the whole-note pulse at the supplied tempo
277 * note that note_type is currently ignored in this function. see minute_at_tempo().
279 * for constant tempi, this is anaologous to pulse_at_minute().
282 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
284 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && m < minute());
286 return ((m - minute()) * pulses_per_minute()) + pulse();
289 return _pulse_at_tempo (ntpm) + pulse();
292 /** returns the whole-note pulse at the supplied session-relative minute.
295 TempoSection::pulse_at_minute (const double& m) const
297 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && m < minute());
299 return ((m - minute()) * pulses_per_minute()) + pulse();
302 return _pulse_at_time (m - minute()) + pulse();
305 /** returns the session-relative minute at the supplied whole-note pulse.
308 TempoSection::minute_at_pulse (const double& p) const
310 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && p < pulse());
312 return ((p - pulse()) / pulses_per_minute()) + minute();
315 return _time_at_pulse (p - pulse()) + minute();
318 /** returns thw whole-note pulse at session frame position f.
319 * @param f the frame position.
320 * @return the position in whole-note pulses corresponding to f
322 * for use with musical units whose granularity is coarser than frames (e.g. ticks)
325 TempoSection::pulse_at_frame (const framepos_t& f) const
327 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && f < frame());
329 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
332 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
336 TempoSection::frame_at_pulse (const double& p) const
338 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && p < pulse());
340 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
343 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
351 Tt----|-----------------*|
352 Ta----|--------------|* |
358 _______________|___|____
359 time a t (next tempo)
362 Duration in beats at time a is the integral of some Tempo function.
363 In our case, the Tempo function (Tempo at time t) is
366 with function constant
371 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
372 b(t) = T0(e^(ct) - 1) / c
374 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:
375 t(b) = log((c.b / T0) + 1) / c
377 The time t at which Tempo T occurs is a as above:
378 t(T) = log(T / T0) / c
380 The beat at which a Tempo T occurs is:
383 The Tempo at which beat b occurs is:
386 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
387 Our problem is that we usually don't know t.
388 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.
389 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
390 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
392 By substituting our expanded t as a in the c function above, our problem is reduced to:
393 c = T0 (e^(log (Ta / T0)) - 1) / b
395 Of course the word 'beat' has been left loosely defined above.
396 In music, a beat is defined by the musical pulse (which comes from the tempo)
397 and the meter in use at a particular time (how many pulse divisions there are in one bar).
398 It would be more accurate to substitute the work 'pulse' for 'beat' above.
402 We can now store c for future time calculations.
403 If the following tempo section (the one that defines c in conjunction with this one)
404 is changed or moved, c is no longer valid.
406 The public methods are session-relative.
408 Most of this stuff is taken from this paper:
411 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
414 Zurich University of Arts
415 Institute for Computer Music and Sound Technology
417 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
421 /** compute this ramp's function constant from some tempo-pulse point
422 * @param end_npm end tempo (in note types per minute)
423 * @param end_pulse duration (pulses into global start) of some other position.
424 * @return the calculated function constant
427 TempoSection::compute_c_func_pulse (const double& end_npm, const double& end_pulse) const
429 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
430 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
433 /** compute the function constant from some tempo-time point.
434 * @param end_npm tempo (note types/min.)
435 * @param end_minute distance (in minutes) from session origin
436 * @return the calculated function constant
439 TempoSection::compute_c_func_minute (const double& end_npm, const double& end_minute) const
441 return c_func (end_npm, end_minute - minute());
444 /* position function */
446 TempoSection::a_func (double end_npm, double c_func) const
448 return log (end_npm / note_types_per_minute()) / c_func;
451 /*function constant*/
453 TempoSection::c_func (double end_npm, double end_time) const
455 return log (end_npm / note_types_per_minute()) / end_time;
458 /* tempo in note types per minute at time in minutes */
460 TempoSection::_tempo_at_time (const double& time) const
462 return exp (_c_func * time) * note_types_per_minute();
465 /* time in minutes at tempo in note types per minute */
467 TempoSection::_time_at_tempo (const double& npm) const
469 return log (npm / note_types_per_minute()) / _c_func;
472 /* pulse at tempo in note types per minute */
474 TempoSection::_pulse_at_tempo (const double& npm) const
476 return ((npm - note_types_per_minute()) / _c_func) / _note_type;
479 /* tempo in note types per minute at pulse */
481 TempoSection::_tempo_at_pulse (const double& pulse) const
483 return (pulse * _note_type * _c_func) + note_types_per_minute();
486 /* pulse at time in minutes */
488 TempoSection::_pulse_at_time (const double& time) const
490 return (expm1 (_c_func * time) * (note_types_per_minute() / _c_func)) / _note_type;
493 /* time in minutes at pulse */
495 TempoSection::_time_at_pulse (const double& pulse) const
497 return log1p ((_c_func * pulse * _note_type) / note_types_per_minute()) / _c_func;
500 /***********************************************************************/
502 const string MeterSection::xml_state_node_name = "Meter";
504 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
505 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
507 XMLProperty const * prop;
512 framepos_t frame = 0;
513 pair<double, BBT_Time> start;
515 if ((prop = node.property ("start")) != 0) {
516 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
520 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
522 /* legacy session - start used to be in bbt*/
523 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
528 if ((prop = node.property ("pulse")) != 0) {
529 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
530 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
535 if ((prop = node.property ("beat")) != 0) {
536 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
537 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
543 if ((prop = node.property ("bbt")) == 0) {
544 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
545 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
549 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
550 throw failed_constructor();
556 if ((prop = node.property ("frame")) != 0) {
557 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
558 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
559 throw failed_constructor();
561 set_minute (minute_at_frame (frame));
565 /* beats-per-bar is old; divisions-per-bar is new */
567 if ((prop = node.property ("divisions-per-bar")) == 0) {
568 if ((prop = node.property ("beats-per-bar")) == 0) {
569 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
570 throw failed_constructor();
573 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
574 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
575 throw failed_constructor();
578 if ((prop = node.property ("note-type")) == 0) {
579 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
580 throw failed_constructor();
582 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
583 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
584 throw failed_constructor();
587 if ((prop = node.property ("movable")) == 0) {
588 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
589 throw failed_constructor();
592 set_initial (!string_is_affirmative (prop->value()));
594 if ((prop = node.property ("lock-style")) == 0) {
595 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
597 set_position_lock_style (MusicTime);
599 set_position_lock_style (AudioTime);
602 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
607 MeterSection::get_state() const
609 XMLNode *root = new XMLNode (xml_state_node_name);
613 snprintf (buf, sizeof (buf), "%lf", pulse());
614 root->add_property ("pulse", buf);
615 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
619 root->add_property ("bbt", buf);
620 snprintf (buf, sizeof (buf), "%lf", beat());
621 root->add_property ("beat", buf);
622 snprintf (buf, sizeof (buf), "%lf", _note_type);
623 root->add_property ("note-type", buf);
624 snprintf (buf, sizeof (buf), "%li", frame());
625 root->add_property ("frame", buf);
626 root->add_property ("lock-style", enum_2_string (position_lock_style()));
627 snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
628 root->add_property ("divisions-per-bar", buf);
629 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
630 root->add_property ("movable", buf);
635 /***********************************************************************/
639 Tempo determines the rate of musical pulse determined by its components
640 note types per minute - the rate per minute of the whole note divisor _note_type
641 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
642 Meter divides the musical pulse into measures and beats according to its components
646 TempoSection - translates between time, musical pulse and tempo.
647 has a musical location in whole notes (pulses).
648 has a time location in minutes.
649 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
650 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
652 MeterSection - translates between BBT, meter-based beat and musical pulse.
653 has a musical location in whole notes (pulses)
654 has a musical location in meter-based beats
655 has a musical location in BBT time
656 has a time location expressed in minutes.
658 TempoSection and MeterSection may be locked to either audio or music (position lock style).
659 The lock style determines the location type to be kept as a reference when location is recalculated.
661 The first tempo and meter are special. they must move together, and are locked to audio.
662 Audio locked tempi which lie before the first meter are made inactive.
664 Recomputing the map is the process where the 'missing' location types are calculated.
665 We construct the tempo map by first using the locked location type of each section
666 to determine non-locked location types (pulse or minute position).
667 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
669 Having done this, we can now traverse the Metrics list by pulse or minute
670 to query its relevant meter/tempo.
672 It is important to keep the _metrics in an order that makes sense.
673 Because ramped MusicTime and AudioTime tempos can interact with each other,
674 reordering is frequent. Care must be taken to keep _metrics in a solved state.
675 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
679 Music and audio-locked objects may seem interchangeable on the surface, but when translating
680 between audio samples and beat, remember that a sample is only a quantised approximation
681 of the actual time (in minutes) of a beat.
682 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
683 mean that this frame is the actual location in time of 1|3|0.
685 You cannot use a frame measurement to determine beat distance except under special circumstances
686 (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).
688 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
689 sample space the user is operating at to be translated correctly to the object.
691 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
692 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
693 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
695 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
697 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
698 The result is rounded to audio frames.
699 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
702 frame_at_beat (beat_at_frame (frame)) == frame
704 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
706 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
707 frames_between_quarter_notes () eliminats this effect when determining time duration
708 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
710 The above pointless example could instead do:
711 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
713 The Shaggs - Things I Wonder
714 https://www.youtube.com/watch?v=9wQK6zMJOoQ
717 struct MetricSectionSorter {
718 bool operator() (const MetricSection* a, const MetricSection* b) {
719 return a->pulse() < b->pulse();
723 struct MetricSectionFrameSorter {
724 bool operator() (const MetricSection* a, const MetricSection* b) {
725 return a->frame() < b->frame();
729 TempoMap::TempoMap (framecnt_t fr)
732 BBT_Time start (1, 1, 0);
734 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo.note_types_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime, fr);
735 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
737 t->set_initial (true);
738 m->set_initial (true);
740 /* note: frame time is correct (zero) for both of these */
742 _metrics.push_back (t);
743 _metrics.push_back (m);
747 TempoMap::TempoMap (TempoMap const & other)
749 _frame_rate = other._frame_rate;
750 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
751 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
752 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
755 TempoSection* new_section = new TempoSection (*ts);
756 _metrics.push_back (new_section);
758 MeterSection* new_section = new MeterSection (*ms);
759 _metrics.push_back (new_section);
765 TempoMap::operator= (TempoMap const & other)
767 if (&other != this) {
768 _frame_rate = other._frame_rate;
770 Metrics::const_iterator d = _metrics.begin();
771 while (d != _metrics.end()) {
777 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
778 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
779 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
782 TempoSection* new_section = new TempoSection (*ts);
783 _metrics.push_back (new_section);
785 MeterSection* new_section = new MeterSection (*ms);
786 _metrics.push_back (new_section);
791 PropertyChanged (PropertyChange());
796 TempoMap::~TempoMap ()
798 Metrics::const_iterator d = _metrics.begin();
799 while (d != _metrics.end()) {
807 TempoMap::frame_at_minute (const double time) const
809 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
813 TempoMap::minute_at_frame (const framepos_t frame) const
815 return (frame / (double) _frame_rate) / 60.0;
819 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
821 bool removed = false;
824 Glib::Threads::RWLock::WriterLock lm (lock);
825 if ((removed = remove_tempo_locked (tempo))) {
826 if (complete_operation) {
827 recompute_map (_metrics);
832 if (removed && complete_operation) {
833 PropertyChanged (PropertyChange ());
838 TempoMap::remove_tempo_locked (const TempoSection& tempo)
842 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
843 if (dynamic_cast<TempoSection*> (*i) != 0) {
844 if (tempo.frame() == (*i)->frame()) {
845 if (!(*i)->initial()) {
858 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
860 bool removed = false;
863 Glib::Threads::RWLock::WriterLock lm (lock);
864 if ((removed = remove_meter_locked (tempo))) {
865 if (complete_operation) {
866 recompute_map (_metrics);
871 if (removed && complete_operation) {
872 PropertyChanged (PropertyChange ());
877 TempoMap::remove_meter_locked (const MeterSection& meter)
880 if (meter.position_lock_style() == AudioTime) {
881 /* remove meter-locked tempo */
882 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
884 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
885 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
894 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
895 if (dynamic_cast<MeterSection*> (*i) != 0) {
896 if (meter.frame() == (*i)->frame()) {
897 if (!(*i)->initial()) {
910 TempoMap::do_insert (MetricSection* section)
912 bool need_add = true;
913 /* we only allow new meters to be inserted on beat 1 of an existing
917 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
919 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
921 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
922 corrected.second.beats = 1;
923 corrected.second.ticks = 0;
924 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
925 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
926 m->bbt(), corrected.second) << endmsg;
927 //m->set_pulse (corrected);
931 /* Look for any existing MetricSection that is of the same type and
932 in the same bar as the new one, and remove it before adding
933 the new one. Note that this means that if we find a matching,
934 existing section, we can break out of the loop since we're
935 guaranteed that there is only one such match.
938 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
940 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
941 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
942 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
943 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
945 if (tempo && insert_tempo) {
948 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
949 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
951 if (tempo->initial()) {
953 /* can't (re)move this section, so overwrite
954 * its data content (but not its properties as
958 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
959 (*i)->set_position_lock_style (AudioTime);
961 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
962 t->set_type (insert_tempo->type());
972 } else if (meter && insert_meter) {
976 bool const ipm = insert_meter->position_lock_style() == MusicTime;
978 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
980 if (meter->initial()) {
982 /* can't (re)move this section, so overwrite
983 * its data content (but not its properties as
987 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
988 (*i)->set_position_lock_style (AudioTime);
998 /* non-matching types, so we don't care */
1002 /* Add the given MetricSection, if we didn't just reset an existing
1007 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
1008 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
1009 Metrics::iterator i;
1011 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1012 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
1015 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1016 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
1021 } else if (insert_tempo) {
1022 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1023 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1026 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1027 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
1034 _metrics.insert (i, section);
1038 /* user supplies the exact pulse if pls == MusicTime */
1040 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
1042 TempoSection* ts = 0;
1044 Glib::Threads::RWLock::WriterLock lm (lock);
1045 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
1049 PropertyChanged (PropertyChange ());
1055 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
1057 const bool locked_to_meter = ts.locked_to_meter();
1060 Glib::Threads::RWLock::WriterLock lm (lock);
1061 TempoSection& first (first_tempo());
1062 if (ts.frame() != first.frame()) {
1063 remove_tempo_locked (ts);
1064 add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter);
1066 first.set_type (type);
1067 first.set_pulse (0.0);
1068 first.set_minute (minute_at_frame (frame));
1069 first.set_position_lock_style (AudioTime);
1071 /* cannot move the first tempo section */
1072 *static_cast<Tempo*>(&first) = tempo;
1073 recompute_map (_metrics);
1078 PropertyChanged (PropertyChange ());
1082 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1083 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1085 TempoSection* t = new TempoSection (pulse, minute, tempo.note_types_per_minute(), tempo.note_type(), type, pls, _frame_rate);
1086 t->set_locked_to_meter (locked_to_meter);
1087 bool solved = false;
1092 if (pls == AudioTime) {
1093 solved = solve_map_minute (_metrics, t, t->minute());
1095 solved = solve_map_pulse (_metrics, t, t->pulse());
1097 recompute_meters (_metrics);
1100 if (!solved && recompute) {
1101 recompute_map (_metrics);
1108 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, PositionLockStyle pls)
1110 MeterSection* m = 0;
1112 Glib::Threads::RWLock::WriterLock lm (lock);
1113 m = add_meter_locked (meter, beat, where, pls, true);
1118 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1123 PropertyChanged (PropertyChange ());
1128 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, PositionLockStyle pls)
1131 Glib::Threads::RWLock::WriterLock lm (lock);
1132 const double beat = beat_at_bbt_locked (_metrics, where);
1134 if (!ms.initial()) {
1135 remove_meter_locked (ms);
1136 add_meter_locked (meter, beat, where, pls, true);
1138 MeterSection& first (first_meter());
1139 TempoSection& first_t (first_tempo());
1140 /* cannot move the first meter section */
1141 *static_cast<Meter*>(&first) = meter;
1142 first.set_position_lock_style (AudioTime);
1143 first.set_pulse (0.0);
1144 //first.set_minute (minute_at_frame (frame));
1145 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1146 first.set_beat (beat);
1147 first_t.set_minute (first.minute());
1148 first_t.set_pulse (0.0);
1149 first_t.set_position_lock_style (AudioTime);
1150 recompute_map (_metrics);
1154 PropertyChanged (PropertyChange ());
1158 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, PositionLockStyle pls, bool recompute)
1160 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1161 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1162 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1163 TempoSection* mlt = 0;
1165 if (pls == AudioTime) {
1166 /* add meter-locked tempo */
1167 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, time_minutes, TempoSection::Ramp, AudioTime, true, true);
1175 MeterSection* new_meter = new MeterSection (pulse, time_minutes, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1176 bool solved = false;
1178 do_insert (new_meter);
1182 if (pls == AudioTime) {
1183 solved = solve_map_minute (_metrics, new_meter, time_minutes);
1185 solved = solve_map_bbt (_metrics, new_meter, where);
1186 /* required due to resetting the pulse of meter-locked tempi above.
1187 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1188 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1190 recompute_map (_metrics);
1194 if (!solved && recompute) {
1195 /* if this has failed to solve, there is little we can do other than to ensure that
1196 the new map is recalculated.
1198 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1199 recompute_map (_metrics);
1206 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type)
1208 Tempo newtempo (note_types_per_minute, note_type);
1211 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1212 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1217 Glib::Threads::RWLock::WriterLock lm (lock);
1218 *((Tempo*) t) = newtempo;
1219 recompute_map (_metrics);
1221 PropertyChanged (PropertyChange ());
1228 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type)
1230 Tempo newtempo (note_types_per_minute, note_type);
1233 TempoSection* first;
1234 Metrics::iterator i;
1236 /* find the TempoSection immediately preceding "where"
1239 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1241 if ((*i)->frame() > where) {
1247 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1260 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1270 Glib::Threads::RWLock::WriterLock lm (lock);
1271 /* cannot move the first tempo section */
1272 *((Tempo*)prev) = newtempo;
1273 recompute_map (_metrics);
1276 PropertyChanged (PropertyChange ());
1280 TempoMap::first_meter () const
1282 const MeterSection *m = 0;
1284 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1285 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1290 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1291 abort(); /*NOTREACHED*/
1296 TempoMap::first_meter ()
1298 MeterSection *m = 0;
1300 /* CALLER MUST HOLD LOCK */
1302 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1303 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1308 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1309 abort(); /*NOTREACHED*/
1314 TempoMap::first_tempo () const
1316 const TempoSection *t = 0;
1318 /* CALLER MUST HOLD LOCK */
1320 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1321 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1331 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1332 abort(); /*NOTREACHED*/
1337 TempoMap::first_tempo ()
1339 TempoSection *t = 0;
1341 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1342 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1352 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1353 abort(); /*NOTREACHED*/
1357 TempoMap::recompute_tempi (Metrics& metrics)
1359 TempoSection* prev_t = 0;
1361 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1364 if ((*i)->is_tempo()) {
1365 t = static_cast<TempoSection*> (*i);
1377 if (t->position_lock_style() == AudioTime) {
1378 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
1379 if (!t->locked_to_meter()) {
1380 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
1384 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
1385 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
1393 prev_t->set_c_func (0.0);
1396 /* tempos must be positioned correctly.
1397 the current approach is to use a meter's bbt time as its base position unit.
1398 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1399 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1402 TempoMap::recompute_meters (Metrics& metrics)
1404 MeterSection* meter = 0;
1405 MeterSection* prev_m = 0;
1407 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1408 if (!(*mi)->is_tempo()) {
1409 meter = static_cast<MeterSection*> (*mi);
1410 if (meter->position_lock_style() == AudioTime) {
1412 pair<double, BBT_Time> b_bbt;
1413 TempoSection* meter_locked_tempo = 0;
1414 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1416 if ((*ii)->is_tempo()) {
1417 t = static_cast<TempoSection*> (*ii);
1418 if ((t->locked_to_meter() || t->initial()) && t->frame() == meter->frame()) {
1419 meter_locked_tempo = t;
1426 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1427 if (beats + prev_m->beat() != meter->beat()) {
1428 /* reordering caused a bbt change */
1430 beats = meter->beat() - prev_m->beat();
1431 b_bbt = make_pair (beats + prev_m->beat()
1432 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1433 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1435 } else if (!meter->initial()) {
1436 b_bbt = make_pair (meter->beat(), meter->bbt());
1437 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1440 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1442 if (meter_locked_tempo) {
1443 meter_locked_tempo->set_pulse (pulse);
1445 meter->set_beat (b_bbt);
1446 meter->set_pulse (pulse);
1451 pair<double, BBT_Time> b_bbt;
1453 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1454 if (beats + prev_m->beat() != meter->beat()) {
1455 /* reordering caused a bbt change */
1456 b_bbt = make_pair (beats + prev_m->beat()
1457 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1459 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1461 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1463 /* shouldn't happen - the first is audio-locked */
1464 pulse = pulse_at_beat_locked (metrics, meter->beat());
1465 b_bbt = make_pair (meter->beat(), meter->bbt());
1468 meter->set_beat (b_bbt);
1469 meter->set_pulse (pulse);
1470 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1479 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1481 /* CALLER MUST HOLD WRITE LOCK */
1483 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1486 /* silly call from Session::process() during startup
1491 recompute_tempi (metrics);
1492 recompute_meters (metrics);
1496 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1498 Glib::Threads::RWLock::ReaderLock lm (lock);
1499 TempoMetric m (first_meter(), first_tempo());
1501 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1502 at something, because we insert the default tempo and meter during
1503 TempoMap construction.
1505 now see if we can find better candidates.
1508 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1510 if ((*i)->frame() > frame) {
1524 /* XX meters only */
1526 TempoMap::metric_at (BBT_Time bbt) const
1528 Glib::Threads::RWLock::ReaderLock lm (lock);
1529 TempoMetric m (first_meter(), first_tempo());
1531 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1532 at something, because we insert the default tempo and meter during
1533 TempoMap construction.
1535 now see if we can find better candidates.
1538 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1540 if (!(*i)->is_tempo()) {
1541 mw = static_cast<MeterSection*> (*i);
1542 BBT_Time section_start (mw->bbt());
1544 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1555 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1556 * @param frame The session frame position.
1557 * @return The beat duration according to the tempo map at the supplied frame.
1559 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1560 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1562 * This function uses both tempo and meter.
1565 TempoMap::beat_at_frame (const framecnt_t& frame) const
1567 Glib::Threads::RWLock::ReaderLock lm (lock);
1569 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1572 /* This function uses both tempo and meter.*/
1574 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1576 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1577 MeterSection* prev_m = 0;
1578 MeterSection* next_m = 0;
1580 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1581 if (!(*i)->is_tempo()) {
1582 if (prev_m && (*i)->minute() > minute) {
1583 next_m = static_cast<MeterSection*> (*i);
1586 prev_m = static_cast<MeterSection*> (*i);
1590 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1592 /* audio locked meters fake their beat */
1593 if (next_m && next_m->beat() < beat) {
1594 return next_m->beat();
1600 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1601 * @param beat The BBT (meter-based) beat.
1602 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1604 * This function uses both tempo and meter.
1607 TempoMap::frame_at_beat (const double& beat) const
1609 Glib::Threads::RWLock::ReaderLock lm (lock);
1611 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1614 /* meter & tempo section based */
1616 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1618 MeterSection* prev_m = 0;
1619 TempoSection* prev_t = 0;
1623 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1624 if (!(*i)->is_tempo()) {
1625 m = static_cast<MeterSection*> (*i);
1626 if (prev_m && m->beat() > beat) {
1636 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1637 if ((*i)->is_tempo()) {
1638 t = static_cast<TempoSection*> (*i);
1639 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1648 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1651 /** Returns a Tempo corresponding to the supplied frame position.
1652 * @param frame The audio frame.
1653 * @return a Tempo according to the tempo map at the supplied frame.
1657 TempoMap::tempo_at_frame (const framepos_t& frame) const
1659 Glib::Threads::RWLock::ReaderLock lm (lock);
1661 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1665 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1667 TempoSection* prev_t = 0;
1671 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1672 if ((*i)->is_tempo()) {
1673 t = static_cast<TempoSection*> (*i);
1677 if ((prev_t) && t->minute() > minute) {
1678 /* t is the section past frame */
1679 return prev_t->tempo_at_minute (minute);
1685 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1688 /** returns the frame at which the supplied tempo occurs, or
1689 * the frame of the last tempo section (search exhausted)
1690 * only the position of the first occurence will be returned
1694 TempoMap::frame_at_tempo (const Tempo& tempo) const
1696 Glib::Threads::RWLock::ReaderLock lm (lock);
1698 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1702 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1704 TempoSection* prev_t = 0;
1705 const double tempo_bpm = tempo.note_types_per_minute();
1707 Metrics::const_iterator i;
1709 for (i = metrics.begin(); i != metrics.end(); ++i) {
1711 if ((*i)->is_tempo()) {
1712 t = static_cast<TempoSection*> (*i);
1718 const double t_bpm = t->note_types_per_minute();
1720 if (t_bpm == tempo_bpm) {
1725 const double prev_t_bpm = prev_t->note_types_per_minute();
1727 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1728 return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse());
1735 return prev_t->minute();
1739 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1741 TempoSection* prev_t = 0;
1745 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1746 if ((*i)->is_tempo()) {
1747 t = static_cast<TempoSection*> (*i);
1751 if ((prev_t) && t->pulse() > pulse) {
1752 /* t is the section past frame */
1753 return prev_t->tempo_at_pulse (pulse);
1759 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1763 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1765 TempoSection* prev_t = 0;
1766 const double tempo_bpm = tempo.note_types_per_minute();
1768 Metrics::const_iterator i;
1770 for (i = metrics.begin(); i != metrics.end(); ++i) {
1772 if ((*i)->is_tempo()) {
1773 t = static_cast<TempoSection*> (*i);
1779 const double t_bpm = t->note_types_per_minute();
1781 if (t_bpm == tempo_bpm) {
1786 const double prev_t_bpm = prev_t->note_types_per_minute();
1788 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1789 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1796 return prev_t->pulse();
1799 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1800 * @param qn the position in quarter note beats.
1801 * @return the Tempo at the supplied quarter-note.
1804 TempoMap::tempo_at_quarter_note (const double& qn) const
1806 Glib::Threads::RWLock::ReaderLock lm (lock);
1808 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1811 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1812 * @param tempo the tempo.
1813 * @return the position in quarter-note beats where the map bpm
1814 * is equal to that of the Tempo. currently ignores note_type.
1817 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1819 Glib::Threads::RWLock::ReaderLock lm (lock);
1821 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1824 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1825 * @param metrics the list of metric sections used to calculate the pulse.
1826 * @param beat The BBT (meter-based) beat.
1827 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1829 * a pulse or whole note is the base musical position of a MetricSection.
1830 * it is equivalent to four quarter notes.
1834 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1836 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1838 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1841 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1842 * @param metrics the list of metric sections used to calculate the beat.
1843 * @param pulse the whole-note pulse.
1844 * @return the meter-based beat at the supplied whole-note pulse.
1846 * a pulse or whole note is the base musical position of a MetricSection.
1847 * it is equivalent to four quarter notes.
1850 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1852 MeterSection* prev_m = 0;
1854 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1856 if (!(*i)->is_tempo()) {
1857 m = static_cast<MeterSection*> (*i);
1858 if (prev_m && m->pulse() > pulse) {
1866 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1870 /* tempo section based */
1872 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1874 /* HOLD (at least) THE READER LOCK */
1875 TempoSection* prev_t = 0;
1877 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1879 if ((*i)->is_tempo()) {
1880 t = static_cast<TempoSection*> (*i);
1884 if (prev_t && t->minute() > minute) {
1885 /*the previous ts is the one containing the frame */
1886 const double ret = prev_t->pulse_at_minute (minute);
1887 /* audio locked section in new meter*/
1888 if (t->pulse() < ret) {
1897 /* treated as constant for this ts */
1898 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1900 return pulses_in_section + prev_t->pulse();
1903 /* tempo section based */
1905 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1907 /* HOLD THE READER LOCK */
1909 const TempoSection* prev_t = 0;
1911 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1914 if ((*i)->is_tempo()) {
1915 t = static_cast<TempoSection*> (*i);
1919 if (prev_t && t->pulse() > pulse) {
1920 return prev_t->minute_at_pulse (pulse);
1926 /* must be treated as constant, irrespective of _type */
1927 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1929 return dtime + prev_t->minute();
1932 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1933 * @param bbt The BBT time (meter-based).
1934 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1938 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1940 Glib::Threads::RWLock::ReaderLock lm (lock);
1941 return beat_at_bbt_locked (_metrics, bbt);
1946 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1948 /* CALLER HOLDS READ LOCK */
1950 MeterSection* prev_m = 0;
1952 /* because audio-locked meters have 'fake' integral beats,
1953 there is no pulse offset here.
1957 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1958 if (!(*i)->is_tempo()) {
1959 m = static_cast<MeterSection*> (*i);
1961 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1962 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1970 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1971 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1972 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1977 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
1978 * @param beat The BBT (meter-based) beat.
1979 * @return The BBT time (meter-based) at the supplied meter-based beat.
1983 TempoMap::bbt_at_beat (const double& beat)
1985 Glib::Threads::RWLock::ReaderLock lm (lock);
1986 return bbt_at_beat_locked (_metrics, beat);
1990 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1992 /* CALLER HOLDS READ LOCK */
1993 MeterSection* prev_m = 0;
1994 const double beats = max (0.0, b);
1996 MeterSection* m = 0;
1998 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1999 if (!(*i)->is_tempo()) {
2000 m = static_cast<MeterSection*> (*i);
2002 if (m->beat() > beats) {
2003 /* this is the meter after the one our beat is on*/
2013 const double beats_in_ms = beats - prev_m->beat();
2014 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2015 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2016 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2017 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2021 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2022 ret.beats = (uint32_t) floor (remaining_beats);
2023 ret.bars = total_bars;
2025 /* 0 0 0 to 1 1 0 - based mapping*/
2029 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2031 ret.ticks -= BBT_Time::ticks_per_beat;
2034 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2042 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2043 * @param bbt The BBT time (meter-based).
2044 * @return the quarter note beat at the supplied BBT time
2046 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2048 * while the input uses meter, the output does not.
2051 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2053 Glib::Threads::RWLock::ReaderLock lm (lock);
2055 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2059 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2061 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2064 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2067 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2071 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2073 /* CALLER HOLDS READ LOCK */
2075 MeterSection* prev_m = 0;
2077 /* because audio-locked meters have 'fake' integral beats,
2078 there is no pulse offset here.
2082 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2083 if (!(*i)->is_tempo()) {
2084 m = static_cast<MeterSection*> (*i);
2086 if (m->bbt().bars > bbt.bars) {
2094 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2095 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2096 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2101 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2102 * @param qn the quarter-note beat.
2103 * @return The BBT time (meter-based) at the supplied meter-based beat.
2105 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2109 TempoMap::bbt_at_quarter_note (const double& qn)
2111 Glib::Threads::RWLock::ReaderLock lm (lock);
2113 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2116 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2117 * @param metrics The list of metric sections used to determine the result.
2118 * @param pulse The whole-note pulse.
2119 * @return The BBT time at the supplied whole-note pulse.
2121 * a pulse or whole note is the basic musical position of a MetricSection.
2122 * it is equivalent to four quarter notes.
2123 * while the output uses meter, the input does not.
2126 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2128 MeterSection* prev_m = 0;
2130 MeterSection* m = 0;
2132 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2134 if (!(*i)->is_tempo()) {
2135 m = static_cast<MeterSection*> (*i);
2138 double const pulses_to_m = m->pulse() - prev_m->pulse();
2139 if (prev_m->pulse() + pulses_to_m > pulse) {
2140 /* this is the meter after the one our beat is on*/
2151 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2152 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2153 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2154 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2155 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2159 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2160 ret.beats = (uint32_t) floor (remaining_beats);
2161 ret.bars = total_bars;
2163 /* 0 0 0 to 1 1 0 mapping*/
2167 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2169 ret.ticks -= BBT_Time::ticks_per_beat;
2172 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2180 /** Returns the BBT time corresponding to the supplied frame position.
2181 * @param frame the position in audio samples.
2182 * @return the BBT time at the frame position .
2186 TempoMap::bbt_at_frame (framepos_t frame)
2193 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2196 Glib::Threads::RWLock::ReaderLock lm (lock);
2198 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2202 TempoMap::bbt_at_frame_rt (framepos_t frame)
2204 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2207 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2210 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2214 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2224 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2225 MeterSection* prev_m = 0;
2226 MeterSection* next_m = 0;
2230 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2231 if (!(*i)->is_tempo()) {
2232 m = static_cast<MeterSection*> (*i);
2233 if (prev_m && m->minute() > minute) {
2241 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2243 /* handle frame before first meter */
2244 if (minute < prev_m->minute()) {
2247 /* audio locked meters fake their beat */
2248 if (next_m && next_m->beat() < beat) {
2249 beat = next_m->beat();
2252 beat = max (0.0, beat);
2254 const double beats_in_ms = beat - prev_m->beat();
2255 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2256 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2257 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2258 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2262 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2263 ret.beats = (uint32_t) floor (remaining_beats);
2264 ret.bars = total_bars;
2266 /* 0 0 0 to 1 1 0 - based mapping*/
2270 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2272 ret.ticks -= BBT_Time::ticks_per_beat;
2275 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2283 /** Returns the frame position corresponding to the supplied BBT time.
2284 * @param bbt the position in BBT time.
2285 * @return the frame position at bbt.
2289 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2292 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2296 if (bbt.beats < 1) {
2297 throw std::logic_error ("beats are counted from one");
2299 Glib::Threads::RWLock::ReaderLock lm (lock);
2301 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
2304 /* meter & tempo section based */
2306 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2308 /* HOLD THE READER LOCK */
2310 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2315 * Returns the quarter-note beat position corresponding to the supplied frame.
2317 * @param frame the position in frames.
2318 * @return The quarter-note position of the supplied frame. Ignores meter.
2322 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2324 Glib::Threads::RWLock::ReaderLock lm (lock);
2326 const double ret = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
2332 TempoMap::quarter_note_at_minute_locked (const Metrics& metrics, const double minute) const
2334 const double ret = pulse_at_minute_locked (metrics, minute) * 4.0;
2340 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2342 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2345 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2348 const double ret = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
2354 * Returns the frame position corresponding to the supplied quarter-note beat.
2356 * @param quarter_note the quarter-note position.
2357 * @return the frame position of the supplied quarter-note. Ignores meter.
2362 TempoMap::frame_at_quarter_note (const double quarter_note) const
2364 Glib::Threads::RWLock::ReaderLock lm (lock);
2366 const framepos_t ret = frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note));
2372 TempoMap::minute_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2374 const double ret = minute_at_pulse_locked (metrics, quarter_note / 4.0);
2379 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2380 * @param beat The BBT (meter-based) beat.
2381 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2383 * a quarter-note may be compared with and assigned to Evoral::Beats.
2387 TempoMap::quarter_note_at_beat (const double beat) const
2389 Glib::Threads::RWLock::ReaderLock lm (lock);
2391 const double ret = quarter_note_at_beat_locked (_metrics, beat);
2397 TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) const
2399 const double ret = pulse_at_beat_locked (metrics, beat) * 4.0;
2404 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2405 * @param quarter_note The position in quarter-note beats.
2406 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2408 * a quarter-note is the musical unit of Evoral::Beats.
2412 TempoMap::beat_at_quarter_note (const double quarter_note) const
2414 Glib::Threads::RWLock::ReaderLock lm (lock);
2416 const double ret = beat_at_quarter_note_locked (_metrics, quarter_note);
2422 TempoMap::beat_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2425 return beat_at_pulse_locked (metrics, quarter_note / 4.0);
2428 /** Returns the duration in frames between two supplied quarter-note beat positions.
2429 * @param start the first position in quarter-note beats.
2430 * @param end the end position in quarter-note beats.
2431 * @return the frame distance ober the quarter-note beats duration.
2433 * use this rather than e.g.
2434 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2435 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2439 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2441 Glib::Threads::RWLock::ReaderLock lm (lock);
2443 return frame_at_minute (minutes_between_quarter_notes_locked (_metrics, start, end));
2447 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2450 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2454 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2456 Glib::Threads::RWLock::ReaderLock lm (lock);
2458 return quarter_notes_between_frames_locked (_metrics, start, end);
2462 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2464 const TempoSection* prev_t = 0;
2466 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2469 if ((*i)->is_tempo()) {
2470 t = static_cast<TempoSection*> (*i);
2474 if (prev_t && t->frame() > start) {
2481 const double start_qn = prev_t->pulse_at_frame (start);
2483 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2486 if ((*i)->is_tempo()) {
2487 t = static_cast<TempoSection*> (*i);
2491 if (prev_t && t->frame() > end) {
2497 const double end_qn = prev_t->pulse_at_frame (end);
2499 return (end_qn - start_qn) * 4.0;
2503 TempoMap::check_solved (const Metrics& metrics) const
2505 TempoSection* prev_t = 0;
2506 MeterSection* prev_m = 0;
2508 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2511 if ((*i)->is_tempo()) {
2512 t = static_cast<TempoSection*> (*i);
2517 /* check ordering */
2518 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2522 /* precision check ensures tempo and frames align.*/
2523 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))) {
2524 if (!t->locked_to_meter()) {
2529 /* gradient limit - who knows what it should be?
2530 things are also ok (if a little chaotic) without this
2532 if (fabs (prev_t->c_func()) > 1000.0) {
2533 //std::cout << "c : " << prev_t->c_func() << std::endl;
2540 if (!(*i)->is_tempo()) {
2541 m = static_cast<MeterSection*> (*i);
2542 if (prev_m && m->position_lock_style() == AudioTime) {
2543 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2544 const double nascent_m_minute = t->minute_at_pulse (m->pulse());
2545 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2547 if (t && (nascent_m_minute > m->minute() || nascent_m_minute < 0.0)) {
2561 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2563 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2565 if ((*i)->is_tempo()) {
2566 t = static_cast<TempoSection*> (*i);
2568 t->set_active (true);
2570 } else if (t->position_lock_style() == AudioTime) {
2571 if (t->active () && t->frame() < frame) {
2572 t->set_active (false);
2574 } else if (t->frame() > frame) {
2575 t->set_active (true);
2576 } else if (t->frame() == frame) {
2586 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2588 TempoSection* prev_t = 0;
2589 TempoSection* section_prev = 0;
2590 double first_m_minute = 0.0;
2592 /* can't move a tempo before the first meter */
2593 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2595 if (!(*i)->is_tempo()) {
2596 m = static_cast<MeterSection*> (*i);
2598 first_m_minute = m->minute();
2603 if (!section->initial() && minute <= first_m_minute) {
2607 section->set_active (true);
2608 section->set_minute (minute);
2610 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2612 if ((*i)->is_tempo()) {
2613 t = static_cast<TempoSection*> (*i);
2620 section_prev = prev_t;
2621 if (t->locked_to_meter()) {
2627 if (t->frame() == frame_at_minute (minute)) {
2631 if (t->position_lock_style() == MusicTime) {
2632 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2633 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2635 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2636 if (!t->locked_to_meter()) {
2637 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2646 section_prev->set_c_func (section_prev->compute_c_func_minute (section->note_types_per_minute(), minute));
2647 if (!section->locked_to_meter()) {
2648 section->set_pulse (section_prev->pulse_at_ntpm (section->note_types_per_minute(), minute));
2653 recompute_tempi (imaginary);
2655 if (check_solved (imaginary)) {
2658 dunp (imaginary, std::cout);
2662 MetricSectionFrameSorter fcmp;
2663 imaginary.sort (fcmp);
2665 recompute_tempi (imaginary);
2667 if (check_solved (imaginary)) {
2675 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2677 TempoSection* prev_t = 0;
2678 TempoSection* section_prev = 0;
2680 section->set_pulse (pulse);
2682 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2684 if ((*i)->is_tempo()) {
2685 t = static_cast<TempoSection*> (*i);
2696 section_prev = prev_t;
2699 if (t->position_lock_style() == MusicTime) {
2700 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2701 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2703 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2704 if (!t->locked_to_meter()) {
2705 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2714 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->note_types_per_minute(), pulse));
2715 section->set_minute (section_prev->minute_at_ntpm (section->note_types_per_minute(), pulse));
2719 recompute_tempi (imaginary);
2721 if (check_solved (imaginary)) {
2724 dunp (imaginary, std::cout);
2728 MetricSectionSorter cmp;
2729 imaginary.sort (cmp);
2731 recompute_tempi (imaginary);
2733 * XX need a restriction here, but only for this case,
2734 * as audio locked tempos don't interact in the same way.
2736 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2738 * |50 bpm |250 bpm |60 bpm
2739 * drag 250 to the pulse after 60->
2740 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2742 if (check_solved (imaginary)) {
2750 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2752 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2753 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2754 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2758 if (section->initial()) {
2759 /* lock the first tempo to our first meter */
2760 if (!set_active_tempi (imaginary, section->frame_at_minute (minute))) {
2765 TempoSection* meter_locked_tempo = 0;
2767 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2769 if ((*ii)->is_tempo()) {
2770 t = static_cast<TempoSection*> (*ii);
2771 if ((t->locked_to_meter() || t->initial()) && t->minute() == section->minute()) {
2772 meter_locked_tempo = t;
2778 if (!meter_locked_tempo) {
2782 MeterSection* prev_m = 0;
2784 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2785 bool solved = false;
2787 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2789 if (!(*i)->is_tempo()) {
2790 m = static_cast<MeterSection*> (*i);
2792 if (prev_m && !section->initial()) {
2793 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2794 if (beats + prev_m->beat() < section->beat()) {
2795 /* set the section pulse according to its musical position,
2796 * as an earlier time than this has been requested.
2798 const double new_pulse = ((section->beat() - prev_m->beat())
2799 / prev_m->note_divisor()) + prev_m->pulse();
2801 tempo_copy->set_position_lock_style (MusicTime);
2802 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2803 meter_locked_tempo->set_position_lock_style (MusicTime);
2804 section->set_position_lock_style (MusicTime);
2805 section->set_pulse (new_pulse);
2806 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2807 meter_locked_tempo->set_position_lock_style (AudioTime);
2808 section->set_position_lock_style (AudioTime);
2809 section->set_minute (meter_locked_tempo->minute());
2815 Metrics::const_iterator d = future_map.begin();
2816 while (d != future_map.end()) {
2825 /* all is ok. set section's locked tempo if allowed.
2826 possibly disallowed if there is an adjacent audio-locked tempo.
2827 XX this check could possibly go. its never actually happened here.
2829 MeterSection* meter_copy = const_cast<MeterSection*>
2830 (&meter_section_at_minute_locked (future_map, section->minute()));
2832 meter_copy->set_minute (minute);
2834 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2835 section->set_minute (minute);
2836 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2837 / prev_m->note_divisor()) + prev_m->pulse());
2838 solve_map_minute (imaginary, meter_locked_tempo, minute);
2843 Metrics::const_iterator d = future_map.begin();
2844 while (d != future_map.end()) {
2854 /* initial (first meter atm) */
2856 tempo_copy->set_minute (minute);
2857 tempo_copy->set_pulse (0.0);
2859 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2860 section->set_minute (minute);
2861 meter_locked_tempo->set_minute (minute);
2862 meter_locked_tempo->set_pulse (0.0);
2863 solve_map_minute (imaginary, meter_locked_tempo, minute);
2868 Metrics::const_iterator d = future_map.begin();
2869 while (d != future_map.end()) {
2878 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2879 section->set_beat (b_bbt);
2880 section->set_pulse (0.0);
2890 MetricSectionFrameSorter fcmp;
2891 imaginary.sort (fcmp);
2893 recompute_meters (imaginary);
2899 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2901 /* disallow setting section to an existing meter's bbt */
2902 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2904 if (!(*i)->is_tempo()) {
2905 m = static_cast<MeterSection*> (*i);
2906 if (m != section && m->bbt().bars == when.bars) {
2912 MeterSection* prev_m = 0;
2913 MeterSection* section_prev = 0;
2915 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2917 if (!(*i)->is_tempo()) {
2918 m = static_cast<MeterSection*> (*i);
2924 pair<double, BBT_Time> b_bbt;
2925 double new_pulse = 0.0;
2927 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2928 section_prev = prev_m;
2930 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2931 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2932 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2934 section->set_beat (b_bbt);
2935 section->set_pulse (pulse);
2936 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2940 if (m->position_lock_style() == AudioTime) {
2941 TempoSection* meter_locked_tempo = 0;
2943 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2945 if ((*ii)->is_tempo()) {
2946 t = static_cast<TempoSection*> (*ii);
2947 if ((t->locked_to_meter() || t->initial()) && t->frame() == m->frame()) {
2948 meter_locked_tempo = t;
2954 if (!meter_locked_tempo) {
2959 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2961 if (beats + prev_m->beat() != m->beat()) {
2962 /* tempo/ meter change caused a change in beat (bar). */
2964 /* the user has requested that the previous section of music overlaps this one.
2965 we have no choice but to change the bar number here, as being locked to audio means
2966 we must stay where we are on the timeline.
2968 beats = m->beat() - prev_m->beat();
2969 b_bbt = make_pair (beats + prev_m->beat()
2970 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2971 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2973 } else if (!m->initial()) {
2974 b_bbt = make_pair (m->beat(), m->bbt());
2975 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2978 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2981 meter_locked_tempo->set_pulse (new_pulse);
2982 m->set_beat (b_bbt);
2983 m->set_pulse (new_pulse);
2987 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2988 if (beats + prev_m->beat() != m->beat()) {
2989 /* tempo/ meter change caused a change in beat (bar). */
2990 b_bbt = make_pair (beats + prev_m->beat()
2991 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2993 b_bbt = make_pair (beats + prev_m->beat()
2996 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2997 m->set_beat (b_bbt);
2998 m->set_pulse (new_pulse);
2999 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3006 if (!section_prev) {
3008 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3009 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3010 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3012 section->set_beat (b_bbt);
3013 section->set_pulse (pulse);
3014 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3017 MetricSectionSorter cmp;
3018 imaginary.sort (cmp);
3020 recompute_meters (imaginary);
3025 /** places a copy of _metrics into copy and returns a pointer
3026 * to section's equivalent in copy.
3029 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
3031 TempoSection* ret = 0;
3033 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3036 if ((*i)->is_tempo()) {
3037 t = static_cast<TempoSection*> (*i);
3039 ret = new TempoSection (*t);
3040 copy.push_back (ret);
3044 TempoSection* cp = new TempoSection (*t);
3045 copy.push_back (cp);
3047 if (!(*i)->is_tempo()) {
3048 m = static_cast<MeterSection *> (*i);
3049 MeterSection* cp = new MeterSection (*m);
3050 copy.push_back (cp);
3058 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3060 MeterSection* ret = 0;
3062 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3065 if ((*i)->is_tempo()) {
3066 t = static_cast<TempoSection*> (*i);
3067 TempoSection* cp = new TempoSection (*t);
3068 copy.push_back (cp);
3071 if (!(*i)->is_tempo()) {
3072 m = static_cast<MeterSection *> (*i);
3074 ret = new MeterSection (*m);
3075 copy.push_back (ret);
3078 MeterSection* cp = new MeterSection (*m);
3079 copy.push_back (cp);
3086 /** answers the question "is this a valid beat position for this tempo section?".
3087 * it returns true if the tempo section can be moved to the requested bbt position,
3088 * leaving the tempo map in a solved state.
3089 * @param ts the tempo section to be moved
3090 * @param bbt the requested new position for the tempo section
3091 * @return true if the tempo section can be moved to the position, otherwise false.
3094 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3097 TempoSection* tempo_copy = 0;
3100 Glib::Threads::RWLock::ReaderLock lm (lock);
3101 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3107 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3109 Metrics::const_iterator d = copy.begin();
3110 while (d != copy.end()) {
3119 * 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,
3120 * taking any possible reordering as a consequence of this into account.
3121 * @param section - the section to be altered
3122 * @param bbt - the BBT time where the altered tempo will fall
3123 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3125 pair<double, framepos_t>
3126 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3129 pair<double, framepos_t> ret = make_pair (0.0, 0);
3131 Glib::Threads::RWLock::ReaderLock lm (lock);
3133 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3135 const double beat = beat_at_bbt_locked (future_map, bbt);
3137 if (section->position_lock_style() == AudioTime) {
3138 tempo_copy->set_position_lock_style (MusicTime);
3141 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3142 ret.first = tempo_copy->pulse();
3143 ret.second = tempo_copy->frame();
3145 ret.first = section->pulse();
3146 ret.second = section->frame();
3149 Metrics::const_iterator d = future_map.begin();
3150 while (d != future_map.end()) {
3157 /** moves a TempoSection to a specified position.
3158 * @param ts - the section to be moved
3159 * @param frame - the new position in frames for the tempo
3160 * @param sub_num - the snap division to use if using musical time.
3162 * if sub_num is non-zero, the frame position is used to calculate an exact
3165 * -1 | snap to bars (meter-based)
3166 * 0 | no snap - use audio frame for musical position
3167 * 1 | snap to meter-based (BBT) beat
3168 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3170 * this follows the snap convention in the gui.
3171 * if sub_num is zero, the musical position will be taken from the supplied frame.
3174 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3178 if (ts->position_lock_style() == MusicTime) {
3180 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3181 Glib::Threads::RWLock::WriterLock lm (lock);
3182 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3184 tempo_copy->set_position_lock_style (AudioTime);
3186 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3187 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3188 const double pulse = pulse_at_beat_locked (future_map, beat);
3190 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3191 solve_map_pulse (_metrics, ts, pulse);
3192 recompute_meters (_metrics);
3200 Glib::Threads::RWLock::WriterLock lm (lock);
3201 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3203 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3205 /* We're moving the object that defines the grid while snapping to it...
3206 * Placing the ts at the beat corresponding to the requested frame may shift the
3207 * grid in such a way that the mouse is left hovering over a completerly different division,
3208 * causing jittering when the mouse next moves (esp. large tempo deltas).
3210 * This alters the snap behaviour slightly in that we snap to beat divisions
3211 * in the future map rather than the existing one.
3213 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3214 const framepos_t snapped_frame = frame_at_minute (minute_at_quarter_note_locked (future_map, qn));
3216 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3217 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3218 ts->set_pulse (qn / 4.0);
3219 recompute_meters (_metrics);
3222 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3223 recompute_meters (_metrics);
3229 Metrics::const_iterator d = future_map.begin();
3230 while (d != future_map.end()) {
3235 MetricPositionChanged (); // Emit Signal
3238 /** moves a MeterSection to a specified position.
3239 * @param ms - the section to be moved
3240 * @param frame - the new position in frames for the meter
3242 * as a meter cannot snap to anything but bars,
3243 * the supplied frame is rounded to the nearest bar, possibly
3244 * leaving the meter position unchanged.
3247 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
3251 if (ms->position_lock_style() == AudioTime) {
3254 Glib::Threads::RWLock::WriterLock lm (lock);
3255 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3257 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3258 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3259 recompute_tempi (_metrics);
3264 Glib::Threads::RWLock::WriterLock lm (lock);
3265 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3267 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3268 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3270 if (solve_map_bbt (future_map, copy, bbt)) {
3271 solve_map_bbt (_metrics, ms, bbt);
3272 recompute_tempi (_metrics);
3277 Metrics::const_iterator d = future_map.begin();
3278 while (d != future_map.end()) {
3283 MetricPositionChanged (); // Emit Signal
3287 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3290 bool can_solve = false;
3292 Glib::Threads::RWLock::WriterLock lm (lock);
3293 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3294 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3295 recompute_tempi (future_map);
3297 if (check_solved (future_map)) {
3298 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3299 recompute_map (_metrics);
3304 Metrics::const_iterator d = future_map.begin();
3305 while (d != future_map.end()) {
3310 MetricPositionChanged (); // Emit Signal
3316 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
3319 Ts (future prev_t) Tnext
3322 |----------|----------
3329 Glib::Threads::RWLock::WriterLock lm (lock);
3335 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3336 TempoSection* prev_to_prev_t = 0;
3337 const frameoffset_t fr_off = end_frame - frame;
3341 if (prev_t->pulse() > 0.0) {
3342 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
3345 TempoSection* next_t = 0;
3346 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
3347 TempoSection* t = 0;
3348 if ((*i)->is_tempo()) {
3349 t = static_cast<TempoSection*> (*i);
3350 if (t->frame() > ts->frame()) {
3356 /* minimum allowed measurement distance in frames */
3357 const framepos_t min_dframe = 2;
3359 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3360 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3362 double contribution = 0.0;
3364 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3365 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3368 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3370 const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame));
3371 const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame));
3375 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
3377 if (prev_t->position_lock_style() == MusicTime) {
3378 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3379 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3381 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3382 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3384 new_bpm = prev_t->note_types_per_minute();
3387 /* prev to prev is irrelevant */
3389 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3390 new_bpm = prev_t->note_types_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3392 new_bpm = prev_t->note_types_per_minute();
3397 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3398 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3400 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3401 / (double) ((end_frame) - prev_to_prev_t->frame()));
3403 new_bpm = prev_t->note_types_per_minute();
3406 /* prev_to_prev_t is irrelevant */
3408 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3409 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3411 new_bpm = prev_t->note_types_per_minute();
3417 double frame_ratio = 1.0;
3418 double pulse_ratio = 1.0;
3419 const double pulse_pos = frame;
3421 if (prev_to_prev_t) {
3422 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3423 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3425 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3426 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3429 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3430 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3432 pulse_ratio = (start_pulse / end_pulse);
3434 new_bpm = prev_t->note_types_per_minute() * (pulse_ratio * frame_ratio);
3437 /* don't clamp and proceed here.
3438 testing has revealed that this can go negative,
3439 which is an entirely different thing to just being too low.
3441 if (new_bpm < 0.5) {
3444 new_bpm = min (new_bpm, (double) 1000.0);
3445 prev_t->set_note_types_per_minute (new_bpm);
3446 recompute_tempi (future_map);
3447 recompute_meters (future_map);
3449 if (check_solved (future_map)) {
3450 ts->set_note_types_per_minute (new_bpm);
3451 recompute_tempi (_metrics);
3452 recompute_meters (_metrics);
3456 Metrics::const_iterator d = future_map.begin();
3457 while (d != future_map.end()) {
3462 MetricPositionChanged (); // Emit Signal
3465 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3466 * the supplied frame, possibly returning a negative value.
3468 * @param frame The session frame position.
3469 * @param sub_num The subdivision to use when rounding the beat.
3470 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3471 * Positive integers indicate quarter note (non BBT) divisions.
3472 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3473 * @return The beat position of the supplied frame.
3475 * when working to a musical grid, the use of sub_nom indicates that
3476 * the position should be interpreted musically.
3478 * it effectively snaps to meter bars, meter beats or quarter note divisions
3479 * (as per current gui convention) and returns a musical position independent of frame rate.
3481 * If the supplied frame lies before the first meter, the return will be negative,
3482 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3483 * the continuation of the tempo curve (backwards).
3485 * This function is sensitive to tempo and meter.
3488 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3490 Glib::Threads::RWLock::ReaderLock lm (lock);
3492 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3496 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3498 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3501 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3502 * the supplied frame, possibly returning a negative value.
3504 * @param frame The session frame position.
3505 * @param sub_num The subdivision to use when rounding the quarter note.
3506 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3507 * Positive integers indicate quarter note (non BBT) divisions.
3508 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3509 * @return The quarter note position of the supplied frame.
3511 * When working to a musical grid, the use of sub_nom indicates that
3512 * the frame position should be interpreted musically.
3514 * it effectively snaps to meter bars, meter beats or quarter note divisions
3515 * (as per current gui convention) and returns a musical position independent of frame rate.
3517 * If the supplied frame lies before the first meter, the return will be negative,
3518 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3519 * the continuation of the tempo curve (backwards).
3521 * This function is tempo-sensitive.
3524 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3526 Glib::Threads::RWLock::ReaderLock lm (lock);
3528 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3532 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3534 double qn = quarter_note_at_minute_locked (metrics, minute_at_frame (frame));
3537 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3538 } else if (sub_num == 1) {
3539 /* the gui requested exact musical (BBT) beat */
3540 qn = quarter_note_at_beat_locked (metrics, floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5));
3541 } else if (sub_num == -1) {
3543 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3547 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3549 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3551 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3561 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3562 * @param pos the frame position in the tempo map.
3563 * @param bbt the distance in BBT time from pos to calculate.
3564 * @param dir the rounding direction..
3565 * @return the duration in frames between pos and bbt
3568 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3570 Glib::Threads::RWLock::ReaderLock lm (lock);
3572 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3574 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3577 pos_bbt.bars += bbt.bars;
3579 pos_bbt.ticks += bbt.ticks;
3580 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3582 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3585 pos_bbt.beats += bbt.beats;
3586 if ((double) pos_bbt.beats > divisions) {
3588 pos_bbt.beats -= divisions;
3590 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3592 return pos_bbt_frame - pos;
3596 if (pos_bbt.bars <= bbt.bars) {
3599 pos_bbt.bars -= bbt.bars;
3602 if (pos_bbt.ticks < bbt.ticks) {
3603 if (pos_bbt.bars > 1) {
3604 if (pos_bbt.beats == 1) {
3606 pos_bbt.beats = divisions;
3610 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3616 pos_bbt.ticks -= bbt.ticks;
3619 if (pos_bbt.beats <= bbt.beats) {
3620 if (pos_bbt.bars > 1) {
3622 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3627 pos_bbt.beats -= bbt.beats;
3630 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3637 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3639 return round_to_type (fr, dir, Bar);
3643 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3645 return round_to_type (fr, dir, Beat);
3649 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3651 Glib::Threads::RWLock::ReaderLock lm (lock);
3652 uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3653 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3654 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3656 ticks -= beats * BBT_Time::ticks_per_beat;
3659 /* round to next (or same iff dir == RoundUpMaybe) */
3661 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3663 if (mod == 0 && dir == RoundUpMaybe) {
3664 /* right on the subdivision, which is fine, so do nothing */
3666 } else if (mod == 0) {
3667 /* right on the subdivision, so the difference is just the subdivision ticks */
3668 ticks += ticks_one_subdivisions_worth;
3671 /* not on subdivision, compute distance to next subdivision */
3673 ticks += ticks_one_subdivisions_worth - mod;
3676 if (ticks >= BBT_Time::ticks_per_beat) {
3677 ticks -= BBT_Time::ticks_per_beat;
3679 } else if (dir < 0) {
3681 /* round to previous (or same iff dir == RoundDownMaybe) */
3683 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3685 if (difference == 0 && dir == RoundDownAlways) {
3686 /* right on the subdivision, but force-rounding down,
3687 so the difference is just the subdivision ticks */
3688 difference = ticks_one_subdivisions_worth;
3691 if (ticks < difference) {
3692 ticks = BBT_Time::ticks_per_beat - ticks;
3694 ticks -= difference;
3698 /* round to nearest */
3701 /* compute the distance to the previous and next subdivision */
3703 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3705 /* closer to the next subdivision, so shift forward */
3707 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3709 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3711 if (ticks > BBT_Time::ticks_per_beat) {
3713 ticks -= BBT_Time::ticks_per_beat;
3714 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3717 } else if (rem > 0) {
3719 /* closer to previous subdivision, so shift backward */
3723 /* can't go backwards past zero, so ... */
3726 /* step back to previous beat */
3728 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3729 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3731 ticks = lrint (ticks - rem);
3732 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3735 /* on the subdivision, do nothing */
3739 const framepos_t ret_frame = frame_at_minute (minute_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3745 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3747 Glib::Threads::RWLock::ReaderLock lm (lock);
3748 uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3749 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3750 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3752 ticks -= beats * BBT_Time::ticks_per_beat;
3755 /* round to next (or same iff dir == RoundUpMaybe) */
3757 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3759 if (mod == 0 && dir == RoundUpMaybe) {
3760 /* right on the subdivision, which is fine, so do nothing */
3762 } else if (mod == 0) {
3763 /* right on the subdivision, so the difference is just the subdivision ticks */
3764 ticks += ticks_one_subdivisions_worth;
3767 /* not on subdivision, compute distance to next subdivision */
3769 ticks += ticks_one_subdivisions_worth - mod;
3772 if (ticks >= BBT_Time::ticks_per_beat) {
3773 ticks -= BBT_Time::ticks_per_beat;
3775 } else if (dir < 0) {
3777 /* round to previous (or same iff dir == RoundDownMaybe) */
3779 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3781 if (difference == 0 && dir == RoundDownAlways) {
3782 /* right on the subdivision, but force-rounding down,
3783 so the difference is just the subdivision ticks */
3784 difference = ticks_one_subdivisions_worth;
3787 if (ticks < difference) {
3788 ticks = BBT_Time::ticks_per_beat - ticks;
3790 ticks -= difference;
3794 /* round to nearest */
3797 /* compute the distance to the previous and next subdivision */
3799 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3801 /* closer to the next subdivision, so shift forward */
3803 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3805 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3807 if (ticks > BBT_Time::ticks_per_beat) {
3809 ticks -= BBT_Time::ticks_per_beat;
3810 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3813 } else if (rem > 0) {
3815 /* closer to previous subdivision, so shift backward */
3819 /* can't go backwards past zero, so ... */
3822 /* step back to previous beat */
3824 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3825 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3827 ticks = lrint (ticks - rem);
3828 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3831 /* on the subdivision, do nothing */
3835 const framepos_t ret_frame = frame_at_minute (minute_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3841 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3843 Glib::Threads::RWLock::ReaderLock lm (lock);
3845 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (frame)));
3846 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3851 /* find bar previous to 'frame' */
3854 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3856 } else if (dir > 0) {
3857 /* find bar following 'frame' */
3861 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3863 /* true rounding: find nearest bar */
3864 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3867 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3869 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3871 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3882 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
3883 } else if (dir > 0) {
3884 return frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
3886 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
3895 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3896 framepos_t lower, framepos_t upper, uint32_t bar_mod)
3898 Glib::Threads::RWLock::ReaderLock lm (lock);
3899 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
3901 /* although the map handles negative beats, bbt doesn't. */
3906 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
3910 while (pos >= 0 && pos < upper) {
3911 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
3912 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3913 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3914 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3915 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3919 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
3924 bbt.bars -= bbt.bars % bar_mod;
3928 while (pos >= 0 && pos < upper) {
3929 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3930 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3931 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3932 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3933 bbt.bars += bar_mod;
3939 TempoMap::tempo_section_at_frame (framepos_t frame) const
3941 Glib::Threads::RWLock::ReaderLock lm (lock);
3943 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
3947 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
3949 TempoSection* prev = 0;
3953 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3955 if ((*i)->is_tempo()) {
3956 t = static_cast<TempoSection*> (*i);
3960 if (prev && t->minute() > minute) {
3970 abort(); /*NOTREACHED*/
3977 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3979 TempoSection* prev_t = 0;
3980 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3984 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3985 if ((*i)->is_tempo()) {
3986 t = static_cast<TempoSection*> (*i);
3987 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3997 /* don't use this to calculate length (the tempo is only correct for this frame).
3998 do that stuff based on the beat_at_frame and frame_at_beat api
4001 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4003 Glib::Threads::RWLock::ReaderLock lm (lock);
4005 const TempoSection* ts_at = 0;
4006 const TempoSection* ts_after = 0;
4007 Metrics::const_iterator i;
4010 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4012 if ((*i)->is_tempo()) {
4013 t = static_cast<TempoSection*> (*i);
4017 if (ts_at && (*i)->frame() > frame) {
4027 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4029 /* must be treated as constant tempo */
4030 return ts_at->frames_per_quarter_note (_frame_rate);
4034 TempoMap::meter_section_at_frame (framepos_t frame) const
4036 Glib::Threads::RWLock::ReaderLock lm (lock);
4037 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4041 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4043 Metrics::const_iterator i;
4044 MeterSection* prev = 0;
4048 for (i = metrics.begin(); i != metrics.end(); ++i) {
4050 if (!(*i)->is_tempo()) {
4051 m = static_cast<MeterSection*> (*i);
4053 if (prev && (*i)->minute() > minute) {
4063 abort(); /*NOTREACHED*/
4070 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4072 MeterSection* prev_m = 0;
4074 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4076 if (!(*i)->is_tempo()) {
4077 m = static_cast<MeterSection*> (*i);
4078 if (prev_m && m->beat() > beat) {
4089 TempoMap::meter_section_at_beat (double beat) const
4091 Glib::Threads::RWLock::ReaderLock lm (lock);
4092 return meter_section_at_beat_locked (_metrics, beat);
4096 TempoMap::meter_at_frame (framepos_t frame) const
4098 TempoMetric m (metric_at (frame));
4103 TempoMap::fix_legacy_session ()
4105 MeterSection* prev_m = 0;
4106 TempoSection* prev_t = 0;
4108 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4112 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4114 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4117 m->set_minute (0.0);
4118 m->set_position_lock_style (AudioTime);
4123 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4124 + (m->bbt().beats - 1)
4125 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4127 m->set_beat (start);
4128 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4129 + (m->bbt().beats - 1)
4130 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4131 m->set_pulse (start_beat / prev_m->note_divisor());
4134 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4142 t->set_minute (0.0);
4143 t->set_position_lock_style (AudioTime);
4149 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4150 + (t->legacy_bbt().beats - 1)
4151 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4153 t->set_pulse (beat / prev_m->note_divisor());
4155 /* really shouldn't happen but.. */
4156 t->set_pulse (beat / 4.0);
4165 TempoMap::get_state ()
4167 Metrics::const_iterator i;
4168 XMLNode *root = new XMLNode ("TempoMap");
4171 Glib::Threads::RWLock::ReaderLock lm (lock);
4172 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4173 root->add_child_nocopy ((*i)->get_state());
4181 TempoMap::set_state (const XMLNode& node, int /*version*/)
4184 Glib::Threads::RWLock::WriterLock lm (lock);
4187 XMLNodeConstIterator niter;
4188 Metrics old_metrics (_metrics);
4191 nlist = node.children();
4193 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4194 XMLNode* child = *niter;
4196 if (child->name() == TempoSection::xml_state_node_name) {
4199 TempoSection* ts = new TempoSection (*child, _frame_rate);
4200 _metrics.push_back (ts);
4203 catch (failed_constructor& err){
4204 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4205 _metrics = old_metrics;
4206 old_metrics.clear();
4210 } else if (child->name() == MeterSection::xml_state_node_name) {
4213 MeterSection* ms = new MeterSection (*child, _frame_rate);
4214 _metrics.push_back (ms);
4217 catch (failed_constructor& err) {
4218 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4219 _metrics = old_metrics;
4220 old_metrics.clear();
4226 if (niter == nlist.end()) {
4227 MetricSectionSorter cmp;
4228 _metrics.sort (cmp);
4231 /* check for legacy sessions where bbt was the base musical unit for tempo */
4232 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4234 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4235 if (t->legacy_bbt().bars != 0) {
4236 fix_legacy_session();
4243 /* check for multiple tempo/meters at the same location, which
4244 ardour2 somehow allowed.
4247 Metrics::iterator prev = _metrics.end();
4248 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4249 if (prev != _metrics.end()) {
4251 MeterSection* prev_m;
4253 TempoSection* prev_t;
4254 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4255 if (prev_m->pulse() == ms->pulse()) {
4256 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4257 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4260 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4261 if (prev_t->pulse() == ts->pulse()) {
4262 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4263 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4271 recompute_map (_metrics);
4273 Metrics::const_iterator d = old_metrics.begin();
4274 while (d != old_metrics.end()) {
4278 old_metrics.clear ();
4281 PropertyChanged (PropertyChange ());
4287 TempoMap::dump (std::ostream& o) const
4289 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4290 const MeterSection* m;
4291 const TempoSection* t;
4292 const TempoSection* prev_t = 0;
4294 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4296 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4297 o << "Tempo @ " << *i << t->note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4298 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4299 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4300 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4302 o << " current : " << t->note_types_per_minute()
4303 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4304 o << " previous : " << prev_t->note_types_per_minute()
4305 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4306 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4307 << " | " << prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())
4308 << " | " << frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))
4309 << " | " << prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()) << std::endl;
4312 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4313 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4314 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4315 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4318 o << "------" << std::endl;
4322 TempoMap::n_tempos() const
4324 Glib::Threads::RWLock::ReaderLock lm (lock);
4327 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4328 if ((*i)->is_tempo()) {
4337 TempoMap::n_meters() const
4339 Glib::Threads::RWLock::ReaderLock lm (lock);
4342 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4343 if (!(*i)->is_tempo()) {
4352 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4354 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4355 if ((*i)->frame() >= where && !(*i)->initial ()) {
4359 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4360 gui_move_meter (ms, (*i)->frame() + amount);
4363 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4364 gui_move_tempo (ts, (*i)->frame() + amount, 0);
4369 PropertyChanged (PropertyChange ());
4373 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4377 std::list<MetricSection*> metric_kill_list;
4379 TempoSection* last_tempo = NULL;
4380 MeterSection* last_meter = NULL;
4381 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4382 bool meter_after = false; // is there a meter marker likewise?
4384 Glib::Threads::RWLock::WriterLock lm (lock);
4385 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4386 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4387 metric_kill_list.push_back(*i);
4388 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4391 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4395 else if ((*i)->frame() >= where) {
4396 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4397 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4398 if ((*i)->frame() == where) {
4399 // marker was immediately after end of range
4400 tempo_after = dynamic_cast<TempoSection*> (*i);
4401 meter_after = dynamic_cast<MeterSection*> (*i);
4407 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4408 if (last_tempo && !tempo_after) {
4409 metric_kill_list.remove(last_tempo);
4410 last_tempo->set_minute (minute_at_frame (where));
4413 if (last_meter && !meter_after) {
4414 metric_kill_list.remove(last_meter);
4415 last_meter->set_minute (minute_at_frame (where));
4419 //remove all the remaining metrics
4420 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4421 _metrics.remove(*i);
4426 recompute_map (_metrics);
4429 PropertyChanged (PropertyChange ());
4433 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4434 * pos can be -ve, if required.
4437 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4439 Glib::Threads::RWLock::ReaderLock lm (lock);
4440 const double frame_qn = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
4442 return frame_at_minute (minute_at_quarter_note_locked (_metrics, frame_qn + beats.to_double()));
4446 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4448 Glib::Threads::RWLock::ReaderLock lm (lock);
4450 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4451 pos_bbt.ticks += op.ticks;
4452 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4454 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4456 pos_bbt.beats += op.beats;
4457 /* the meter in effect will start on the bar */
4458 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();
4459 while (pos_bbt.beats >= divisions_per_bar + 1) {
4461 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4462 pos_bbt.beats -= divisions_per_bar;
4464 pos_bbt.bars += op.bars;
4466 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4469 /** Count the number of beats that are equivalent to distance when going forward,
4473 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4475 Glib::Threads::RWLock::ReaderLock lm (lock);
4477 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4481 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4487 operator<< (std::ostream& o, const Meter& m) {
4488 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4492 operator<< (std::ostream& o, const Tempo& t) {
4493 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4497 operator<< (std::ostream& o, const MetricSection& section) {
4499 o << "MetricSection @ " << section.frame() << ' ';
4501 const TempoSection* ts;
4502 const MeterSection* ms;
4504 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4505 o << *((const Tempo*) ts);
4506 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4507 o << *((const Meter*) ms);