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);
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.beats_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;
101 _legacy_bbt = BBT_Time (0, 0, 0);
103 if ((prop = node.property ("start")) != 0) {
104 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
108 /* legacy session - start used to be in bbt*/
111 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
115 if ((prop = node.property ("pulse")) != 0) {
116 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
117 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
123 if ((prop = node.property ("frame")) != 0) {
124 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
125 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
127 set_minute (minute_at_frame (frame));
131 if ((prop = node.property ("minute")) != 0) {
132 if (sscanf (prop->value().c_str(), "%lf", &minute) != 1) {
133 error << _("TempoSection XML node has an illegal \"minute\" value") << endmsg;
139 if ((prop = node.property ("beats-per-minute")) == 0) {
140 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
141 throw failed_constructor();
144 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
145 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
146 throw failed_constructor();
149 if ((prop = node.property ("note-type")) == 0) {
150 /* older session, make note type be quarter by default */
153 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
154 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
155 throw failed_constructor();
159 if ((prop = node.property ("movable")) == 0) {
160 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
161 throw failed_constructor();
164 set_movable (string_is_affirmative (prop->value()));
166 if ((prop = node.property ("active")) == 0) {
167 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
170 set_active (string_is_affirmative (prop->value()));
173 if ((prop = node.property ("tempo-type")) == 0) {
176 _type = Type (string_2_enum (prop->value(), _type));
179 if ((prop = node.property ("lock-style")) == 0) {
181 set_position_lock_style (MusicTime);
183 set_position_lock_style (AudioTime);
186 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
189 if ((prop = node.property ("locked-to-meter")) == 0) {
190 set_locked_to_meter (false);
192 set_locked_to_meter (string_is_affirmative (prop->value()));
197 TempoSection::get_state() const
199 XMLNode *root = new XMLNode (xml_state_node_name);
203 snprintf (buf, sizeof (buf), "%lf", pulse());
204 root->add_property ("pulse", buf);
205 snprintf (buf, sizeof (buf), "%li", frame());
206 root->add_property ("frame", buf);
207 snprintf (buf, sizeof (buf), "%lf", minute());
208 root->add_property ("minute", buf);
209 snprintf (buf, sizeof (buf), "%lf", _beats_per_minute);
210 root->add_property ("beats-per-minute", buf);
211 snprintf (buf, sizeof (buf), "%lf", _note_type);
212 root->add_property ("note-type", buf);
213 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
214 root->add_property ("movable", buf);
215 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
216 root->add_property ("active", buf);
217 root->add_property ("tempo-type", enum_2_string (_type));
218 root->add_property ("lock-style", enum_2_string (position_lock_style()));
219 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
225 TempoSection::set_type (Type type)
230 /** returns the tempo on note types per minute at the zero-based (relative to session) minute.
233 TempoSection::tempo_at_minute (const double& m) const
236 if (_type == Constant || _c_func == 0.0) {
237 return beats_per_minute();
240 return _tempo_at_time (m - minute());
243 /** returns the zero-based minute (relative to session)
244 where the tempo in note types per minute occurs in this section.
245 pulse p is only used for constant tempi.
246 note that the tempo map may have multiple such values.
249 TempoSection::minute_at_tempo (const double& bpm, const double& p) const
251 if (_type == Constant || _c_func == 0.0) {
252 return (((p - pulse()) * note_type()) / beats_per_minute()) + minute();
255 return _time_at_tempo (bpm) + minute();
258 /** returns the tempo in note types per minute at the supplied pulse.
261 TempoSection::tempo_at_pulse (const double& p) const
264 if (_type == Constant || _c_func == 0.0) {
265 return beats_per_minute();
268 return _tempo_at_pulse (p - pulse());
271 /** returns the pulse where the tempo in note types per minute occurs given minute m.
272 minute m is only used for constant tempi.
273 note that the session tempo map may have multiple locations where a given tempo occurs.
276 TempoSection::pulse_at_tempo (const double& bpm, const double& m) const
278 if (_type == Constant || _c_func == 0.0) {
279 const double pulses = (((m - minute()) * beats_per_minute()) / note_type()) + pulse();
283 return _pulse_at_tempo (bpm) + pulse();
286 /** returns the pulse at the supplied session-relative minute.
289 TempoSection::pulse_at_minute (const double& m) const
291 if (_type == Constant || _c_func == 0.0) {
292 return (((m - minute()) * beats_per_minute()) / _note_type) + pulse();
295 return _pulse_at_time (m - minute()) + pulse();
298 /** returns the minute (relative to session start) at the supplied pulse.
301 TempoSection::minute_at_pulse (const double& p) const
303 if (_type == Constant || _c_func == 0.0) {
304 return (((p - pulse()) * note_type()) / beats_per_minute()) + minute();
307 return _time_at_pulse (p - pulse()) + minute();
315 Tt----|-----------------*|
316 Ta----|--------------|* |
322 _______________|___|____
323 time a t (next tempo)
326 Duration in beats at time a is the integral of some Tempo function.
327 In our case, the Tempo function (Tempo at time t) is
330 with function constant
335 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
336 b(t) = T0(e^(ct) - 1) / c
338 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:
339 t(b) = log((c.b / T0) + 1) / c
341 The time t at which Tempo T occurs is a as above:
342 t(T) = log(T / T0) / c
344 The beat at which a Tempo T occurs is:
347 The Tempo at which beat b occurs is:
350 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
351 Our problem is that we usually don't know t.
352 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.
353 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
354 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
356 By substituting our expanded t as a in the c function above, our problem is reduced to:
357 c = T0 (e^(log (Ta / T0)) - 1) / b
359 Of course the word 'beat' has been left loosely defined above.
360 In music, a beat is defined by the musical pulse (which comes from the tempo)
361 and the meter in use at a particular time (how many pulse divisions there are in one bar).
362 It would be more accurate to substitute the work 'pulse' for 'beat' above.
366 We can now store c for future time calculations.
367 If the following tempo section (the one that defines c in conjunction with this one)
368 is changed or moved, c is no longer valid.
370 The public methods are session-relative.
372 Most of this stuff is taken from this paper:
375 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
378 Zurich University of Arts
379 Institute for Computer Music and Sound Technology
381 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
386 compute this ramp's function constant from some tempo-pulse point
387 end tempo (in note types per minute)
388 duration (pulses into global start) of some other position.
391 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse) const
393 double const log_tempo_ratio = log (end_bpm / beats_per_minute());
394 return (beats_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
397 /* compute the function constant from some tempo-time point.
398 tempo (note types/min.)
399 distance (in minutes) from session origin
402 TempoSection::compute_c_func_minute (const double& end_bpm, const double& end_minute) const
404 return c_func (end_bpm, end_minute - minute());
407 /* position function */
409 TempoSection::a_func (double end_bpm, double c_func) const
411 return log (end_bpm / beats_per_minute()) / c_func;
414 /*function constant*/
416 TempoSection::c_func (double end_bpm, double end_time) const
418 return log (end_bpm / beats_per_minute()) / end_time;
421 /* tempo in note types per minute at time in minutes */
423 TempoSection::_tempo_at_time (const double& time) const
425 return exp (_c_func * time) * beats_per_minute();
428 /* time in minutes at tempo in note types per minute */
430 TempoSection::_time_at_tempo (const double& tempo) const
432 return log (tempo / beats_per_minute()) / _c_func;
435 /* pulse at tempo in note types per minute */
437 TempoSection::_pulse_at_tempo (const double& tempo) const
439 return ((tempo - beats_per_minute()) / _c_func) / _note_type;
442 /* tempo in note types per minute at pulse */
444 TempoSection::_tempo_at_pulse (const double& pulse) const
446 return (pulse * _note_type * _c_func) + beats_per_minute();
449 /* pulse at time in minutes */
451 TempoSection::_pulse_at_time (const double& time) const
453 return expm1 (_c_func * time) * (beats_per_minute() / (_c_func * _note_type));
456 /* time in minutes at pulse */
458 TempoSection::_time_at_pulse (const double& pulse) const
460 return log1p ((_c_func * pulse * _note_type) / beats_per_minute()) / _c_func;
463 /***********************************************************************/
465 const string MeterSection::xml_state_node_name = "Meter";
467 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
468 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
470 XMLProperty const * prop;
475 framepos_t frame = 0;
476 pair<double, BBT_Time> start;
479 if ((prop = node.property ("start")) != 0) {
480 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
484 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
486 /* legacy session - start used to be in bbt*/
487 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
492 if ((prop = node.property ("pulse")) != 0) {
493 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
494 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
499 if ((prop = node.property ("beat")) != 0) {
500 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
501 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
507 if ((prop = node.property ("bbt")) == 0) {
508 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
509 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
513 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
514 throw failed_constructor();
520 if ((prop = node.property ("frame")) != 0) {
521 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
522 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
524 set_minute (minute_at_frame (frame));
527 if ((prop = node.property ("minute")) != 0) {
528 if (sscanf (prop->value().c_str(), "%lf", &minute) != 1) {
529 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
535 /* beats-per-bar is old; divisions-per-bar is new */
537 if ((prop = node.property ("divisions-per-bar")) == 0) {
538 if ((prop = node.property ("beats-per-bar")) == 0) {
539 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
540 throw failed_constructor();
543 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
544 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
545 throw failed_constructor();
548 if ((prop = node.property ("note-type")) == 0) {
549 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
550 throw failed_constructor();
552 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
553 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
554 throw failed_constructor();
557 if ((prop = node.property ("movable")) == 0) {
558 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
559 throw failed_constructor();
562 set_movable (string_is_affirmative (prop->value()));
564 if ((prop = node.property ("lock-style")) == 0) {
565 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
567 set_position_lock_style (MusicTime);
569 set_position_lock_style (AudioTime);
572 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
577 MeterSection::get_state() const
579 XMLNode *root = new XMLNode (xml_state_node_name);
583 snprintf (buf, sizeof (buf), "%lf", pulse());
584 root->add_property ("pulse", buf);
585 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
589 root->add_property ("bbt", buf);
590 snprintf (buf, sizeof (buf), "%lf", beat());
591 root->add_property ("beat", buf);
592 snprintf (buf, sizeof (buf), "%lf", _note_type);
593 root->add_property ("note-type", buf);
594 snprintf (buf, sizeof (buf), "%li", frame());
595 root->add_property ("frame", buf);
596 snprintf (buf, sizeof (buf), "%lf", minute());
597 root->add_property ("minute", buf);
598 root->add_property ("lock-style", enum_2_string (position_lock_style()));
599 snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
600 root->add_property ("divisions-per-bar", buf);
601 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
602 root->add_property ("movable", buf);
607 /***********************************************************************/
611 Tempo determines the rate of musical pulse determined by its components
612 note types per minute - the rate per minute of the whole note divisor _note_type
613 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
614 Meter divides the musical pulse into measures and beats according to its components
618 TempoSection - translates between time, musical pulse and tempo.
619 has a musical location in whole notes (pulses).
620 has a time location in minutes.
621 Note that 'beats' in Tempo::beats_per_minute() are in fact note types per minute.
622 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
624 MeterSection - translates between BBT, meter-based beat and musical pulse.
625 has a musical location in whole notes (pulses)
626 has a musical location in meter-based beats
627 has a musical location in BBT time
628 has a time location expressed in minutes.
630 TempoSection and MeterSection may be locked to either audio or music (position lock style).
631 The lock style determines the location type to be kept as a reference when location is recalculated.
633 The first tempo and meter are special. they must move together, and are locked to audio.
634 Audio locked tempi which lie before the first meter are made inactive.
636 Recomputing the map is the process where the 'missing' location types are calculated.
637 We construct the tempo map by first using the locked location type of each section
638 to determine non-locked location types (pulse or minute position).
639 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
641 Having done this, we can now traverse the Metrics list by pulse or minute
642 to query its relevant meter/tempo.
644 It is important to keep the _metrics in an order that makes sense.
645 Because ramped MusicTime and AudioTime tempos can interact with each other,
646 reordering is frequent. Care must be taken to keep _metrics in a solved state.
647 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
651 Music and audio-locked objects may seem interchangeable on the surface, but when translating
652 between audio samples and beat, remember that a sample is only a quantised approximation
653 of the actual time (in minutes) of a beat.
654 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
655 mean that this frame is the actual location in time of 1|3|0.
657 You cannot use a frame measurement to determine beat distance except under special circumstances
658 (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).
660 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
661 sample space the user is operating at to be translated correctly to the object.
663 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
664 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
665 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
667 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
669 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
670 The result is rounded to audio frames.
671 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
674 frame_at_beat (beat_at_frame (frame)) == frame
676 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
678 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
679 frames_between_quarter_notes () eliminats this effect when determining time duration
680 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
682 The above pointless example could instead do:
683 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
685 The Shaggs - Things I Wonder
686 https://www.youtube.com/watch?v=9wQK6zMJOoQ
689 struct MetricSectionSorter {
690 bool operator() (const MetricSection* a, const MetricSection* b) {
691 return a->pulse() < b->pulse();
695 struct MetricSectionFrameSorter {
696 bool operator() (const MetricSection* a, const MetricSection* b) {
697 return a->frame() < b->frame();
701 TempoMap::TempoMap (framecnt_t fr)
704 BBT_Time start (1, 1, 0);
706 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime, fr);
707 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
709 t->set_movable (false);
710 m->set_movable (false);
712 /* note: frame time is correct (zero) for both of these */
714 _metrics.push_back (t);
715 _metrics.push_back (m);
719 TempoMap::~TempoMap ()
721 Metrics::const_iterator d = _metrics.begin();
722 while (d != _metrics.end()) {
730 TempoMap::frame_at_minute (const double time) const
732 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
736 TempoMap::minute_at_frame (const framepos_t frame) const
738 return (frame / (double) _frame_rate) / 60.0;
742 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
744 bool removed = false;
747 Glib::Threads::RWLock::WriterLock lm (lock);
748 if ((removed = remove_tempo_locked (tempo))) {
749 if (complete_operation) {
750 recompute_map (_metrics);
755 if (removed && complete_operation) {
756 PropertyChanged (PropertyChange ());
761 TempoMap::remove_tempo_locked (const TempoSection& tempo)
765 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
766 if (dynamic_cast<TempoSection*> (*i) != 0) {
767 if (tempo.frame() == (*i)->frame()) {
768 if ((*i)->movable()) {
781 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
783 bool removed = false;
786 Glib::Threads::RWLock::WriterLock lm (lock);
787 if ((removed = remove_meter_locked (tempo))) {
788 if (complete_operation) {
789 recompute_map (_metrics);
794 if (removed && complete_operation) {
795 PropertyChanged (PropertyChange ());
800 TempoMap::remove_meter_locked (const MeterSection& meter)
803 if (meter.position_lock_style() == AudioTime) {
804 /* remove meter-locked tempo */
805 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
807 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
808 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
817 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
818 if (dynamic_cast<MeterSection*> (*i) != 0) {
819 if (meter.frame() == (*i)->frame()) {
820 if ((*i)->movable()) {
833 TempoMap::do_insert (MetricSection* section)
835 bool need_add = true;
836 /* we only allow new meters to be inserted on beat 1 of an existing
840 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
842 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
844 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
845 corrected.second.beats = 1;
846 corrected.second.ticks = 0;
847 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
848 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
849 m->bbt(), corrected.second) << endmsg;
850 //m->set_pulse (corrected);
854 /* Look for any existing MetricSection that is of the same type and
855 in the same bar as the new one, and remove it before adding
856 the new one. Note that this means that if we find a matching,
857 existing section, we can break out of the loop since we're
858 guaranteed that there is only one such match.
861 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
863 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
864 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
865 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
866 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
868 if (tempo && insert_tempo) {
871 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
872 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
874 if (!tempo->movable()) {
876 /* can't (re)move this section, so overwrite
877 * its data content (but not its properties as
881 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
882 (*i)->set_position_lock_style (AudioTime);
884 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
885 t->set_type (insert_tempo->type());
895 } else if (meter && insert_meter) {
899 bool const ipm = insert_meter->position_lock_style() == MusicTime;
901 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
903 if (!meter->movable()) {
905 /* can't (re)move this section, so overwrite
906 * its data content (but not its properties as
910 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
911 (*i)->set_position_lock_style (AudioTime);
921 /* non-matching types, so we don't care */
925 /* Add the given MetricSection, if we didn't just reset an existing
930 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
931 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
934 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
935 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
938 bool const ipm = insert_meter->position_lock_style() == MusicTime;
939 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
944 } else if (insert_tempo) {
945 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
946 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
949 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
950 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
957 _metrics.insert (i, section);
958 //dump (_metrics, std::cout);
961 /* user supplies the exact pulse if pls == MusicTime */
963 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
965 TempoSection* ts = 0;
967 Glib::Threads::RWLock::WriterLock lm (lock);
968 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
972 PropertyChanged (PropertyChange ());
978 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
980 const bool locked_to_meter = ts.locked_to_meter();
983 Glib::Threads::RWLock::WriterLock lm (lock);
984 TempoSection& first (first_tempo());
985 if (ts.frame() != first.frame()) {
986 remove_tempo_locked (ts);
987 add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter);
989 first.set_type (type);
990 first.set_pulse (0.0);
991 first.set_minute (minute_at_frame (frame));
992 first.set_position_lock_style (AudioTime);
994 /* cannot move the first tempo section */
995 *static_cast<Tempo*>(&first) = tempo;
996 recompute_map (_metrics);
1001 PropertyChanged (PropertyChange ());
1005 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1006 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1008 TempoSection* t = new TempoSection (pulse, minute, tempo.beats_per_minute(), tempo.note_type(), type, pls, _frame_rate);
1009 t->set_locked_to_meter (locked_to_meter);
1010 bool solved = false;
1015 if (pls == AudioTime) {
1016 solved = solve_map_minute (_metrics, t, t->minute());
1018 solved = solve_map_pulse (_metrics, t, t->pulse());
1020 recompute_meters (_metrics);
1023 if (!solved && recompute) {
1024 recompute_map (_metrics);
1031 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, PositionLockStyle pls)
1033 MeterSection* m = 0;
1035 Glib::Threads::RWLock::WriterLock lm (lock);
1036 m = add_meter_locked (meter, beat, where, pls, true);
1041 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1042 dump (_metrics, std::cerr);
1046 PropertyChanged (PropertyChange ());
1051 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, PositionLockStyle pls)
1054 Glib::Threads::RWLock::WriterLock lm (lock);
1055 const double beat = beat_at_bbt_locked (_metrics, where);
1058 remove_meter_locked (ms);
1059 add_meter_locked (meter, beat, where, pls, true);
1061 MeterSection& first (first_meter());
1062 TempoSection& first_t (first_tempo());
1063 /* cannot move the first meter section */
1064 *static_cast<Meter*>(&first) = meter;
1065 first.set_position_lock_style (AudioTime);
1066 first.set_pulse (0.0);
1067 //first.set_minute (minute_at_frame (frame));
1068 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1069 first.set_beat (beat);
1070 first_t.set_minute (first.minute());
1071 first_t.set_pulse (0.0);
1072 first_t.set_position_lock_style (AudioTime);
1073 recompute_map (_metrics);
1077 PropertyChanged (PropertyChange ());
1081 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, PositionLockStyle pls, bool recompute)
1083 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1084 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1085 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1086 TempoSection* mlt = 0;
1088 if (pls == AudioTime) {
1089 /* add meter-locked tempo */
1090 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, time_minutes, TempoSection::Ramp, AudioTime, true, true);
1098 MeterSection* new_meter = new MeterSection (pulse, time_minutes, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1099 bool solved = false;
1101 do_insert (new_meter);
1105 if (pls == AudioTime) {
1106 solved = solve_map_minute (_metrics, new_meter, time_minutes);
1108 solved = solve_map_bbt (_metrics, new_meter, where);
1109 /* required due to resetting the pulse of meter-locked tempi above.
1110 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1111 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1113 recompute_map (_metrics);
1117 if (!solved && recompute) {
1118 /* if this has failed to solve, there is little we can do other than to ensure that
1119 the new map is recalculated.
1121 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1122 recompute_map (_metrics);
1129 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1131 Tempo newtempo (beats_per_minute, note_type);
1134 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1135 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1140 Glib::Threads::RWLock::WriterLock lm (lock);
1141 *((Tempo*) t) = newtempo;
1142 recompute_map (_metrics);
1144 PropertyChanged (PropertyChange ());
1151 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1153 Tempo newtempo (beats_per_minute, note_type);
1156 TempoSection* first;
1157 Metrics::iterator i;
1159 /* find the TempoSection immediately preceding "where"
1162 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1164 if ((*i)->frame() > where) {
1170 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1183 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1193 Glib::Threads::RWLock::WriterLock lm (lock);
1194 /* cannot move the first tempo section */
1195 *((Tempo*)prev) = newtempo;
1196 recompute_map (_metrics);
1199 PropertyChanged (PropertyChange ());
1203 TempoMap::first_meter () const
1205 const MeterSection *m = 0;
1207 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1208 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1213 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1214 abort(); /*NOTREACHED*/
1219 TempoMap::first_meter ()
1221 MeterSection *m = 0;
1223 /* CALLER MUST HOLD LOCK */
1225 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1226 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1231 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1232 abort(); /*NOTREACHED*/
1237 TempoMap::first_tempo () const
1239 const TempoSection *t = 0;
1241 /* CALLER MUST HOLD LOCK */
1243 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1244 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1248 if (!t->movable()) {
1254 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1255 abort(); /*NOTREACHED*/
1260 TempoMap::first_tempo ()
1262 TempoSection *t = 0;
1264 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1265 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1269 if (!t->movable()) {
1275 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1276 abort(); /*NOTREACHED*/
1280 TempoMap::recompute_tempi (Metrics& metrics)
1282 TempoSection* prev_t = 0;
1284 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1287 if ((*i)->is_tempo()) {
1288 t = static_cast<TempoSection*> (*i);
1292 if (!t->movable()) {
1300 if (t->position_lock_style() == AudioTime) {
1301 prev_t->set_c_func (prev_t->compute_c_func_minute (t->beats_per_minute(), t->minute()));
1302 if (!t->locked_to_meter()) {
1303 t->set_pulse (prev_t->pulse_at_tempo (t->beats_per_minute(), t->minute()));
1307 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->beats_per_minute(), t->pulse()));
1308 t->set_minute (prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()));
1315 prev_t->set_c_func (0.0);
1318 /* tempos must be positioned correctly.
1319 the current approach is to use a meter's bbt time as its base position unit.
1320 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1321 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1324 TempoMap::recompute_meters (Metrics& metrics)
1326 MeterSection* meter = 0;
1327 MeterSection* prev_m = 0;
1329 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1330 if (!(*mi)->is_tempo()) {
1331 meter = static_cast<MeterSection*> (*mi);
1332 if (meter->position_lock_style() == AudioTime) {
1334 pair<double, BBT_Time> b_bbt;
1335 TempoSection* meter_locked_tempo = 0;
1336 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1338 if ((*ii)->is_tempo()) {
1339 t = static_cast<TempoSection*> (*ii);
1340 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1341 meter_locked_tempo = t;
1348 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1349 if (beats + prev_m->beat() != meter->beat()) {
1350 /* reordering caused a bbt change */
1351 b_bbt = make_pair (beats + prev_m->beat()
1352 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1353 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1355 } else if (meter->movable()) {
1356 b_bbt = make_pair (meter->beat(), meter->bbt());
1357 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1360 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1362 if (meter_locked_tempo) {
1363 meter_locked_tempo->set_pulse (pulse);
1365 meter->set_beat (b_bbt);
1366 meter->set_pulse (pulse);
1371 pair<double, BBT_Time> b_bbt;
1373 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1374 if (beats + prev_m->beat() != meter->beat()) {
1375 /* reordering caused a bbt change */
1376 b_bbt = make_pair (beats + prev_m->beat()
1377 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1379 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1381 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1383 /* shouldn't happen - the first is audio-locked */
1384 pulse = pulse_at_beat_locked (metrics, meter->beat());
1385 b_bbt = make_pair (meter->beat(), meter->bbt());
1388 meter->set_beat (b_bbt);
1389 meter->set_pulse (pulse);
1390 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1399 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1401 /* CALLER MUST HOLD WRITE LOCK */
1403 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1406 /* silly call from Session::process() during startup
1411 recompute_tempi (metrics);
1412 recompute_meters (metrics);
1416 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1418 Glib::Threads::RWLock::ReaderLock lm (lock);
1419 TempoMetric m (first_meter(), first_tempo());
1421 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1422 at something, because we insert the default tempo and meter during
1423 TempoMap construction.
1425 now see if we can find better candidates.
1428 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1430 if ((*i)->frame() > frame) {
1444 /* XX meters only */
1446 TempoMap::metric_at (BBT_Time bbt) const
1448 Glib::Threads::RWLock::ReaderLock lm (lock);
1449 TempoMetric m (first_meter(), first_tempo());
1451 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1452 at something, because we insert the default tempo and meter during
1453 TempoMap construction.
1455 now see if we can find better candidates.
1458 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1460 if (!(*i)->is_tempo()) {
1461 mw = static_cast<MeterSection*> (*i);
1462 BBT_Time section_start (mw->bbt());
1464 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1475 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1476 * @param frame The session frame position.
1477 * @return The beat duration according to the tempo map at the supplied frame.
1479 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1480 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1482 * This function uses both tempo and meter.
1485 TempoMap::beat_at_frame (const framecnt_t& frame) const
1487 Glib::Threads::RWLock::ReaderLock lm (lock);
1489 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1492 /* This function uses both tempo and meter.*/
1494 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1496 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1497 MeterSection* prev_m = 0;
1498 MeterSection* next_m = 0;
1500 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1501 if (!(*i)->is_tempo()) {
1502 if (prev_m && (*i)->minute() > minute) {
1503 next_m = static_cast<MeterSection*> (*i);
1506 prev_m = static_cast<MeterSection*> (*i);
1510 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1512 /* audio locked meters fake their beat */
1513 if (next_m && next_m->beat() < beat) {
1514 return next_m->beat();
1520 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1521 * @param beat The BBT (meter-based) beat.
1522 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1524 * This function uses both tempo and meter.
1527 TempoMap::frame_at_beat (const double& beat) const
1529 Glib::Threads::RWLock::ReaderLock lm (lock);
1531 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1534 /* meter & tempo section based */
1536 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1538 MeterSection* prev_m = 0;
1539 TempoSection* prev_t = 0;
1543 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1544 if (!(*i)->is_tempo()) {
1545 m = static_cast<MeterSection*> (*i);
1546 if (prev_m && m->beat() > beat) {
1555 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1556 if ((*i)->is_tempo()) {
1557 t = static_cast<TempoSection*> (*i);
1558 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1566 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1569 /** Returns a Tempo corresponding to the supplied frame position.
1570 * @param frame The audio frame.
1571 * @return a Tempo according to the tempo map at the supplied frame.
1575 TempoMap::tempo_at_frame (const framepos_t& frame) const
1577 Glib::Threads::RWLock::ReaderLock lm (lock);
1579 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1583 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1585 TempoSection* prev_t = 0;
1589 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1590 if ((*i)->is_tempo()) {
1591 t = static_cast<TempoSection*> (*i);
1595 if ((prev_t) && t->minute() > minute) {
1596 /* t is the section past frame */
1597 const double ret_bpm = prev_t->tempo_at_minute (minute);
1598 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
1605 const double ret = prev_t->beats_per_minute();
1606 const Tempo ret_tempo (ret, prev_t->note_type ());
1611 /** returns the frame at which the supplied tempo occurs, or
1612 * the frame of the last tempo section (search exhausted)
1613 * only the position of the first occurence will be returned
1617 TempoMap::frame_at_tempo (const Tempo& tempo) const
1619 Glib::Threads::RWLock::ReaderLock lm (lock);
1621 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1625 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1627 TempoSection* prev_t = 0;
1628 const double tempo_bpm = tempo.beats_per_minute();
1630 Metrics::const_iterator i;
1632 for (i = metrics.begin(); i != metrics.end(); ++i) {
1634 if ((*i)->is_tempo()) {
1635 t = static_cast<TempoSection*> (*i);
1641 const double t_bpm = t->beats_per_minute();
1643 if (t_bpm == tempo_bpm) {
1648 const double prev_t_bpm = prev_t->beats_per_minute();
1650 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1651 return prev_t->minute_at_tempo (tempo_bpm, prev_t->pulse());
1658 return prev_t->minute();
1662 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1664 TempoSection* prev_t = 0;
1668 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1669 if ((*i)->is_tempo()) {
1670 t = static_cast<TempoSection*> (*i);
1674 if ((prev_t) && t->pulse() > pulse) {
1675 /* t is the section past frame */
1676 const double ret_bpm = prev_t->tempo_at_pulse (pulse);
1677 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
1684 const double ret = prev_t->beats_per_minute();
1685 const Tempo ret_tempo (ret, prev_t->note_type ());
1691 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1693 TempoSection* prev_t = 0;
1694 const double tempo_bpm = tempo.beats_per_minute();
1696 Metrics::const_iterator i;
1698 for (i = metrics.begin(); i != metrics.end(); ++i) {
1700 if ((*i)->is_tempo()) {
1701 t = static_cast<TempoSection*> (*i);
1707 const double t_bpm = t->beats_per_minute();
1709 if (t_bpm == tempo_bpm) {
1714 const double prev_t_bpm = prev_t->beats_per_minute();
1716 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1717 return prev_t->pulse_at_tempo (tempo_bpm, prev_t->minute());
1724 return prev_t->pulse();
1727 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1728 * @param qn the position in quarter note beats.
1729 * @return the Tempo at the supplied quarter-note.
1732 TempoMap::tempo_at_quarter_note (const double& qn) const
1734 Glib::Threads::RWLock::ReaderLock lm (lock);
1736 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1739 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1740 * @param tempo the tempo.
1741 * @return the position in quarter-note beats where the map bpm
1742 * is equal to that of the Tempo. currently ignores note_type.
1745 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1747 Glib::Threads::RWLock::ReaderLock lm (lock);
1749 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1752 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1753 * @param metrics the list of metric sections used to calculate the pulse.
1754 * @param beat The BBT (meter-based) beat.
1755 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1757 * a pulse or whole note is the base musical position of a MetricSection.
1758 * it is equivalent to four quarter notes.
1762 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1764 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1766 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1769 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1770 * @param metrics the list of metric sections used to calculate the beat.
1771 * @param pulse the whole-note pulse.
1772 * @return the meter-based beat at the supplied whole-note pulse.
1774 * a pulse or whole note is the base musical position of a MetricSection.
1775 * it is equivalent to four quarter notes.
1778 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1780 MeterSection* prev_m = 0;
1782 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1784 if (!(*i)->is_tempo()) {
1785 m = static_cast<MeterSection*> (*i);
1786 if (prev_m && m->pulse() > pulse) {
1793 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1797 /* tempo section based */
1799 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1801 /* HOLD (at least) THE READER LOCK */
1802 TempoSection* prev_t = 0;
1804 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1806 if ((*i)->is_tempo()) {
1807 t = static_cast<TempoSection*> (*i);
1811 if (prev_t && t->minute() > minute) {
1812 /*the previous ts is the one containing the frame */
1813 const double ret = prev_t->pulse_at_minute (minute);
1814 /* audio locked section in new meter*/
1815 if (t->pulse() < ret) {
1824 /* treated as constant for this ts */
1825 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->beats_per_minute()) / prev_t->note_type();
1827 return pulses_in_section + prev_t->pulse();
1830 /* tempo section based */
1832 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1834 /* HOLD THE READER LOCK */
1836 const TempoSection* prev_t = 0;
1838 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1841 if ((*i)->is_tempo()) {
1842 t = static_cast<TempoSection*> (*i);
1846 if (prev_t && t->pulse() > pulse) {
1847 return prev_t->minute_at_pulse (pulse);
1853 /* must be treated as constant, irrespective of _type */
1854 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->beats_per_minute();
1856 return dtime + prev_t->minute();
1859 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1860 * @param bbt The BBT time (meter-based).
1861 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1865 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1867 Glib::Threads::RWLock::ReaderLock lm (lock);
1868 return beat_at_bbt_locked (_metrics, bbt);
1873 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1875 /* CALLER HOLDS READ LOCK */
1877 MeterSection* prev_m = 0;
1879 /* because audio-locked meters have 'fake' integral beats,
1880 there is no pulse offset here.
1884 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1885 if (!(*i)->is_tempo()) {
1886 m = static_cast<MeterSection*> (*i);
1888 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1889 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1897 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1898 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1899 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1904 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
1905 * @param beat The BBT (meter-based) beat.
1906 * @return The BBT time (meter-based) at the supplied meter-based beat.
1910 TempoMap::bbt_at_beat (const double& beat)
1912 Glib::Threads::RWLock::ReaderLock lm (lock);
1913 return bbt_at_beat_locked (_metrics, beat);
1917 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1919 /* CALLER HOLDS READ LOCK */
1920 MeterSection* prev_m = 0;
1921 const double beats = max (0.0, b);
1923 MeterSection* m = 0;
1925 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1926 if (!(*i)->is_tempo()) {
1927 m = static_cast<MeterSection*> (*i);
1929 if (m->beat() > beats) {
1930 /* this is the meter after the one our beat is on*/
1939 const double beats_in_ms = beats - prev_m->beat();
1940 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1941 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1942 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1943 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1947 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1948 ret.beats = (uint32_t) floor (remaining_beats);
1949 ret.bars = total_bars;
1951 /* 0 0 0 to 1 1 0 - based mapping*/
1955 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1957 ret.ticks -= BBT_Time::ticks_per_beat;
1960 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1968 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
1969 * @param bbt The BBT time (meter-based).
1970 * @return the quarter note beat at the supplied BBT time
1972 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
1974 * while the input uses meter, the output does not.
1977 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
1979 Glib::Threads::RWLock::ReaderLock lm (lock);
1981 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
1985 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
1987 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1990 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
1993 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
1997 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1999 /* CALLER HOLDS READ LOCK */
2001 MeterSection* prev_m = 0;
2003 /* because audio-locked meters have 'fake' integral beats,
2004 there is no pulse offset here.
2008 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2009 if (!(*i)->is_tempo()) {
2010 m = static_cast<MeterSection*> (*i);
2012 if (m->bbt().bars > bbt.bars) {
2020 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2021 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2022 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2027 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2028 * @param qn the quarter-note beat.
2029 * @return The BBT time (meter-based) at the supplied meter-based beat.
2031 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2035 TempoMap::bbt_at_quarter_note (const double& qn)
2037 Glib::Threads::RWLock::ReaderLock lm (lock);
2039 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2042 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2043 * @param metrics The list of metric sections used to determine the result.
2044 * @param pulse The whole-note pulse.
2045 * @return The BBT time at the supplied whole-note pulse.
2047 * a pulse or whole note is the basic musical position of a MetricSection.
2048 * it is equivalent to four quarter notes.
2049 * while the output uses meter, the input does not.
2052 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2054 MeterSection* prev_m = 0;
2056 MeterSection* m = 0;
2058 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2060 if (!(*i)->is_tempo()) {
2061 m = static_cast<MeterSection*> (*i);
2064 double const pulses_to_m = m->pulse() - prev_m->pulse();
2065 if (prev_m->pulse() + pulses_to_m > pulse) {
2066 /* this is the meter after the one our beat is on*/
2075 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2076 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2077 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2078 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2079 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2083 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2084 ret.beats = (uint32_t) floor (remaining_beats);
2085 ret.bars = total_bars;
2087 /* 0 0 0 to 1 1 0 mapping*/
2091 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2093 ret.ticks -= BBT_Time::ticks_per_beat;
2096 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2104 /** Returns the BBT time corresponding to the supplied frame position.
2105 * @param frame the position in audio samples.
2106 * @return the BBT time at the frame position .
2110 TempoMap::bbt_at_frame (framepos_t frame)
2117 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
2120 Glib::Threads::RWLock::ReaderLock lm (lock);
2122 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2126 TempoMap::bbt_at_frame_rt (framepos_t frame)
2128 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2131 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2134 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2138 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2148 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2149 MeterSection* prev_m = 0;
2150 MeterSection* next_m = 0;
2154 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2155 if (!(*i)->is_tempo()) {
2156 m = static_cast<MeterSection*> (*i);
2157 if (prev_m && m->minute() > minute) {
2165 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2167 /* handle frame before first meter */
2168 if (minute < prev_m->minute()) {
2171 /* audio locked meters fake their beat */
2172 if (next_m && next_m->beat() < beat) {
2173 beat = next_m->beat();
2176 beat = max (0.0, beat);
2178 const double beats_in_ms = beat - prev_m->beat();
2179 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2180 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2181 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2182 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2186 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2187 ret.beats = (uint32_t) floor (remaining_beats);
2188 ret.bars = total_bars;
2190 /* 0 0 0 to 1 1 0 - based mapping*/
2194 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2196 ret.ticks -= BBT_Time::ticks_per_beat;
2199 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2207 /** Returns the frame position corresponding to the supplied BBT time.
2208 * @param bbt the position in BBT time.
2209 * @return the frame position at bbt.
2213 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2216 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2220 if (bbt.beats < 1) {
2221 throw std::logic_error ("beats are counted from one");
2223 Glib::Threads::RWLock::ReaderLock lm (lock);
2225 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
2228 /* meter & tempo section based */
2230 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2232 /* HOLD THE READER LOCK */
2234 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2239 * Returns the quarter-note beat position corresponding to the supplied frame.
2241 * @param frame the position in frames.
2242 * @return The quarter-note position of the supplied frame. Ignores meter.
2246 TempoMap::quarter_note_at_frame (const framepos_t frame)
2248 Glib::Threads::RWLock::ReaderLock lm (lock);
2250 const double ret = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
2256 TempoMap::quarter_note_at_minute_locked (const Metrics& metrics, const double minute) const
2258 const double ret = pulse_at_minute_locked (metrics, minute) * 4.0;
2264 TempoMap::quarter_note_at_frame_rt (const framepos_t frame)
2266 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2269 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2272 const double ret = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
2278 * Returns the frame position corresponding to the supplied quarter-note beat.
2280 * @param quarter_note the quarter-note position.
2281 * @return the frame position of the supplied quarter-note. Ignores meter.
2286 TempoMap::frame_at_quarter_note (const double quarter_note)
2288 Glib::Threads::RWLock::ReaderLock lm (lock);
2290 const framepos_t ret = frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note));
2296 TempoMap::minute_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2298 const double ret = minute_at_pulse_locked (metrics, quarter_note / 4.0);
2303 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2304 * @param beat The BBT (meter-based) beat.
2305 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2307 * a quarter-note may be compared with and assigned to Evoral::Beats.
2311 TempoMap::quarter_note_at_beat (const double beat)
2313 Glib::Threads::RWLock::ReaderLock lm (lock);
2315 const double ret = quarter_note_at_beat_locked (_metrics, beat);
2321 TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) const
2323 const double ret = pulse_at_beat_locked (metrics, beat) * 4.0;
2328 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2329 * @param quarter_note The position in quarter-note beats.
2330 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2332 * a quarter-note is the musical unit of Evoral::Beats.
2336 TempoMap::beat_at_quarter_note (const double quarter_note)
2338 Glib::Threads::RWLock::ReaderLock lm (lock);
2340 const double ret = beat_at_quarter_note_locked (_metrics, quarter_note);
2346 TempoMap::beat_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2349 return beat_at_pulse_locked (metrics, quarter_note / 4.0);
2352 /** Returns the duration in frames between two supplied quarter-note beat positions.
2353 * @param start the first position in quarter-note beats.
2354 * @param end the end position in quarter-note beats.
2355 * @return the frame distance ober the quarter-note beats duration.
2357 * use this rather than e.g.
2358 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2359 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2363 TempoMap::frames_between_quarter_notes (const double start, const double end)
2365 Glib::Threads::RWLock::ReaderLock lm (lock);
2367 return frame_at_minute (minutes_between_quarter_notes_locked (_metrics, start, end));
2371 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end)
2374 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2378 TempoMap::check_solved (const Metrics& metrics) const
2380 TempoSection* prev_t = 0;
2381 MeterSection* prev_m = 0;
2383 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2386 if ((*i)->is_tempo()) {
2387 t = static_cast<TempoSection*> (*i);
2392 /* check ordering */
2393 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2397 /* precision check ensures tempo and frames align.*/
2398 if (t->frame() != frame_at_minute (prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()))) {
2399 if (!t->locked_to_meter()) {
2404 /* gradient limit - who knows what it should be?
2405 things are also ok (if a little chaotic) without this
2407 if (fabs (prev_t->c_func()) > 1000.0) {
2408 //std::cout << "c : " << prev_t->c_func() << std::endl;
2415 if (!(*i)->is_tempo()) {
2416 m = static_cast<MeterSection*> (*i);
2417 if (prev_m && m->position_lock_style() == AudioTime) {
2418 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2419 const double nascent_m_minute = t->minute_at_pulse (m->pulse());
2420 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2422 if (t && (nascent_m_minute > m->minute() || nascent_m_minute < 0.0)) {
2436 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
2438 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2440 if ((*i)->is_tempo()) {
2441 t = static_cast<TempoSection*> (*i);
2442 if (!t->movable()) {
2443 t->set_active (true);
2446 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
2447 t->set_active (false);
2449 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
2450 t->set_active (true);
2451 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
2460 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2462 TempoSection* prev_t = 0;
2463 TempoSection* section_prev = 0;
2464 double first_m_minute = 0.0;
2466 /* can't move a tempo before the first meter */
2467 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2469 if (!(*i)->is_tempo()) {
2470 m = static_cast<MeterSection*> (*i);
2471 if (!m->movable()) {
2472 first_m_minute = m->minute();
2477 if (section->movable() && minute <= first_m_minute) {
2481 section->set_active (true);
2482 section->set_minute (minute);
2484 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2486 if ((*i)->is_tempo()) {
2487 t = static_cast<TempoSection*> (*i);
2494 section_prev = prev_t;
2495 if (t->locked_to_meter()) {
2500 if (t->position_lock_style() == MusicTime) {
2501 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->beats_per_minute(), t->pulse()));
2502 t->set_minute (prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()));
2504 prev_t->set_c_func (prev_t->compute_c_func_minute (t->beats_per_minute(), t->minute()));
2505 if (!t->locked_to_meter()) {
2506 t->set_pulse (prev_t->pulse_at_tempo (t->beats_per_minute(), t->minute()));
2515 section_prev->set_c_func (section_prev->compute_c_func_minute (section->beats_per_minute(), minute));
2516 if (!section->locked_to_meter()) {
2517 section->set_pulse (section_prev->pulse_at_tempo (section->beats_per_minute(), minute));
2522 recompute_tempi (imaginary);
2524 if (check_solved (imaginary)) {
2527 dunp (imaginary, std::cout);
2531 MetricSectionFrameSorter fcmp;
2532 imaginary.sort (fcmp);
2534 recompute_tempi (imaginary);
2536 if (check_solved (imaginary)) {
2544 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2546 TempoSection* prev_t = 0;
2547 TempoSection* section_prev = 0;
2549 section->set_pulse (pulse);
2551 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2553 if ((*i)->is_tempo()) {
2554 t = static_cast<TempoSection*> (*i);
2558 if (!t->movable()) {
2565 section_prev = prev_t;
2568 if (t->position_lock_style() == MusicTime) {
2569 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->beats_per_minute(), t->pulse()));
2570 t->set_minute (prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()));
2572 prev_t->set_c_func (prev_t->compute_c_func_minute (t->beats_per_minute(), t->minute()));
2573 if (!t->locked_to_meter()) {
2574 t->set_pulse (prev_t->pulse_at_tempo (t->beats_per_minute(), t->minute()));
2583 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->beats_per_minute(), pulse));
2584 section->set_minute (section_prev->minute_at_tempo (section->beats_per_minute(), pulse));
2588 recompute_tempi (imaginary);
2590 if (check_solved (imaginary)) {
2593 dunp (imaginary, std::cout);
2597 MetricSectionSorter cmp;
2598 imaginary.sort (cmp);
2600 recompute_tempi (imaginary);
2602 * XX need a restriction here, but only for this case,
2603 * as audio locked tempos don't interact in the same way.
2605 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2607 * |50 bpm |250 bpm |60 bpm
2608 * drag 250 to the pulse after 60->
2609 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2611 if (check_solved (imaginary)) {
2619 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2621 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2622 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2623 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->minute() >= minute)) {
2627 if (!section->movable()) {
2628 /* lock the first tempo to our first meter */
2629 if (!set_active_tempos (imaginary, section->frame_at_minute (minute))) {
2634 TempoSection* meter_locked_tempo = 0;
2636 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2638 if ((*ii)->is_tempo()) {
2639 t = static_cast<TempoSection*> (*ii);
2640 if ((t->locked_to_meter() || !t->movable()) && t->minute() == section->minute()) {
2641 meter_locked_tempo = t;
2647 if (!meter_locked_tempo) {
2651 MeterSection* prev_m = 0;
2653 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2654 bool solved = false;
2656 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2658 if (!(*i)->is_tempo()) {
2659 m = static_cast<MeterSection*> (*i);
2661 if (prev_m && section->movable()) {
2662 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2663 if (beats + prev_m->beat() < section->beat()) {
2664 /* set the section pulse according to its musical position,
2665 * as an earlier time than this has been requested.
2667 const double new_pulse = ((section->beat() - prev_m->beat())
2668 / prev_m->note_divisor()) + prev_m->pulse();
2670 tempo_copy->set_position_lock_style (MusicTime);
2671 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2672 meter_locked_tempo->set_position_lock_style (MusicTime);
2673 section->set_position_lock_style (MusicTime);
2674 section->set_pulse (new_pulse);
2675 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2676 meter_locked_tempo->set_position_lock_style (AudioTime);
2677 section->set_position_lock_style (AudioTime);
2678 section->set_minute (meter_locked_tempo->minute());
2684 Metrics::const_iterator d = future_map.begin();
2685 while (d != future_map.end()) {
2694 /* all is ok. set section's locked tempo if allowed.
2695 possibly disallowed if there is an adjacent audio-locked tempo.
2696 XX this check could possibly go. its never actually happened here.
2698 MeterSection* meter_copy = const_cast<MeterSection*>
2699 (&meter_section_at_minute_locked (future_map, section->minute()));
2701 meter_copy->set_minute (minute);
2703 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2704 section->set_minute (minute);
2705 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2706 / prev_m->note_divisor()) + prev_m->pulse());
2707 solve_map_minute (imaginary, meter_locked_tempo, minute);
2712 Metrics::const_iterator d = future_map.begin();
2713 while (d != future_map.end()) {
2723 /* not movable (first meter atm) */
2725 tempo_copy->set_minute (minute);
2726 tempo_copy->set_pulse (0.0);
2728 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2729 section->set_minute (minute);
2730 meter_locked_tempo->set_minute (minute);
2731 meter_locked_tempo->set_pulse (0.0);
2732 solve_map_minute (imaginary, meter_locked_tempo, minute);
2737 Metrics::const_iterator d = future_map.begin();
2738 while (d != future_map.end()) {
2747 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2748 section->set_beat (b_bbt);
2749 section->set_pulse (0.0);
2759 MetricSectionFrameSorter fcmp;
2760 imaginary.sort (fcmp);
2762 recompute_meters (imaginary);
2768 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2770 /* disallow setting section to an existing meter's bbt */
2771 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2773 if (!(*i)->is_tempo()) {
2774 m = static_cast<MeterSection*> (*i);
2775 if (m != section && m->bbt().bars == when.bars) {
2781 MeterSection* prev_m = 0;
2782 MeterSection* section_prev = 0;
2784 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2786 if (!(*i)->is_tempo()) {
2787 m = static_cast<MeterSection*> (*i);
2788 pair<double, BBT_Time> b_bbt;
2789 double new_pulse = 0.0;
2791 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2792 section_prev = prev_m;
2793 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2794 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2795 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2797 section->set_beat (b_bbt);
2798 section->set_pulse (pulse);
2799 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2804 if (m->position_lock_style() == AudioTime) {
2805 TempoSection* meter_locked_tempo = 0;
2807 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2809 if ((*ii)->is_tempo()) {
2810 t = static_cast<TempoSection*> (*ii);
2811 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2812 meter_locked_tempo = t;
2818 if (!meter_locked_tempo) {
2823 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2825 if (beats + prev_m->beat() != m->beat()) {
2826 /* tempo/ meter change caused a change in beat (bar). */
2827 b_bbt = make_pair (beats + prev_m->beat()
2828 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2829 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2830 } else if (m->movable()) {
2831 b_bbt = make_pair (m->beat(), m->bbt());
2832 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2835 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2838 meter_locked_tempo->set_pulse (new_pulse);
2839 m->set_beat (b_bbt);
2840 m->set_pulse (new_pulse);
2844 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2845 if (beats + prev_m->beat() != m->beat()) {
2846 /* tempo/ meter change caused a change in beat (bar). */
2847 b_bbt = make_pair (beats + prev_m->beat()
2848 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2850 b_bbt = make_pair (beats + prev_m->beat()
2853 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2854 m->set_beat (b_bbt);
2855 m->set_pulse (new_pulse);
2856 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
2863 if (!section_prev) {
2865 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2866 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2867 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2869 section->set_beat (b_bbt);
2870 section->set_pulse (pulse);
2871 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2874 MetricSectionSorter cmp;
2875 imaginary.sort (cmp);
2877 recompute_meters (imaginary);
2882 /** places a copy of _metrics into copy and returns a pointer
2883 * to section's equivalent in copy.
2886 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2888 TempoSection* ret = 0;
2890 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2893 if ((*i)->is_tempo()) {
2894 t = static_cast<TempoSection*> (*i);
2896 ret = new TempoSection (*t);
2897 copy.push_back (ret);
2901 TempoSection* cp = new TempoSection (*t);
2902 copy.push_back (cp);
2904 if (!(*i)->is_tempo()) {
2905 m = static_cast<MeterSection *> (*i);
2906 MeterSection* cp = new MeterSection (*m);
2907 copy.push_back (cp);
2915 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2917 MeterSection* ret = 0;
2919 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2922 if ((*i)->is_tempo()) {
2923 t = static_cast<TempoSection*> (*i);
2924 TempoSection* cp = new TempoSection (*t);
2925 copy.push_back (cp);
2928 if (!(*i)->is_tempo()) {
2929 m = static_cast<MeterSection *> (*i);
2931 ret = new MeterSection (*m);
2932 copy.push_back (ret);
2935 MeterSection* cp = new MeterSection (*m);
2936 copy.push_back (cp);
2943 /** answers the question "is this a valid beat position for this tempo section?".
2944 * it returns true if the tempo section can be moved to the requested bbt position,
2945 * leaving the tempo map in a solved state.
2946 * @param ts the tempo section to be moved
2947 * @param bbt the requested new position for the tempo section
2948 * @return true if the tempo section can be moved to the position, otherwise false.
2951 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2954 TempoSection* tempo_copy = 0;
2957 Glib::Threads::RWLock::ReaderLock lm (lock);
2958 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2964 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
2966 Metrics::const_iterator d = copy.begin();
2967 while (d != copy.end()) {
2976 * 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,
2977 * taking any possible reordering as a consequence of this into account.
2978 * @param section - the section to be altered
2979 * @param bbt - the BBT time where the altered tempo will fall
2980 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
2982 pair<double, framepos_t>
2983 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
2986 pair<double, framepos_t> ret = make_pair (0.0, 0);
2988 Glib::Threads::RWLock::ReaderLock lm (lock);
2990 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2992 const double beat = beat_at_bbt_locked (future_map, bbt);
2994 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2995 ret.first = tempo_copy->pulse();
2996 ret.second = tempo_copy->frame();
2998 ret.first = section->pulse();
2999 ret.second = section->frame();
3002 Metrics::const_iterator d = future_map.begin();
3003 while (d != future_map.end()) {
3010 /** moves a TempoSection to a specified position.
3011 * @param ts - the section to be moved
3012 * @param frame - the new position in frames for the tempo
3013 * @param sub_num - the snap division to use if using musical time.
3015 * if sub_num is non-zero, the frame position is used to calculate an exact
3018 * -1 | snap to bars (meter-based)
3019 * 0 | no snap - use audio frame for musical position
3020 * 1 | snap to meter-based (BBT) beat
3021 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3023 * this follows the snap convention in the gui.
3024 * if sub_num is zero, the musical position will be taken from the supplied frame.
3027 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3031 if (ts->position_lock_style() == MusicTime) {
3033 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3034 Glib::Threads::RWLock::WriterLock lm (lock);
3035 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3037 tempo_copy->set_position_lock_style (AudioTime);
3039 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3040 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3041 const double pulse = pulse_at_beat_locked (future_map, beat);
3043 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3044 solve_map_pulse (_metrics, ts, pulse);
3045 recompute_meters (_metrics);
3053 Glib::Threads::RWLock::WriterLock lm (lock);
3054 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3056 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3058 /* We're moving the object that defines the grid while snapping to it...
3059 * Placing the ts at the beat corresponding to the requested frame may shift the
3060 * grid in such a way that the mouse is left hovering over a completerly different division,
3061 * causing jittering when the mouse next moves (esp. large tempo deltas).
3062 * To avoid this, place the ts at the requested frame in a dummy map
3063 * then find the closest beat subdivision to that frame in the dummy.
3064 * This alters the snap behaviour slightly in that we snap to beat divisions
3065 * in the future map rather than the existing one.
3067 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3068 const double pulse = pulse_at_beat_locked (future_map, beat);
3070 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3071 /* snapping to a grid. force MusicTime temporarily. */
3072 ts->set_position_lock_style (MusicTime);
3073 solve_map_pulse (_metrics, ts, pulse);
3074 ts->set_position_lock_style (AudioTime);
3076 recompute_meters (_metrics);
3079 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3080 recompute_meters (_metrics);
3086 Metrics::const_iterator d = future_map.begin();
3087 while (d != future_map.end()) {
3092 MetricPositionChanged (); // Emit Signal
3095 /** moves a MeterSection to a specified position.
3096 * @param ms - the section to be moved
3097 * @param frame - the new position in frames for the meter
3099 * as a meter cannot snap to anything but bars,
3100 * the supplied frame is rounded to the nearest bar, possibly
3101 * leaving the meter position unchanged.
3104 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
3108 if (ms->position_lock_style() == AudioTime) {
3111 Glib::Threads::RWLock::WriterLock lm (lock);
3112 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3114 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3115 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3116 recompute_tempi (_metrics);
3121 Glib::Threads::RWLock::WriterLock lm (lock);
3122 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3124 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3125 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3127 if (solve_map_bbt (future_map, copy, bbt)) {
3128 solve_map_bbt (_metrics, ms, bbt);
3129 recompute_tempi (_metrics);
3134 Metrics::const_iterator d = future_map.begin();
3135 while (d != future_map.end()) {
3140 MetricPositionChanged (); // Emit Signal
3144 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3147 bool can_solve = false;
3149 Glib::Threads::RWLock::WriterLock lm (lock);
3150 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3151 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
3152 recompute_tempi (future_map);
3154 if (check_solved (future_map)) {
3155 ts->set_beats_per_minute (bpm.beats_per_minute());
3156 recompute_map (_metrics);
3161 Metrics::const_iterator d = future_map.begin();
3162 while (d != future_map.end()) {
3167 MetricPositionChanged (); // Emit Signal
3173 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
3176 Ts (future prev_t) Tnext
3179 |----------|----------
3186 Glib::Threads::RWLock::WriterLock lm (lock);
3192 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3193 TempoSection* prev_to_prev_t = 0;
3194 const frameoffset_t fr_off = end_frame - frame;
3196 if (prev_t && prev_t->pulse() > 0.0) {
3197 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
3200 TempoSection* next_t = 0;
3201 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
3202 TempoSection* t = 0;
3203 if ((*i)->is_tempo()) {
3204 t = static_cast<TempoSection*> (*i);
3205 if (t->frame() > ts->frame()) {
3211 /* minimum allowed measurement distance in frames */
3212 const framepos_t min_dframe = 2;
3214 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3215 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3217 double contribution = 0.0;
3219 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3220 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3223 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3225 const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame));
3226 const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame));
3230 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
3232 if (prev_t->position_lock_style() == MusicTime) {
3233 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3234 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3236 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
3237 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3239 new_bpm = prev_t->beats_per_minute();
3242 /* prev to prev is irrelevant */
3244 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3245 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3247 new_bpm = prev_t->beats_per_minute();
3252 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3253 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3255 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
3256 / (double) ((end_frame) - prev_to_prev_t->frame()));
3258 new_bpm = prev_t->beats_per_minute();
3261 /* prev_to_prev_t is irrelevant */
3263 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3264 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3266 new_bpm = prev_t->beats_per_minute();
3272 double frame_ratio = 1.0;
3273 double pulse_ratio = 1.0;
3274 const double pulse_pos = frame;
3276 if (prev_to_prev_t) {
3277 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3278 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3280 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3281 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3284 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3285 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3287 pulse_ratio = (start_pulse / end_pulse);
3289 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
3292 /* don't clamp and proceed here.
3293 testing has revealed that this can go negative,
3294 which is an entirely different thing to just being too low.
3296 if (new_bpm < 0.5) {
3299 new_bpm = min (new_bpm, (double) 1000.0);
3300 prev_t->set_beats_per_minute (new_bpm);
3301 recompute_tempi (future_map);
3302 recompute_meters (future_map);
3304 if (check_solved (future_map)) {
3305 ts->set_beats_per_minute (new_bpm);
3306 recompute_tempi (_metrics);
3307 recompute_meters (_metrics);
3311 Metrics::const_iterator d = future_map.begin();
3312 while (d != future_map.end()) {
3317 MetricPositionChanged (); // Emit Signal
3320 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3321 * the supplied frame, possibly returning a negative value.
3323 * @param frame The session frame position.
3324 * @param sub_num The subdivision to use when rounding the beat.
3325 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3326 * Positive integers indicate quarter note (non BBT) divisions.
3327 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3328 * @return The beat position of the supplied frame.
3330 * when working to a musical grid, the use of sub_nom indicates that
3331 * the position should be interpreted musically.
3333 * it effectively snaps to meter bars, meter beats or quarter note divisions
3334 * (as per current gui convention) and returns a musical position independent of frame rate.
3336 * If the supplied frame lies before the first meter, the return will be negative,
3337 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3338 * the continuation of the tempo curve (backwards).
3340 * This function is sensitive to tempo and meter.
3343 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num)
3345 Glib::Threads::RWLock::ReaderLock lm (lock);
3347 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3351 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions)
3353 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3356 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3357 * the supplied frame, possibly returning a negative value.
3359 * @param frame The session frame position.
3360 * @param sub_num The subdivision to use when rounding the quarter note.
3361 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3362 * Positive integers indicate quarter note (non BBT) divisions.
3363 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3364 * @return The quarter note position of the supplied frame.
3366 * When working to a musical grid, the use of sub_nom indicates that
3367 * the frame position should be interpreted musically.
3369 * it effectively snaps to meter bars, meter beats or quarter note divisions
3370 * (as per current gui convention) and returns a musical position independent of frame rate.
3372 * If the supplied frame lies before the first meter, the return will be negative,
3373 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3374 * the continuation of the tempo curve (backwards).
3376 * This function is tempo-sensitive.
3379 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num)
3381 Glib::Threads::RWLock::ReaderLock lm (lock);
3383 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3387 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num)
3389 double qn = quarter_note_at_minute_locked (metrics, minute_at_frame (frame));
3392 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3393 } else if (sub_num == 1) {
3394 /* the gui requested exact musical (BBT) beat */
3395 qn = quarter_note_at_beat_locked (metrics, floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5));
3396 } else if (sub_num == -1) {
3398 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3402 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3404 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3406 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3416 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3417 * @param pos the frame position in the tempo map.
3418 * @param bbt the distance in BBT time from pos to calculate.
3419 * @param dir the rounding direction..
3420 * @return the duration in frames between pos and bbt
3423 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3425 Glib::Threads::RWLock::ReaderLock lm (lock);
3427 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3428 const framecnt_t offset = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3429 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3432 pos_bbt.bars += bbt.bars;
3434 pos_bbt.ticks += bbt.ticks;
3435 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3437 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3440 pos_bbt.beats += bbt.beats;
3441 if ((double) pos_bbt.beats > divisions) {
3443 pos_bbt.beats -= divisions;
3446 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt)) - offset;
3448 pos_bbt.bars -= bbt.bars;
3450 if (pos_bbt.ticks < bbt.ticks) {
3451 if (pos_bbt.beats == 1) {
3453 pos_bbt.beats = divisions;
3457 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3459 pos_bbt.ticks -= bbt.ticks;
3462 if (pos_bbt.beats <= bbt.beats) {
3464 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3466 pos_bbt.beats -= bbt.beats;
3469 return offset - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3476 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3478 return round_to_type (fr, dir, Bar);
3482 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3484 return round_to_type (fr, dir, Beat);
3488 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3490 Glib::Threads::RWLock::ReaderLock lm (lock);
3491 uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3492 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3493 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3495 ticks -= beats * BBT_Time::ticks_per_beat;
3498 /* round to next (or same iff dir == RoundUpMaybe) */
3500 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3502 if (mod == 0 && dir == RoundUpMaybe) {
3503 /* right on the subdivision, which is fine, so do nothing */
3505 } else if (mod == 0) {
3506 /* right on the subdivision, so the difference is just the subdivision ticks */
3507 ticks += ticks_one_subdivisions_worth;
3510 /* not on subdivision, compute distance to next subdivision */
3512 ticks += ticks_one_subdivisions_worth - mod;
3515 if (ticks >= BBT_Time::ticks_per_beat) {
3516 ticks -= BBT_Time::ticks_per_beat;
3518 } else if (dir < 0) {
3520 /* round to previous (or same iff dir == RoundDownMaybe) */
3522 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3524 if (difference == 0 && dir == RoundDownAlways) {
3525 /* right on the subdivision, but force-rounding down,
3526 so the difference is just the subdivision ticks */
3527 difference = ticks_one_subdivisions_worth;
3530 if (ticks < difference) {
3531 ticks = BBT_Time::ticks_per_beat - ticks;
3533 ticks -= difference;
3537 /* round to nearest */
3540 /* compute the distance to the previous and next subdivision */
3542 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3544 /* closer to the next subdivision, so shift forward */
3546 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3548 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3550 if (ticks > BBT_Time::ticks_per_beat) {
3552 ticks -= BBT_Time::ticks_per_beat;
3553 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3556 } else if (rem > 0) {
3558 /* closer to previous subdivision, so shift backward */
3562 /* can't go backwards past zero, so ... */
3565 /* step back to previous beat */
3567 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3568 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3570 ticks = lrint (ticks - rem);
3571 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3574 /* on the subdivision, do nothing */
3578 const framepos_t ret_frame = frame_at_minute (minute_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3584 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3586 Glib::Threads::RWLock::ReaderLock lm (lock);
3587 uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3588 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3589 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3591 ticks -= beats * BBT_Time::ticks_per_beat;
3594 /* round to next (or same iff dir == RoundUpMaybe) */
3596 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3598 if (mod == 0 && dir == RoundUpMaybe) {
3599 /* right on the subdivision, which is fine, so do nothing */
3601 } else if (mod == 0) {
3602 /* right on the subdivision, so the difference is just the subdivision ticks */
3603 ticks += ticks_one_subdivisions_worth;
3606 /* not on subdivision, compute distance to next subdivision */
3608 ticks += ticks_one_subdivisions_worth - mod;
3611 if (ticks >= BBT_Time::ticks_per_beat) {
3612 ticks -= BBT_Time::ticks_per_beat;
3614 } else if (dir < 0) {
3616 /* round to previous (or same iff dir == RoundDownMaybe) */
3618 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3620 if (difference == 0 && dir == RoundDownAlways) {
3621 /* right on the subdivision, but force-rounding down,
3622 so the difference is just the subdivision ticks */
3623 difference = ticks_one_subdivisions_worth;
3626 if (ticks < difference) {
3627 ticks = BBT_Time::ticks_per_beat - ticks;
3629 ticks -= difference;
3633 /* round to nearest */
3636 /* compute the distance to the previous and next subdivision */
3638 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3640 /* closer to the next subdivision, so shift forward */
3642 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3644 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3646 if (ticks > BBT_Time::ticks_per_beat) {
3648 ticks -= BBT_Time::ticks_per_beat;
3649 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3652 } else if (rem > 0) {
3654 /* closer to previous subdivision, so shift backward */
3658 /* can't go backwards past zero, so ... */
3661 /* step back to previous beat */
3663 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3664 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3666 ticks = lrint (ticks - rem);
3667 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3670 /* on the subdivision, do nothing */
3674 const framepos_t ret_frame = frame_at_minute (minute_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3680 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3682 Glib::Threads::RWLock::ReaderLock lm (lock);
3684 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (frame)));
3685 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3690 /* find bar previous to 'frame' */
3693 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3695 } else if (dir > 0) {
3696 /* find bar following 'frame' */
3700 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3702 /* true rounding: find nearest bar */
3703 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3706 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3708 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3710 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3721 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
3722 } else if (dir > 0) {
3723 return frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
3725 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
3734 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3735 framepos_t lower, framepos_t upper, uint32_t bar_mod)
3737 Glib::Threads::RWLock::ReaderLock lm (lock);
3738 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
3740 /* although the map handles negative beats, bbt doesn't. */
3745 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
3749 while (pos >= 0 && pos < upper) {
3750 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
3751 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3752 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3753 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3754 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3758 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
3763 bbt.bars -= bbt.bars % bar_mod;
3767 while (pos >= 0 && pos < upper) {
3768 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3769 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3770 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3771 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3772 bbt.bars += bar_mod;
3778 TempoMap::tempo_section_at_frame (framepos_t frame) const
3780 Glib::Threads::RWLock::ReaderLock lm (lock);
3782 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
3786 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
3788 TempoSection* prev = 0;
3792 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3794 if ((*i)->is_tempo()) {
3795 t = static_cast<TempoSection*> (*i);
3799 if (prev && t->minute() > minute) {
3809 abort(); /*NOTREACHED*/
3816 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3818 TempoSection* prev_t = 0;
3819 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3823 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3824 if ((*i)->is_tempo()) {
3825 t = static_cast<TempoSection*> (*i);
3826 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3836 /* don't use this to calculate length (the tempo is only correct for this frame).
3837 do that stuff based on the beat_at_frame and frame_at_beat api
3840 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3842 Glib::Threads::RWLock::ReaderLock lm (lock);
3844 const TempoSection* ts_at = 0;
3845 const TempoSection* ts_after = 0;
3846 Metrics::const_iterator i;
3849 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3851 if ((*i)->is_tempo()) {
3852 t = static_cast<TempoSection*> (*i);
3856 if (ts_at && (*i)->frame() > frame) {
3865 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame));
3867 /* must be treated as constant tempo */
3868 return ts_at->frames_per_beat (_frame_rate);
3872 TempoMap::meter_section_at_frame (framepos_t frame) const
3874 Glib::Threads::RWLock::ReaderLock lm (lock);
3875 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
3879 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
3881 Metrics::const_iterator i;
3882 MeterSection* prev = 0;
3886 for (i = metrics.begin(); i != metrics.end(); ++i) {
3888 if (!(*i)->is_tempo()) {
3889 m = static_cast<MeterSection*> (*i);
3891 if (prev && (*i)->minute() > minute) {
3901 abort(); /*NOTREACHED*/
3908 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3910 MeterSection* prev_m = 0;
3912 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3914 if (!(*i)->is_tempo()) {
3915 m = static_cast<MeterSection*> (*i);
3916 if (prev_m && m->beat() > beat) {
3927 TempoMap::meter_section_at_beat (double beat) const
3929 Glib::Threads::RWLock::ReaderLock lm (lock);
3930 return meter_section_at_beat_locked (_metrics, beat);
3934 TempoMap::meter_at_frame (framepos_t frame) const
3936 TempoMetric m (metric_at (frame));
3941 TempoMap::fix_legacy_session ()
3943 MeterSection* prev_m = 0;
3944 TempoSection* prev_t = 0;
3946 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3950 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3951 if (!m->movable()) {
3952 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3955 m->set_minute (0.0);
3956 m->set_position_lock_style (AudioTime);
3961 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3962 + (m->bbt().beats - 1)
3963 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3965 m->set_beat (start);
3966 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3967 + (m->bbt().beats - 1)
3968 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3969 m->set_pulse (start_beat / prev_m->note_divisor());
3972 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3978 if (!t->movable()) {
3980 t->set_minute (0.0);
3981 t->set_position_lock_style (AudioTime);
3987 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3988 + (t->legacy_bbt().beats - 1)
3989 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3991 t->set_pulse (beat / prev_m->note_divisor());
3993 /* really shouldn't happen but.. */
3994 t->set_pulse (beat / 4.0);
4003 TempoMap::get_state ()
4005 Metrics::const_iterator i;
4006 XMLNode *root = new XMLNode ("TempoMap");
4009 Glib::Threads::RWLock::ReaderLock lm (lock);
4010 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4011 root->add_child_nocopy ((*i)->get_state());
4019 TempoMap::set_state (const XMLNode& node, int /*version*/)
4022 Glib::Threads::RWLock::WriterLock lm (lock);
4025 XMLNodeConstIterator niter;
4026 Metrics old_metrics (_metrics);
4029 nlist = node.children();
4031 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4032 XMLNode* child = *niter;
4034 if (child->name() == TempoSection::xml_state_node_name) {
4037 TempoSection* ts = new TempoSection (*child, _frame_rate);
4038 _metrics.push_back (ts);
4041 catch (failed_constructor& err){
4042 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4043 _metrics = old_metrics;
4044 old_metrics.clear();
4048 } else if (child->name() == MeterSection::xml_state_node_name) {
4051 MeterSection* ms = new MeterSection (*child, _frame_rate);
4052 _metrics.push_back (ms);
4055 catch (failed_constructor& err) {
4056 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4057 _metrics = old_metrics;
4058 old_metrics.clear();
4064 if (niter == nlist.end()) {
4065 MetricSectionSorter cmp;
4066 _metrics.sort (cmp);
4069 /* check for legacy sessions where bbt was the base musical unit for tempo */
4070 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4072 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4073 if (t->legacy_bbt().bars != 0) {
4074 fix_legacy_session();
4081 /* check for multiple tempo/meters at the same location, which
4082 ardour2 somehow allowed.
4085 Metrics::iterator prev = _metrics.end();
4086 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4087 if (prev != _metrics.end()) {
4089 MeterSection* prev_m;
4091 TempoSection* prev_t;
4092 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4093 if (prev_m->pulse() == ms->pulse()) {
4094 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4095 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4098 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4099 if (prev_t->pulse() == ts->pulse()) {
4100 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4101 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4109 recompute_map (_metrics);
4111 Metrics::const_iterator d = old_metrics.begin();
4112 while (d != old_metrics.end()) {
4116 old_metrics.clear ();
4119 PropertyChanged (PropertyChange ());
4125 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
4127 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4128 const MeterSection* m;
4129 const TempoSection* t;
4130 const TempoSection* prev_t = 0;
4132 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4134 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4135 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type()
4136 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4137 << " minute= " << t->minute() << " frame= " << t->frame() << " (movable? " << t->movable() << ')'
4138 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4140 o << std::setprecision (17) << " current : " << t->beats_per_minute()
4141 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4142 o << " previous : " << prev_t->beats_per_minute()
4143 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4144 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4145 << " | " << prev_t->pulse_at_tempo (t->beats_per_minute(), t->minute())
4146 << " | " << frame_at_minute (prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()))
4147 << " | " << prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()) << std::endl;
4150 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4151 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4152 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4153 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
4156 o << "------" << std::endl;
4160 TempoMap::n_tempos() const
4162 Glib::Threads::RWLock::ReaderLock lm (lock);
4165 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4166 if ((*i)->is_tempo()) {
4175 TempoMap::n_meters() const
4177 Glib::Threads::RWLock::ReaderLock lm (lock);
4180 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4181 if (!(*i)->is_tempo()) {
4190 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4192 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4193 if ((*i)->frame() >= where && (*i)->movable ()) {
4197 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4198 gui_move_meter (ms, (*i)->frame() + amount);
4201 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4202 gui_move_tempo (ts, (*i)->frame() + amount, 0);
4207 PropertyChanged (PropertyChange ());
4211 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4215 std::list<MetricSection*> metric_kill_list;
4217 TempoSection* last_tempo = NULL;
4218 MeterSection* last_meter = NULL;
4219 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4220 bool meter_after = false; // is there a meter marker likewise?
4222 Glib::Threads::RWLock::WriterLock lm (lock);
4223 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4224 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4225 metric_kill_list.push_back(*i);
4226 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4229 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4233 else if ((*i)->frame() >= where) {
4234 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4235 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4236 if ((*i)->frame() == where) {
4237 // marker was immediately after end of range
4238 tempo_after = dynamic_cast<TempoSection*> (*i);
4239 meter_after = dynamic_cast<MeterSection*> (*i);
4245 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4246 if (last_tempo && !tempo_after) {
4247 metric_kill_list.remove(last_tempo);
4248 last_tempo->set_minute (minute_at_frame (where));
4251 if (last_meter && !meter_after) {
4252 metric_kill_list.remove(last_meter);
4253 last_meter->set_minute (minute_at_frame (where));
4257 //remove all the remaining metrics
4258 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4259 _metrics.remove(*i);
4264 recompute_map (_metrics);
4267 PropertyChanged (PropertyChange ());
4271 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4272 * pos can be -ve, if required.
4275 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats quarter_note) const
4277 Glib::Threads::RWLock::ReaderLock lm (lock);
4279 return frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note_at_minute_locked (_metrics, minute_at_frame (frame)) + quarter_note.to_double()));
4283 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4285 Glib::Threads::RWLock::ReaderLock lm (lock);
4287 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4288 pos_bbt.ticks += op.ticks;
4289 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4291 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4293 pos_bbt.beats += op.beats;
4294 /* the meter in effect will start on the bar */
4295 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();
4296 while (pos_bbt.beats >= divisions_per_bar + 1) {
4298 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4299 pos_bbt.beats -= divisions_per_bar;
4301 pos_bbt.bars += op.bars;
4303 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4306 /** Count the number of beats that are equivalent to distance when going forward,
4310 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4312 Glib::Threads::RWLock::ReaderLock lm (lock);
4314 return Evoral::Beats (quarter_note_at_minute_locked (_metrics, minute_at_frame (pos + distance)) - quarter_note_at_minute_locked (_metrics, minute_at_frame (pos)));
4318 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4324 operator<< (std::ostream& o, const Meter& m) {
4325 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4329 operator<< (std::ostream& o, const Tempo& t) {
4330 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
4334 operator<< (std::ostream& o, const MetricSection& section) {
4336 o << "MetricSection @ " << section.frame() << ' ';
4338 const TempoSection* ts;
4339 const MeterSection* ms;
4341 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4342 o << *((const Tempo*) ts);
4343 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4344 o << *((const Meter*) ms);