2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0, 4.0);
51 MetricSection::frame_at_minute (const double& time) const
53 return (framepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
57 MetricSection::minute_at_frame (const framepos_t& frame) const
59 return (frame / (double) _sample_rate) / 60.0;
62 /***********************************************************************/
65 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
67 /* This is tempo- and meter-sensitive. The number it returns
68 is based on the interval between any two lines in the
69 grid that is constructed from tempo and meter sections.
71 The return value IS NOT interpretable in terms of "beats".
74 return (60.0 * sr) / (tempo.note_types_per_minute() * (_note_type/tempo.note_type()));
78 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
80 return frames_per_grid (tempo, sr) * _divisions_per_bar;
83 /***********************************************************************/
85 const string TempoSection::xml_state_node_name = "Tempo";
87 TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate)
88 : MetricSection (0.0, 0, MusicTime, true, sample_rate)
89 , Tempo (TempoMap::default_tempo())
92 , _locked_to_meter (false)
94 XMLProperty const * prop;
100 _legacy_bbt = BBT_Time (0, 0, 0);
102 if ((prop = node.property ("start")) != 0) {
103 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
107 /* legacy session - start used to be in bbt*/
110 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
114 if ((prop = node.property ("pulse")) != 0) {
115 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
116 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
122 if ((prop = node.property ("frame")) != 0) {
123 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
124 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
125 throw failed_constructor();
127 set_minute (minute_at_frame (frame));
131 /* XX replace old beats-per-minute name with note-types-per-minute */
132 if ((prop = node.property ("beats-per-minute")) != 0) {
133 if (sscanf (prop->value().c_str(), "%lf", &_note_types_per_minute) != 1 || _note_types_per_minute < 0.0) {
134 error << _("TempoSection XML node has an illegal \"beats-per-minutee\" value") << endmsg;
135 throw failed_constructor();
139 if ((prop = node.property ("note-type")) == 0) {
140 /* older session, make note type be quarter by default */
143 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
144 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
145 throw failed_constructor();
149 if ((prop = node.property ("movable")) == 0) {
150 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
151 throw failed_constructor();
154 set_movable (string_is_affirmative (prop->value()));
156 if ((prop = node.property ("active")) == 0) {
157 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
160 set_active (string_is_affirmative (prop->value()));
163 if ((prop = node.property ("tempo-type")) == 0) {
166 _type = Type (string_2_enum (prop->value(), _type));
169 if ((prop = node.property ("lock-style")) == 0) {
171 set_position_lock_style (MusicTime);
173 set_position_lock_style (AudioTime);
176 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
179 if ((prop = node.property ("locked-to-meter")) == 0) {
180 set_locked_to_meter (false);
182 set_locked_to_meter (string_is_affirmative (prop->value()));
187 TempoSection::get_state() const
189 XMLNode *root = new XMLNode (xml_state_node_name);
193 snprintf (buf, sizeof (buf), "%lf", pulse());
194 root->add_property ("pulse", buf);
195 snprintf (buf, sizeof (buf), "%li", frame());
196 root->add_property ("frame", buf);
197 snprintf (buf, sizeof (buf), "%lf", _note_types_per_minute);
198 root->add_property ("beats-per-minute", buf);
199 snprintf (buf, sizeof (buf), "%lf", _note_type);
200 root->add_property ("note-type", buf);
201 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
202 root->add_property ("movable", buf);
203 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
204 root->add_property ("active", buf);
205 root->add_property ("tempo-type", enum_2_string (_type));
206 root->add_property ("lock-style", enum_2_string (position_lock_style()));
207 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
213 TempoSection::set_type (Type type)
218 /** returns the Tempo at the session-relative minute.
221 TempoSection::tempo_at_minute (const double& m) const
224 if (_type == Constant || _c_func == 0.0) {
225 return Tempo (note_types_per_minute(), note_type());
228 return Tempo (_tempo_at_time (m - minute()), _note_type);
231 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
232 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
233 * @param p the pulse used to calculate the returned minute for constant tempi
234 * @return the minute at the supplied tempo
236 * note that the note_type is currently ignored in this function. see below.
240 /** user feedback dictates that if tempoA (120, 4.0) precedes tempoB (120, 8.0),
241 * there will be no ramp between the two even if set to ramped.
242 * in other words a ramp should only place a curve on note_types_per_minute.
243 * we should be able to use Tempo note type here, but the above
244 * complicates things a bit.
245 * we would ideally like to use arbitrary Tempo structs here.
248 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
250 if (_type == Constant || _c_func == 0.0) {
251 return ((p - pulse()) / pulses_per_minute()) + minute();
254 return _time_at_tempo (ntpm) + minute();
257 /** returns the Tempo at the supplied whole-note pulse.
260 TempoSection::tempo_at_pulse (const double& p) const
263 if (_type == Constant || _c_func == 0.0) {
264 return Tempo (note_types_per_minute(), note_type());
267 return Tempo (_tempo_at_pulse (p - pulse()), _note_type);
270 /** returns the whole-note pulse where a tempo in note types per minute occurs.
271 * constant tempi require minute m.
272 * @param ntpm the note types per minute value used to calculate the returned pulse
273 * @param m the minute used to calculate the returned pulse if the tempo is constant
274 * @return the whole-note pulse at the supplied tempo
276 * note that note_type is currently ignored in this function. see minute_at_tempo().
278 * for constant tempi, this is anaologous to pulse_at_minute().
281 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
283 if (_type == Constant || _c_func == 0.0) {
284 return ((m - minute()) * pulses_per_minute()) + pulse();
287 return _pulse_at_tempo (ntpm) + pulse();
290 /** returns the whole-note pulse at the supplied session-relative minute.
293 TempoSection::pulse_at_minute (const double& m) const
295 if (_type == Constant || _c_func == 0.0) {
296 return ((m - minute()) * pulses_per_minute()) + pulse();
299 return _pulse_at_time (m - minute()) + pulse();
302 /** returns the session-relative minute at the supplied whole-note pulse.
305 TempoSection::minute_at_pulse (const double& p) const
307 if (_type == Constant || _c_func == 0.0) {
308 return ((p - pulse()) / pulses_per_minute()) + minute();
311 return _time_at_pulse (p - pulse()) + minute();
315 TempoSection::pulse_at_frame (const framepos_t& f) const
317 if (_type == Constant || _c_func == 0.0) {
318 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
321 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
325 TempoSection::frame_at_pulse (const double& p) const
327 if (_type == Constant || _c_func == 0.0) {
328 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
331 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
339 Tt----|-----------------*|
340 Ta----|--------------|* |
346 _______________|___|____
347 time a t (next tempo)
350 Duration in beats at time a is the integral of some Tempo function.
351 In our case, the Tempo function (Tempo at time t) is
354 with function constant
359 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
360 b(t) = T0(e^(ct) - 1) / c
362 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:
363 t(b) = log((c.b / T0) + 1) / c
365 The time t at which Tempo T occurs is a as above:
366 t(T) = log(T / T0) / c
368 The beat at which a Tempo T occurs is:
371 The Tempo at which beat b occurs is:
374 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
375 Our problem is that we usually don't know t.
376 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.
377 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
378 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
380 By substituting our expanded t as a in the c function above, our problem is reduced to:
381 c = T0 (e^(log (Ta / T0)) - 1) / b
383 Of course the word 'beat' has been left loosely defined above.
384 In music, a beat is defined by the musical pulse (which comes from the tempo)
385 and the meter in use at a particular time (how many pulse divisions there are in one bar).
386 It would be more accurate to substitute the work 'pulse' for 'beat' above.
390 We can now store c for future time calculations.
391 If the following tempo section (the one that defines c in conjunction with this one)
392 is changed or moved, c is no longer valid.
394 The public methods are session-relative.
396 Most of this stuff is taken from this paper:
399 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
402 Zurich University of Arts
403 Institute for Computer Music and Sound Technology
405 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
409 /** compute this ramp's function constant from some tempo-pulse point
410 * @param end_npm end tempo (in note types per minute)
411 * @param end_pulse duration (pulses into global start) of some other position.
412 * @return the calculated function constant
415 TempoSection::compute_c_func_pulse (const double& end_npm, const double& end_pulse) const
417 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
418 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
421 /** compute the function constant from some tempo-time point.
422 * @param end_npm tempo (note types/min.)
423 * @param end_minute distance (in minutes) from session origin
424 * @return the calculated function constant
427 TempoSection::compute_c_func_minute (const double& end_npm, const double& end_minute) const
429 return c_func (end_npm, end_minute - minute());
432 /* position function */
434 TempoSection::a_func (double end_npm, double c_func) const
436 return log (end_npm / note_types_per_minute()) / c_func;
439 /*function constant*/
441 TempoSection::c_func (double end_npm, double end_time) const
443 return log (end_npm / note_types_per_minute()) / end_time;
446 /* tempo in note types per minute at time in minutes */
448 TempoSection::_tempo_at_time (const double& time) const
450 return exp (_c_func * time) * note_types_per_minute();
453 /* time in minutes at tempo in note types per minute */
455 TempoSection::_time_at_tempo (const double& npm) const
457 return log (npm / note_types_per_minute()) / _c_func;
460 /* pulse at tempo in note types per minute */
462 TempoSection::_pulse_at_tempo (const double& npm) const
464 return ((npm - note_types_per_minute()) / _c_func) / _note_type;
467 /* tempo in note types per minute at pulse */
469 TempoSection::_tempo_at_pulse (const double& pulse) const
471 return (pulse * _note_type * _c_func) + note_types_per_minute();
474 /* pulse at time in minutes */
476 TempoSection::_pulse_at_time (const double& time) const
478 return (expm1 (_c_func * time) * (note_types_per_minute() / _c_func)) / _note_type;
481 /* time in minutes at pulse */
483 TempoSection::_time_at_pulse (const double& pulse) const
485 return log1p ((_c_func * pulse * _note_type) / note_types_per_minute()) / _c_func;
488 /***********************************************************************/
490 const string MeterSection::xml_state_node_name = "Meter";
492 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
493 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
495 XMLProperty const * prop;
500 framepos_t frame = 0;
501 pair<double, BBT_Time> start;
503 if ((prop = node.property ("start")) != 0) {
504 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
508 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
510 /* legacy session - start used to be in bbt*/
511 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
516 if ((prop = node.property ("pulse")) != 0) {
517 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
518 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
523 if ((prop = node.property ("beat")) != 0) {
524 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
525 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
531 if ((prop = node.property ("bbt")) == 0) {
532 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
533 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
537 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
538 throw failed_constructor();
544 if ((prop = node.property ("frame")) != 0) {
545 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
546 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
547 throw failed_constructor();
549 set_minute (minute_at_frame (frame));
553 /* beats-per-bar is old; divisions-per-bar is new */
555 if ((prop = node.property ("divisions-per-bar")) == 0) {
556 if ((prop = node.property ("beats-per-bar")) == 0) {
557 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
558 throw failed_constructor();
561 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
562 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
563 throw failed_constructor();
566 if ((prop = node.property ("note-type")) == 0) {
567 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
568 throw failed_constructor();
570 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
571 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
572 throw failed_constructor();
575 if ((prop = node.property ("movable")) == 0) {
576 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
577 throw failed_constructor();
580 set_movable (string_is_affirmative (prop->value()));
582 if ((prop = node.property ("lock-style")) == 0) {
583 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
585 set_position_lock_style (MusicTime);
587 set_position_lock_style (AudioTime);
590 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
595 MeterSection::get_state() const
597 XMLNode *root = new XMLNode (xml_state_node_name);
601 snprintf (buf, sizeof (buf), "%lf", pulse());
602 root->add_property ("pulse", buf);
603 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
607 root->add_property ("bbt", buf);
608 snprintf (buf, sizeof (buf), "%lf", beat());
609 root->add_property ("beat", buf);
610 snprintf (buf, sizeof (buf), "%lf", _note_type);
611 root->add_property ("note-type", buf);
612 snprintf (buf, sizeof (buf), "%li", frame());
613 root->add_property ("frame", buf);
614 root->add_property ("lock-style", enum_2_string (position_lock_style()));
615 snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
616 root->add_property ("divisions-per-bar", buf);
617 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
618 root->add_property ("movable", buf);
623 /***********************************************************************/
627 Tempo determines the rate of musical pulse determined by its components
628 note types per minute - the rate per minute of the whole note divisor _note_type
629 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
630 Meter divides the musical pulse into measures and beats according to its components
634 TempoSection - translates between time, musical pulse and tempo.
635 has a musical location in whole notes (pulses).
636 has a time location in minutes.
637 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
638 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
640 MeterSection - translates between BBT, meter-based beat and musical pulse.
641 has a musical location in whole notes (pulses)
642 has a musical location in meter-based beats
643 has a musical location in BBT time
644 has a time location expressed in minutes.
646 TempoSection and MeterSection may be locked to either audio or music (position lock style).
647 The lock style determines the location type to be kept as a reference when location is recalculated.
649 The first tempo and meter are special. they must move together, and are locked to audio.
650 Audio locked tempi which lie before the first meter are made inactive.
652 Recomputing the map is the process where the 'missing' location types are calculated.
653 We construct the tempo map by first using the locked location type of each section
654 to determine non-locked location types (pulse or minute position).
655 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
657 Having done this, we can now traverse the Metrics list by pulse or minute
658 to query its relevant meter/tempo.
660 It is important to keep the _metrics in an order that makes sense.
661 Because ramped MusicTime and AudioTime tempos can interact with each other,
662 reordering is frequent. Care must be taken to keep _metrics in a solved state.
663 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
667 Music and audio-locked objects may seem interchangeable on the surface, but when translating
668 between audio samples and beat, remember that a sample is only a quantised approximation
669 of the actual time (in minutes) of a beat.
670 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
671 mean that this frame is the actual location in time of 1|3|0.
673 You cannot use a frame measurement to determine beat distance except under special circumstances
674 (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).
676 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
677 sample space the user is operating at to be translated correctly to the object.
679 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
680 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
681 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
683 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
685 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
686 The result is rounded to audio frames.
687 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
690 frame_at_beat (beat_at_frame (frame)) == frame
692 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
694 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
695 frames_between_quarter_notes () eliminats this effect when determining time duration
696 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
698 The above pointless example could instead do:
699 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
701 The Shaggs - Things I Wonder
702 https://www.youtube.com/watch?v=9wQK6zMJOoQ
705 struct MetricSectionSorter {
706 bool operator() (const MetricSection* a, const MetricSection* b) {
707 return a->pulse() < b->pulse();
711 struct MetricSectionFrameSorter {
712 bool operator() (const MetricSection* a, const MetricSection* b) {
713 return a->frame() < b->frame();
717 TempoMap::TempoMap (framecnt_t fr)
720 BBT_Time start (1, 1, 0);
722 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo.note_types_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime, fr);
723 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
725 t->set_movable (false);
726 m->set_movable (false);
728 /* note: frame time is correct (zero) for both of these */
730 _metrics.push_back (t);
731 _metrics.push_back (m);
735 TempoMap::~TempoMap ()
737 Metrics::const_iterator d = _metrics.begin();
738 while (d != _metrics.end()) {
746 TempoMap::frame_at_minute (const double time) const
748 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
752 TempoMap::minute_at_frame (const framepos_t frame) const
754 return (frame / (double) _frame_rate) / 60.0;
758 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
760 bool removed = false;
763 Glib::Threads::RWLock::WriterLock lm (lock);
764 if ((removed = remove_tempo_locked (tempo))) {
765 if (complete_operation) {
766 recompute_map (_metrics);
771 if (removed && complete_operation) {
772 PropertyChanged (PropertyChange ());
777 TempoMap::remove_tempo_locked (const TempoSection& tempo)
781 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
782 if (dynamic_cast<TempoSection*> (*i) != 0) {
783 if (tempo.frame() == (*i)->frame()) {
784 if ((*i)->movable()) {
797 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
799 bool removed = false;
802 Glib::Threads::RWLock::WriterLock lm (lock);
803 if ((removed = remove_meter_locked (tempo))) {
804 if (complete_operation) {
805 recompute_map (_metrics);
810 if (removed && complete_operation) {
811 PropertyChanged (PropertyChange ());
816 TempoMap::remove_meter_locked (const MeterSection& meter)
819 if (meter.position_lock_style() == AudioTime) {
820 /* remove meter-locked tempo */
821 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
823 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
824 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
833 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
834 if (dynamic_cast<MeterSection*> (*i) != 0) {
835 if (meter.frame() == (*i)->frame()) {
836 if ((*i)->movable()) {
849 TempoMap::do_insert (MetricSection* section)
851 bool need_add = true;
852 /* we only allow new meters to be inserted on beat 1 of an existing
856 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
858 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
860 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
861 corrected.second.beats = 1;
862 corrected.second.ticks = 0;
863 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
864 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
865 m->bbt(), corrected.second) << endmsg;
866 //m->set_pulse (corrected);
870 /* Look for any existing MetricSection that is of the same type and
871 in the same bar as the new one, and remove it before adding
872 the new one. Note that this means that if we find a matching,
873 existing section, we can break out of the loop since we're
874 guaranteed that there is only one such match.
877 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
879 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
880 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
881 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
882 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
884 if (tempo && insert_tempo) {
887 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
888 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
890 if (!tempo->movable()) {
892 /* can't (re)move this section, so overwrite
893 * its data content (but not its properties as
897 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
898 (*i)->set_position_lock_style (AudioTime);
900 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
901 t->set_type (insert_tempo->type());
911 } else if (meter && insert_meter) {
915 bool const ipm = insert_meter->position_lock_style() == MusicTime;
917 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
919 if (!meter->movable()) {
921 /* can't (re)move this section, so overwrite
922 * its data content (but not its properties as
926 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
927 (*i)->set_position_lock_style (AudioTime);
937 /* non-matching types, so we don't care */
941 /* Add the given MetricSection, if we didn't just reset an existing
946 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
947 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
950 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
951 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
954 bool const ipm = insert_meter->position_lock_style() == MusicTime;
955 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
960 } else if (insert_tempo) {
961 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
962 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
965 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
966 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
973 _metrics.insert (i, section);
974 //dump (_metrics, std::cout);
977 /* user supplies the exact pulse if pls == MusicTime */
979 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
981 TempoSection* ts = 0;
983 Glib::Threads::RWLock::WriterLock lm (lock);
984 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
988 PropertyChanged (PropertyChange ());
994 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
996 const bool locked_to_meter = ts.locked_to_meter();
999 Glib::Threads::RWLock::WriterLock lm (lock);
1000 TempoSection& first (first_tempo());
1001 if (ts.frame() != first.frame()) {
1002 remove_tempo_locked (ts);
1003 add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter);
1005 first.set_type (type);
1006 first.set_pulse (0.0);
1007 first.set_minute (minute_at_frame (frame));
1008 first.set_position_lock_style (AudioTime);
1010 /* cannot move the first tempo section */
1011 *static_cast<Tempo*>(&first) = tempo;
1012 recompute_map (_metrics);
1017 PropertyChanged (PropertyChange ());
1021 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1022 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1024 TempoSection* t = new TempoSection (pulse, minute, tempo.note_types_per_minute(), tempo.note_type(), type, pls, _frame_rate);
1025 t->set_locked_to_meter (locked_to_meter);
1026 bool solved = false;
1031 if (pls == AudioTime) {
1032 solved = solve_map_minute (_metrics, t, t->minute());
1034 solved = solve_map_pulse (_metrics, t, t->pulse());
1036 recompute_meters (_metrics);
1039 if (!solved && recompute) {
1040 recompute_map (_metrics);
1047 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, PositionLockStyle pls)
1049 MeterSection* m = 0;
1051 Glib::Threads::RWLock::WriterLock lm (lock);
1052 m = add_meter_locked (meter, beat, where, pls, true);
1057 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1058 dump (_metrics, std::cerr);
1062 PropertyChanged (PropertyChange ());
1067 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, PositionLockStyle pls)
1070 Glib::Threads::RWLock::WriterLock lm (lock);
1071 const double beat = beat_at_bbt_locked (_metrics, where);
1074 remove_meter_locked (ms);
1075 add_meter_locked (meter, beat, where, pls, true);
1077 MeterSection& first (first_meter());
1078 TempoSection& first_t (first_tempo());
1079 /* cannot move the first meter section */
1080 *static_cast<Meter*>(&first) = meter;
1081 first.set_position_lock_style (AudioTime);
1082 first.set_pulse (0.0);
1083 //first.set_minute (minute_at_frame (frame));
1084 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1085 first.set_beat (beat);
1086 first_t.set_minute (first.minute());
1087 first_t.set_pulse (0.0);
1088 first_t.set_position_lock_style (AudioTime);
1089 recompute_map (_metrics);
1093 PropertyChanged (PropertyChange ());
1097 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, PositionLockStyle pls, bool recompute)
1099 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1100 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1101 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1102 TempoSection* mlt = 0;
1104 if (pls == AudioTime) {
1105 /* add meter-locked tempo */
1106 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, time_minutes, TempoSection::Ramp, AudioTime, true, true);
1114 MeterSection* new_meter = new MeterSection (pulse, time_minutes, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1115 bool solved = false;
1117 do_insert (new_meter);
1121 if (pls == AudioTime) {
1122 solved = solve_map_minute (_metrics, new_meter, time_minutes);
1124 solved = solve_map_bbt (_metrics, new_meter, where);
1125 /* required due to resetting the pulse of meter-locked tempi above.
1126 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1127 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1129 recompute_map (_metrics);
1133 if (!solved && recompute) {
1134 /* if this has failed to solve, there is little we can do other than to ensure that
1135 the new map is recalculated.
1137 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1138 recompute_map (_metrics);
1145 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type)
1147 Tempo newtempo (note_types_per_minute, note_type);
1150 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1151 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1156 Glib::Threads::RWLock::WriterLock lm (lock);
1157 *((Tempo*) t) = newtempo;
1158 recompute_map (_metrics);
1160 PropertyChanged (PropertyChange ());
1167 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type)
1169 Tempo newtempo (note_types_per_minute, note_type);
1172 TempoSection* first;
1173 Metrics::iterator i;
1175 /* find the TempoSection immediately preceding "where"
1178 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1180 if ((*i)->frame() > where) {
1186 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1199 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1209 Glib::Threads::RWLock::WriterLock lm (lock);
1210 /* cannot move the first tempo section */
1211 *((Tempo*)prev) = newtempo;
1212 recompute_map (_metrics);
1215 PropertyChanged (PropertyChange ());
1219 TempoMap::first_meter () const
1221 const MeterSection *m = 0;
1223 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1224 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1229 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1230 abort(); /*NOTREACHED*/
1235 TempoMap::first_meter ()
1237 MeterSection *m = 0;
1239 /* CALLER MUST HOLD LOCK */
1241 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1242 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1247 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1248 abort(); /*NOTREACHED*/
1253 TempoMap::first_tempo () const
1255 const TempoSection *t = 0;
1257 /* CALLER MUST HOLD LOCK */
1259 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1260 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1264 if (!t->movable()) {
1270 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1271 abort(); /*NOTREACHED*/
1276 TempoMap::first_tempo ()
1278 TempoSection *t = 0;
1280 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1281 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1285 if (!t->movable()) {
1291 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1292 abort(); /*NOTREACHED*/
1296 TempoMap::recompute_tempi (Metrics& metrics)
1298 TempoSection* prev_t = 0;
1300 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1303 if ((*i)->is_tempo()) {
1304 t = static_cast<TempoSection*> (*i);
1308 if (!t->movable()) {
1316 if (t->position_lock_style() == AudioTime) {
1317 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
1318 if (!t->locked_to_meter()) {
1319 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
1323 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
1324 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
1331 prev_t->set_c_func (0.0);
1334 /* tempos must be positioned correctly.
1335 the current approach is to use a meter's bbt time as its base position unit.
1336 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1337 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1340 TempoMap::recompute_meters (Metrics& metrics)
1342 MeterSection* meter = 0;
1343 MeterSection* prev_m = 0;
1345 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1346 if (!(*mi)->is_tempo()) {
1347 meter = static_cast<MeterSection*> (*mi);
1348 if (meter->position_lock_style() == AudioTime) {
1350 pair<double, BBT_Time> b_bbt;
1351 TempoSection* meter_locked_tempo = 0;
1352 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1354 if ((*ii)->is_tempo()) {
1355 t = static_cast<TempoSection*> (*ii);
1356 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1357 meter_locked_tempo = t;
1364 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1365 if (beats + prev_m->beat() != meter->beat()) {
1366 /* reordering caused a bbt change */
1367 b_bbt = make_pair (beats + prev_m->beat()
1368 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1369 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1371 } else if (meter->movable()) {
1372 b_bbt = make_pair (meter->beat(), meter->bbt());
1373 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1376 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1378 if (meter_locked_tempo) {
1379 meter_locked_tempo->set_pulse (pulse);
1381 meter->set_beat (b_bbt);
1382 meter->set_pulse (pulse);
1387 pair<double, BBT_Time> b_bbt;
1389 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1390 if (beats + prev_m->beat() != meter->beat()) {
1391 /* reordering caused a bbt change */
1392 b_bbt = make_pair (beats + prev_m->beat()
1393 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1395 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1397 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1399 /* shouldn't happen - the first is audio-locked */
1400 pulse = pulse_at_beat_locked (metrics, meter->beat());
1401 b_bbt = make_pair (meter->beat(), meter->bbt());
1404 meter->set_beat (b_bbt);
1405 meter->set_pulse (pulse);
1406 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1415 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1417 /* CALLER MUST HOLD WRITE LOCK */
1419 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1422 /* silly call from Session::process() during startup
1427 recompute_tempi (metrics);
1428 recompute_meters (metrics);
1432 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1434 Glib::Threads::RWLock::ReaderLock lm (lock);
1435 TempoMetric m (first_meter(), first_tempo());
1437 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1438 at something, because we insert the default tempo and meter during
1439 TempoMap construction.
1441 now see if we can find better candidates.
1444 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1446 if ((*i)->frame() > frame) {
1460 /* XX meters only */
1462 TempoMap::metric_at (BBT_Time bbt) const
1464 Glib::Threads::RWLock::ReaderLock lm (lock);
1465 TempoMetric m (first_meter(), first_tempo());
1467 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1468 at something, because we insert the default tempo and meter during
1469 TempoMap construction.
1471 now see if we can find better candidates.
1474 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1476 if (!(*i)->is_tempo()) {
1477 mw = static_cast<MeterSection*> (*i);
1478 BBT_Time section_start (mw->bbt());
1480 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1491 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1492 * @param frame The session frame position.
1493 * @return The beat duration according to the tempo map at the supplied frame.
1495 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1496 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1498 * This function uses both tempo and meter.
1501 TempoMap::beat_at_frame (const framecnt_t& frame) const
1503 Glib::Threads::RWLock::ReaderLock lm (lock);
1505 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1508 /* This function uses both tempo and meter.*/
1510 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1512 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1513 MeterSection* prev_m = 0;
1514 MeterSection* next_m = 0;
1516 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1517 if (!(*i)->is_tempo()) {
1518 if (prev_m && (*i)->minute() > minute) {
1519 next_m = static_cast<MeterSection*> (*i);
1522 prev_m = static_cast<MeterSection*> (*i);
1526 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1528 /* audio locked meters fake their beat */
1529 if (next_m && next_m->beat() < beat) {
1530 return next_m->beat();
1536 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1537 * @param beat The BBT (meter-based) beat.
1538 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1540 * This function uses both tempo and meter.
1543 TempoMap::frame_at_beat (const double& beat) const
1545 Glib::Threads::RWLock::ReaderLock lm (lock);
1547 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1550 /* meter & tempo section based */
1552 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1554 MeterSection* prev_m = 0;
1555 TempoSection* prev_t = 0;
1559 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1560 if (!(*i)->is_tempo()) {
1561 m = static_cast<MeterSection*> (*i);
1562 if (prev_m && m->beat() > beat) {
1571 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1572 if ((*i)->is_tempo()) {
1573 t = static_cast<TempoSection*> (*i);
1574 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1582 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1585 /** Returns a Tempo corresponding to the supplied frame position.
1586 * @param frame The audio frame.
1587 * @return a Tempo according to the tempo map at the supplied frame.
1591 TempoMap::tempo_at_frame (const framepos_t& frame) const
1593 Glib::Threads::RWLock::ReaderLock lm (lock);
1595 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1599 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1601 TempoSection* prev_t = 0;
1605 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1606 if ((*i)->is_tempo()) {
1607 t = static_cast<TempoSection*> (*i);
1611 if ((prev_t) && t->minute() > minute) {
1612 /* t is the section past frame */
1613 return prev_t->tempo_at_minute (minute);
1619 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1622 /** returns the frame at which the supplied tempo occurs, or
1623 * the frame of the last tempo section (search exhausted)
1624 * only the position of the first occurence will be returned
1628 TempoMap::frame_at_tempo (const Tempo& tempo) const
1630 Glib::Threads::RWLock::ReaderLock lm (lock);
1632 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1636 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1638 TempoSection* prev_t = 0;
1639 const double tempo_bpm = tempo.note_types_per_minute();
1641 Metrics::const_iterator i;
1643 for (i = metrics.begin(); i != metrics.end(); ++i) {
1645 if ((*i)->is_tempo()) {
1646 t = static_cast<TempoSection*> (*i);
1652 const double t_bpm = t->note_types_per_minute();
1654 if (t_bpm == tempo_bpm) {
1659 const double prev_t_bpm = prev_t->note_types_per_minute();
1661 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1662 return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse());
1669 return prev_t->minute();
1673 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1675 TempoSection* prev_t = 0;
1679 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1680 if ((*i)->is_tempo()) {
1681 t = static_cast<TempoSection*> (*i);
1685 if ((prev_t) && t->pulse() > pulse) {
1686 /* t is the section past frame */
1687 return prev_t->tempo_at_pulse (pulse);
1693 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1697 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1699 TempoSection* prev_t = 0;
1700 const double tempo_bpm = tempo.note_types_per_minute();
1702 Metrics::const_iterator i;
1704 for (i = metrics.begin(); i != metrics.end(); ++i) {
1706 if ((*i)->is_tempo()) {
1707 t = static_cast<TempoSection*> (*i);
1713 const double t_bpm = t->note_types_per_minute();
1715 if (t_bpm == tempo_bpm) {
1720 const double prev_t_bpm = prev_t->note_types_per_minute();
1722 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1723 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1730 return prev_t->pulse();
1733 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1734 * @param qn the position in quarter note beats.
1735 * @return the Tempo at the supplied quarter-note.
1738 TempoMap::tempo_at_quarter_note (const double& qn) const
1740 Glib::Threads::RWLock::ReaderLock lm (lock);
1742 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1745 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1746 * @param tempo the tempo.
1747 * @return the position in quarter-note beats where the map bpm
1748 * is equal to that of the Tempo. currently ignores note_type.
1751 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1753 Glib::Threads::RWLock::ReaderLock lm (lock);
1755 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1758 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1759 * @param metrics the list of metric sections used to calculate the pulse.
1760 * @param beat The BBT (meter-based) beat.
1761 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1763 * a pulse or whole note is the base musical position of a MetricSection.
1764 * it is equivalent to four quarter notes.
1768 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1770 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1772 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1775 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1776 * @param metrics the list of metric sections used to calculate the beat.
1777 * @param pulse the whole-note pulse.
1778 * @return the meter-based beat at the supplied whole-note pulse.
1780 * a pulse or whole note is the base musical position of a MetricSection.
1781 * it is equivalent to four quarter notes.
1784 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1786 MeterSection* prev_m = 0;
1788 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1790 if (!(*i)->is_tempo()) {
1791 m = static_cast<MeterSection*> (*i);
1792 if (prev_m && m->pulse() > pulse) {
1799 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1803 /* tempo section based */
1805 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1807 /* HOLD (at least) THE READER LOCK */
1808 TempoSection* prev_t = 0;
1810 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1812 if ((*i)->is_tempo()) {
1813 t = static_cast<TempoSection*> (*i);
1817 if (prev_t && t->minute() > minute) {
1818 /*the previous ts is the one containing the frame */
1819 const double ret = prev_t->pulse_at_minute (minute);
1820 /* audio locked section in new meter*/
1821 if (t->pulse() < ret) {
1830 /* treated as constant for this ts */
1831 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1833 return pulses_in_section + prev_t->pulse();
1836 /* tempo section based */
1838 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1840 /* HOLD THE READER LOCK */
1842 const TempoSection* prev_t = 0;
1844 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1847 if ((*i)->is_tempo()) {
1848 t = static_cast<TempoSection*> (*i);
1852 if (prev_t && t->pulse() > pulse) {
1853 return prev_t->minute_at_pulse (pulse);
1859 /* must be treated as constant, irrespective of _type */
1860 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1862 return dtime + prev_t->minute();
1865 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1866 * @param bbt The BBT time (meter-based).
1867 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1871 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1873 Glib::Threads::RWLock::ReaderLock lm (lock);
1874 return beat_at_bbt_locked (_metrics, bbt);
1879 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1881 /* CALLER HOLDS READ LOCK */
1883 MeterSection* prev_m = 0;
1885 /* because audio-locked meters have 'fake' integral beats,
1886 there is no pulse offset here.
1890 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1891 if (!(*i)->is_tempo()) {
1892 m = static_cast<MeterSection*> (*i);
1894 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1895 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1903 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1904 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1905 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1910 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
1911 * @param beat The BBT (meter-based) beat.
1912 * @return The BBT time (meter-based) at the supplied meter-based beat.
1916 TempoMap::bbt_at_beat (const double& beat)
1918 Glib::Threads::RWLock::ReaderLock lm (lock);
1919 return bbt_at_beat_locked (_metrics, beat);
1923 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1925 /* CALLER HOLDS READ LOCK */
1926 MeterSection* prev_m = 0;
1927 const double beats = max (0.0, b);
1929 MeterSection* m = 0;
1931 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1932 if (!(*i)->is_tempo()) {
1933 m = static_cast<MeterSection*> (*i);
1935 if (m->beat() > beats) {
1936 /* this is the meter after the one our beat is on*/
1945 const double beats_in_ms = beats - prev_m->beat();
1946 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1947 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1948 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1949 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1953 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1954 ret.beats = (uint32_t) floor (remaining_beats);
1955 ret.bars = total_bars;
1957 /* 0 0 0 to 1 1 0 - based mapping*/
1961 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1963 ret.ticks -= BBT_Time::ticks_per_beat;
1966 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1974 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
1975 * @param bbt The BBT time (meter-based).
1976 * @return the quarter note beat at the supplied BBT time
1978 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
1980 * while the input uses meter, the output does not.
1983 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
1985 Glib::Threads::RWLock::ReaderLock lm (lock);
1987 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
1991 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
1993 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1996 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
1999 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2003 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2005 /* CALLER HOLDS READ LOCK */
2007 MeterSection* prev_m = 0;
2009 /* because audio-locked meters have 'fake' integral beats,
2010 there is no pulse offset here.
2014 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2015 if (!(*i)->is_tempo()) {
2016 m = static_cast<MeterSection*> (*i);
2018 if (m->bbt().bars > bbt.bars) {
2026 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2027 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2028 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2033 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2034 * @param qn the quarter-note beat.
2035 * @return The BBT time (meter-based) at the supplied meter-based beat.
2037 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2041 TempoMap::bbt_at_quarter_note (const double& qn)
2043 Glib::Threads::RWLock::ReaderLock lm (lock);
2045 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2048 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2049 * @param metrics The list of metric sections used to determine the result.
2050 * @param pulse The whole-note pulse.
2051 * @return The BBT time at the supplied whole-note pulse.
2053 * a pulse or whole note is the basic musical position of a MetricSection.
2054 * it is equivalent to four quarter notes.
2055 * while the output uses meter, the input does not.
2058 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2060 MeterSection* prev_m = 0;
2062 MeterSection* m = 0;
2064 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2066 if (!(*i)->is_tempo()) {
2067 m = static_cast<MeterSection*> (*i);
2070 double const pulses_to_m = m->pulse() - prev_m->pulse();
2071 if (prev_m->pulse() + pulses_to_m > pulse) {
2072 /* this is the meter after the one our beat is on*/
2081 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2082 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2083 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2084 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2085 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2089 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2090 ret.beats = (uint32_t) floor (remaining_beats);
2091 ret.bars = total_bars;
2093 /* 0 0 0 to 1 1 0 mapping*/
2097 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2099 ret.ticks -= BBT_Time::ticks_per_beat;
2102 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2110 /** Returns the BBT time corresponding to the supplied frame position.
2111 * @param frame the position in audio samples.
2112 * @return the BBT time at the frame position .
2116 TempoMap::bbt_at_frame (framepos_t frame)
2123 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2126 Glib::Threads::RWLock::ReaderLock lm (lock);
2128 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2132 TempoMap::bbt_at_frame_rt (framepos_t frame)
2134 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2137 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2140 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2144 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2154 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2155 MeterSection* prev_m = 0;
2156 MeterSection* next_m = 0;
2160 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2161 if (!(*i)->is_tempo()) {
2162 m = static_cast<MeterSection*> (*i);
2163 if (prev_m && m->minute() > minute) {
2171 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2173 /* handle frame before first meter */
2174 if (minute < prev_m->minute()) {
2177 /* audio locked meters fake their beat */
2178 if (next_m && next_m->beat() < beat) {
2179 beat = next_m->beat();
2182 beat = max (0.0, beat);
2184 const double beats_in_ms = beat - prev_m->beat();
2185 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2186 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2187 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2188 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2192 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2193 ret.beats = (uint32_t) floor (remaining_beats);
2194 ret.bars = total_bars;
2196 /* 0 0 0 to 1 1 0 - based mapping*/
2200 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2202 ret.ticks -= BBT_Time::ticks_per_beat;
2205 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2213 /** Returns the frame position corresponding to the supplied BBT time.
2214 * @param bbt the position in BBT time.
2215 * @return the frame position at bbt.
2219 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2222 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2226 if (bbt.beats < 1) {
2227 throw std::logic_error ("beats are counted from one");
2229 Glib::Threads::RWLock::ReaderLock lm (lock);
2231 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
2234 /* meter & tempo section based */
2236 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2238 /* HOLD THE READER LOCK */
2240 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2245 * Returns the quarter-note beat position corresponding to the supplied frame.
2247 * @param frame the position in frames.
2248 * @return The quarter-note position of the supplied frame. Ignores meter.
2252 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2254 Glib::Threads::RWLock::ReaderLock lm (lock);
2256 const double ret = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
2262 TempoMap::quarter_note_at_minute_locked (const Metrics& metrics, const double minute) const
2264 const double ret = pulse_at_minute_locked (metrics, minute) * 4.0;
2270 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2272 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2275 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2278 const double ret = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
2284 * Returns the frame position corresponding to the supplied quarter-note beat.
2286 * @param quarter_note the quarter-note position.
2287 * @return the frame position of the supplied quarter-note. Ignores meter.
2292 TempoMap::frame_at_quarter_note (const double quarter_note) const
2294 Glib::Threads::RWLock::ReaderLock lm (lock);
2296 const framepos_t ret = frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note));
2302 TempoMap::minute_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2304 const double ret = minute_at_pulse_locked (metrics, quarter_note / 4.0);
2309 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2310 * @param beat The BBT (meter-based) beat.
2311 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2313 * a quarter-note may be compared with and assigned to Evoral::Beats.
2317 TempoMap::quarter_note_at_beat (const double beat)
2319 Glib::Threads::RWLock::ReaderLock lm (lock);
2321 const double ret = quarter_note_at_beat_locked (_metrics, beat);
2327 TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) const
2329 const double ret = pulse_at_beat_locked (metrics, beat) * 4.0;
2334 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2335 * @param quarter_note The position in quarter-note beats.
2336 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2338 * a quarter-note is the musical unit of Evoral::Beats.
2342 TempoMap::beat_at_quarter_note (const double quarter_note)
2344 Glib::Threads::RWLock::ReaderLock lm (lock);
2346 const double ret = beat_at_quarter_note_locked (_metrics, quarter_note);
2352 TempoMap::beat_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2355 return beat_at_pulse_locked (metrics, quarter_note / 4.0);
2358 /** Returns the duration in frames between two supplied quarter-note beat positions.
2359 * @param start the first position in quarter-note beats.
2360 * @param end the end position in quarter-note beats.
2361 * @return the frame distance ober the quarter-note beats duration.
2363 * use this rather than e.g.
2364 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2365 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2369 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2371 Glib::Threads::RWLock::ReaderLock lm (lock);
2373 return frame_at_minute (minutes_between_quarter_notes_locked (_metrics, start, end));
2377 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2380 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2384 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2386 Glib::Threads::RWLock::ReaderLock lm (lock);
2388 return quarter_notes_between_frames_locked (_metrics, start, end);
2392 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2394 const TempoSection* prev_t = 0;
2396 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2399 if ((*i)->is_tempo()) {
2400 t = static_cast<TempoSection*> (*i);
2404 if (prev_t && t->frame() > start) {
2410 const double start_qn = prev_t->pulse_at_frame (start);
2412 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2415 if ((*i)->is_tempo()) {
2416 t = static_cast<TempoSection*> (*i);
2420 if (prev_t && t->frame() > end) {
2426 const double end_qn = prev_t->pulse_at_frame (end);
2428 return (end_qn - start_qn) * 4.0;
2432 TempoMap::check_solved (const Metrics& metrics) const
2434 TempoSection* prev_t = 0;
2435 MeterSection* prev_m = 0;
2437 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2440 if ((*i)->is_tempo()) {
2441 t = static_cast<TempoSection*> (*i);
2446 /* check ordering */
2447 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2451 /* precision check ensures tempo and frames align.*/
2452 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))) {
2453 if (!t->locked_to_meter()) {
2458 /* gradient limit - who knows what it should be?
2459 things are also ok (if a little chaotic) without this
2461 if (fabs (prev_t->c_func()) > 1000.0) {
2462 //std::cout << "c : " << prev_t->c_func() << std::endl;
2469 if (!(*i)->is_tempo()) {
2470 m = static_cast<MeterSection*> (*i);
2471 if (prev_m && m->position_lock_style() == AudioTime) {
2472 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2473 const double nascent_m_minute = t->minute_at_pulse (m->pulse());
2474 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2476 if (t && (nascent_m_minute > m->minute() || nascent_m_minute < 0.0)) {
2490 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
2492 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2494 if ((*i)->is_tempo()) {
2495 t = static_cast<TempoSection*> (*i);
2496 if (!t->movable()) {
2497 t->set_active (true);
2500 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
2501 t->set_active (false);
2503 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
2504 t->set_active (true);
2505 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
2514 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2516 TempoSection* prev_t = 0;
2517 TempoSection* section_prev = 0;
2518 double first_m_minute = 0.0;
2520 /* can't move a tempo before the first meter */
2521 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2523 if (!(*i)->is_tempo()) {
2524 m = static_cast<MeterSection*> (*i);
2525 if (!m->movable()) {
2526 first_m_minute = m->minute();
2531 if (section->movable() && minute <= first_m_minute) {
2535 section->set_active (true);
2536 section->set_minute (minute);
2538 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2540 if ((*i)->is_tempo()) {
2541 t = static_cast<TempoSection*> (*i);
2548 section_prev = prev_t;
2549 if (t->locked_to_meter()) {
2554 if (t->position_lock_style() == MusicTime) {
2555 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2556 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2558 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2559 if (!t->locked_to_meter()) {
2560 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2569 section_prev->set_c_func (section_prev->compute_c_func_minute (section->note_types_per_minute(), minute));
2570 if (!section->locked_to_meter()) {
2571 section->set_pulse (section_prev->pulse_at_ntpm (section->note_types_per_minute(), minute));
2576 recompute_tempi (imaginary);
2578 if (check_solved (imaginary)) {
2581 dunp (imaginary, std::cout);
2585 MetricSectionFrameSorter fcmp;
2586 imaginary.sort (fcmp);
2588 recompute_tempi (imaginary);
2590 if (check_solved (imaginary)) {
2598 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2600 TempoSection* prev_t = 0;
2601 TempoSection* section_prev = 0;
2603 section->set_pulse (pulse);
2605 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2607 if ((*i)->is_tempo()) {
2608 t = static_cast<TempoSection*> (*i);
2612 if (!t->movable()) {
2619 section_prev = prev_t;
2622 if (t->position_lock_style() == MusicTime) {
2623 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2624 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2626 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2627 if (!t->locked_to_meter()) {
2628 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2637 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->note_types_per_minute(), pulse));
2638 section->set_minute (section_prev->minute_at_ntpm (section->note_types_per_minute(), pulse));
2642 recompute_tempi (imaginary);
2644 if (check_solved (imaginary)) {
2647 dunp (imaginary, std::cout);
2651 MetricSectionSorter cmp;
2652 imaginary.sort (cmp);
2654 recompute_tempi (imaginary);
2656 * XX need a restriction here, but only for this case,
2657 * as audio locked tempos don't interact in the same way.
2659 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2661 * |50 bpm |250 bpm |60 bpm
2662 * drag 250 to the pulse after 60->
2663 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2665 if (check_solved (imaginary)) {
2673 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2675 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2676 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2677 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->minute() >= minute)) {
2681 if (!section->movable()) {
2682 /* lock the first tempo to our first meter */
2683 if (!set_active_tempos (imaginary, section->frame_at_minute (minute))) {
2688 TempoSection* meter_locked_tempo = 0;
2690 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2692 if ((*ii)->is_tempo()) {
2693 t = static_cast<TempoSection*> (*ii);
2694 if ((t->locked_to_meter() || !t->movable()) && t->minute() == section->minute()) {
2695 meter_locked_tempo = t;
2701 if (!meter_locked_tempo) {
2705 MeterSection* prev_m = 0;
2707 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2708 bool solved = false;
2710 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2712 if (!(*i)->is_tempo()) {
2713 m = static_cast<MeterSection*> (*i);
2715 if (prev_m && section->movable()) {
2716 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2717 if (beats + prev_m->beat() < section->beat()) {
2718 /* set the section pulse according to its musical position,
2719 * as an earlier time than this has been requested.
2721 const double new_pulse = ((section->beat() - prev_m->beat())
2722 / prev_m->note_divisor()) + prev_m->pulse();
2724 tempo_copy->set_position_lock_style (MusicTime);
2725 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2726 meter_locked_tempo->set_position_lock_style (MusicTime);
2727 section->set_position_lock_style (MusicTime);
2728 section->set_pulse (new_pulse);
2729 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2730 meter_locked_tempo->set_position_lock_style (AudioTime);
2731 section->set_position_lock_style (AudioTime);
2732 section->set_minute (meter_locked_tempo->minute());
2738 Metrics::const_iterator d = future_map.begin();
2739 while (d != future_map.end()) {
2748 /* all is ok. set section's locked tempo if allowed.
2749 possibly disallowed if there is an adjacent audio-locked tempo.
2750 XX this check could possibly go. its never actually happened here.
2752 MeterSection* meter_copy = const_cast<MeterSection*>
2753 (&meter_section_at_minute_locked (future_map, section->minute()));
2755 meter_copy->set_minute (minute);
2757 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2758 section->set_minute (minute);
2759 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2760 / prev_m->note_divisor()) + prev_m->pulse());
2761 solve_map_minute (imaginary, meter_locked_tempo, minute);
2766 Metrics::const_iterator d = future_map.begin();
2767 while (d != future_map.end()) {
2777 /* not movable (first meter atm) */
2779 tempo_copy->set_minute (minute);
2780 tempo_copy->set_pulse (0.0);
2782 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2783 section->set_minute (minute);
2784 meter_locked_tempo->set_minute (minute);
2785 meter_locked_tempo->set_pulse (0.0);
2786 solve_map_minute (imaginary, meter_locked_tempo, minute);
2791 Metrics::const_iterator d = future_map.begin();
2792 while (d != future_map.end()) {
2801 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2802 section->set_beat (b_bbt);
2803 section->set_pulse (0.0);
2813 MetricSectionFrameSorter fcmp;
2814 imaginary.sort (fcmp);
2816 recompute_meters (imaginary);
2822 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2824 /* disallow setting section to an existing meter's bbt */
2825 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2827 if (!(*i)->is_tempo()) {
2828 m = static_cast<MeterSection*> (*i);
2829 if (m != section && m->bbt().bars == when.bars) {
2835 MeterSection* prev_m = 0;
2836 MeterSection* section_prev = 0;
2838 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2840 if (!(*i)->is_tempo()) {
2841 m = static_cast<MeterSection*> (*i);
2842 pair<double, BBT_Time> b_bbt;
2843 double new_pulse = 0.0;
2845 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2846 section_prev = prev_m;
2847 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2848 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2849 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2851 section->set_beat (b_bbt);
2852 section->set_pulse (pulse);
2853 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2858 if (m->position_lock_style() == AudioTime) {
2859 TempoSection* meter_locked_tempo = 0;
2861 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2863 if ((*ii)->is_tempo()) {
2864 t = static_cast<TempoSection*> (*ii);
2865 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2866 meter_locked_tempo = t;
2872 if (!meter_locked_tempo) {
2877 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2879 if (beats + prev_m->beat() != m->beat()) {
2880 /* tempo/ meter change caused a change in beat (bar). */
2881 b_bbt = make_pair (beats + prev_m->beat()
2882 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2883 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2884 } else if (m->movable()) {
2885 b_bbt = make_pair (m->beat(), m->bbt());
2886 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2889 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2892 meter_locked_tempo->set_pulse (new_pulse);
2893 m->set_beat (b_bbt);
2894 m->set_pulse (new_pulse);
2898 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2899 if (beats + prev_m->beat() != m->beat()) {
2900 /* tempo/ meter change caused a change in beat (bar). */
2901 b_bbt = make_pair (beats + prev_m->beat()
2902 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2904 b_bbt = make_pair (beats + prev_m->beat()
2907 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2908 m->set_beat (b_bbt);
2909 m->set_pulse (new_pulse);
2910 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
2917 if (!section_prev) {
2919 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2920 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2921 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2923 section->set_beat (b_bbt);
2924 section->set_pulse (pulse);
2925 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2928 MetricSectionSorter cmp;
2929 imaginary.sort (cmp);
2931 recompute_meters (imaginary);
2936 /** places a copy of _metrics into copy and returns a pointer
2937 * to section's equivalent in copy.
2940 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2942 TempoSection* ret = 0;
2944 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2947 if ((*i)->is_tempo()) {
2948 t = static_cast<TempoSection*> (*i);
2950 ret = new TempoSection (*t);
2951 copy.push_back (ret);
2955 TempoSection* cp = new TempoSection (*t);
2956 copy.push_back (cp);
2958 if (!(*i)->is_tempo()) {
2959 m = static_cast<MeterSection *> (*i);
2960 MeterSection* cp = new MeterSection (*m);
2961 copy.push_back (cp);
2969 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2971 MeterSection* ret = 0;
2973 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2976 if ((*i)->is_tempo()) {
2977 t = static_cast<TempoSection*> (*i);
2978 TempoSection* cp = new TempoSection (*t);
2979 copy.push_back (cp);
2982 if (!(*i)->is_tempo()) {
2983 m = static_cast<MeterSection *> (*i);
2985 ret = new MeterSection (*m);
2986 copy.push_back (ret);
2989 MeterSection* cp = new MeterSection (*m);
2990 copy.push_back (cp);
2997 /** answers the question "is this a valid beat position for this tempo section?".
2998 * it returns true if the tempo section can be moved to the requested bbt position,
2999 * leaving the tempo map in a solved state.
3000 * @param ts the tempo section to be moved
3001 * @param bbt the requested new position for the tempo section
3002 * @return true if the tempo section can be moved to the position, otherwise false.
3005 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3008 TempoSection* tempo_copy = 0;
3011 Glib::Threads::RWLock::ReaderLock lm (lock);
3012 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3018 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3020 Metrics::const_iterator d = copy.begin();
3021 while (d != copy.end()) {
3030 * 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,
3031 * taking any possible reordering as a consequence of this into account.
3032 * @param section - the section to be altered
3033 * @param bbt - the BBT time where the altered tempo will fall
3034 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3036 pair<double, framepos_t>
3037 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3040 pair<double, framepos_t> ret = make_pair (0.0, 0);
3042 Glib::Threads::RWLock::ReaderLock lm (lock);
3044 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3046 const double beat = beat_at_bbt_locked (future_map, bbt);
3048 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3049 ret.first = tempo_copy->pulse();
3050 ret.second = tempo_copy->frame();
3052 ret.first = section->pulse();
3053 ret.second = section->frame();
3056 Metrics::const_iterator d = future_map.begin();
3057 while (d != future_map.end()) {
3064 /** moves a TempoSection to a specified position.
3065 * @param ts - the section to be moved
3066 * @param frame - the new position in frames for the tempo
3067 * @param sub_num - the snap division to use if using musical time.
3069 * if sub_num is non-zero, the frame position is used to calculate an exact
3072 * -1 | snap to bars (meter-based)
3073 * 0 | no snap - use audio frame for musical position
3074 * 1 | snap to meter-based (BBT) beat
3075 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3077 * this follows the snap convention in the gui.
3078 * if sub_num is zero, the musical position will be taken from the supplied frame.
3081 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3085 if (ts->position_lock_style() == MusicTime) {
3087 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3088 Glib::Threads::RWLock::WriterLock lm (lock);
3089 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3091 tempo_copy->set_position_lock_style (AudioTime);
3093 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3094 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3095 const double pulse = pulse_at_beat_locked (future_map, beat);
3097 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3098 solve_map_pulse (_metrics, ts, pulse);
3099 recompute_meters (_metrics);
3107 Glib::Threads::RWLock::WriterLock lm (lock);
3108 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3110 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3112 /* We're moving the object that defines the grid while snapping to it...
3113 * Placing the ts at the beat corresponding to the requested frame may shift the
3114 * grid in such a way that the mouse is left hovering over a completerly different division,
3115 * causing jittering when the mouse next moves (esp. large tempo deltas).
3116 * To avoid this, place the ts at the requested frame in a dummy map
3117 * then find the closest beat subdivision to that frame in the dummy.
3118 * This alters the snap behaviour slightly in that we snap to beat divisions
3119 * in the future map rather than the existing one.
3121 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3122 const double pulse = pulse_at_beat_locked (future_map, beat);
3124 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3125 /* snapping to a grid. force MusicTime temporarily. */
3126 ts->set_position_lock_style (MusicTime);
3127 solve_map_pulse (_metrics, ts, pulse);
3128 ts->set_position_lock_style (AudioTime);
3130 recompute_meters (_metrics);
3133 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3134 recompute_meters (_metrics);
3140 Metrics::const_iterator d = future_map.begin();
3141 while (d != future_map.end()) {
3146 MetricPositionChanged (); // Emit Signal
3149 /** moves a MeterSection to a specified position.
3150 * @param ms - the section to be moved
3151 * @param frame - the new position in frames for the meter
3153 * as a meter cannot snap to anything but bars,
3154 * the supplied frame is rounded to the nearest bar, possibly
3155 * leaving the meter position unchanged.
3158 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
3162 if (ms->position_lock_style() == AudioTime) {
3165 Glib::Threads::RWLock::WriterLock lm (lock);
3166 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3168 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3169 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3170 recompute_tempi (_metrics);
3175 Glib::Threads::RWLock::WriterLock lm (lock);
3176 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3178 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3179 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3181 if (solve_map_bbt (future_map, copy, bbt)) {
3182 solve_map_bbt (_metrics, ms, bbt);
3183 recompute_tempi (_metrics);
3188 Metrics::const_iterator d = future_map.begin();
3189 while (d != future_map.end()) {
3194 MetricPositionChanged (); // Emit Signal
3198 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3201 bool can_solve = false;
3203 Glib::Threads::RWLock::WriterLock lm (lock);
3204 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3205 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3206 recompute_tempi (future_map);
3208 if (check_solved (future_map)) {
3209 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3210 recompute_map (_metrics);
3215 Metrics::const_iterator d = future_map.begin();
3216 while (d != future_map.end()) {
3221 MetricPositionChanged (); // Emit Signal
3227 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
3230 Ts (future prev_t) Tnext
3233 |----------|----------
3240 Glib::Threads::RWLock::WriterLock lm (lock);
3246 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3247 TempoSection* prev_to_prev_t = 0;
3248 const frameoffset_t fr_off = end_frame - frame;
3250 if (prev_t && prev_t->pulse() > 0.0) {
3251 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
3254 TempoSection* next_t = 0;
3255 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
3256 TempoSection* t = 0;
3257 if ((*i)->is_tempo()) {
3258 t = static_cast<TempoSection*> (*i);
3259 if (t->frame() > ts->frame()) {
3265 /* minimum allowed measurement distance in frames */
3266 const framepos_t min_dframe = 2;
3268 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3269 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3271 double contribution = 0.0;
3273 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3274 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3277 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3279 const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame));
3280 const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame));
3284 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
3286 if (prev_t->position_lock_style() == MusicTime) {
3287 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3288 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3290 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3291 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3293 new_bpm = prev_t->note_types_per_minute();
3296 /* prev to prev is irrelevant */
3298 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3299 new_bpm = prev_t->note_types_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3301 new_bpm = prev_t->note_types_per_minute();
3306 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3307 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3309 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3310 / (double) ((end_frame) - prev_to_prev_t->frame()));
3312 new_bpm = prev_t->note_types_per_minute();
3315 /* prev_to_prev_t is irrelevant */
3317 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3318 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3320 new_bpm = prev_t->note_types_per_minute();
3326 double frame_ratio = 1.0;
3327 double pulse_ratio = 1.0;
3328 const double pulse_pos = frame;
3330 if (prev_to_prev_t) {
3331 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3332 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3334 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3335 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3338 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3339 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3341 pulse_ratio = (start_pulse / end_pulse);
3343 new_bpm = prev_t->note_types_per_minute() * (pulse_ratio * frame_ratio);
3346 /* don't clamp and proceed here.
3347 testing has revealed that this can go negative,
3348 which is an entirely different thing to just being too low.
3350 if (new_bpm < 0.5) {
3353 new_bpm = min (new_bpm, (double) 1000.0);
3354 prev_t->set_note_types_per_minute (new_bpm);
3355 recompute_tempi (future_map);
3356 recompute_meters (future_map);
3358 if (check_solved (future_map)) {
3359 ts->set_note_types_per_minute (new_bpm);
3360 recompute_tempi (_metrics);
3361 recompute_meters (_metrics);
3365 Metrics::const_iterator d = future_map.begin();
3366 while (d != future_map.end()) {
3371 MetricPositionChanged (); // Emit Signal
3374 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3375 * the supplied frame, possibly returning a negative value.
3377 * @param frame The session frame position.
3378 * @param sub_num The subdivision to use when rounding the beat.
3379 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3380 * Positive integers indicate quarter note (non BBT) divisions.
3381 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3382 * @return The beat position of the supplied frame.
3384 * when working to a musical grid, the use of sub_nom indicates that
3385 * the position should be interpreted musically.
3387 * it effectively snaps to meter bars, meter beats or quarter note divisions
3388 * (as per current gui convention) and returns a musical position independent of frame rate.
3390 * If the supplied frame lies before the first meter, the return will be negative,
3391 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3392 * the continuation of the tempo curve (backwards).
3394 * This function is sensitive to tempo and meter.
3397 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num)
3399 Glib::Threads::RWLock::ReaderLock lm (lock);
3401 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3405 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions)
3407 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3410 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3411 * the supplied frame, possibly returning a negative value.
3413 * @param frame The session frame position.
3414 * @param sub_num The subdivision to use when rounding the quarter note.
3415 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3416 * Positive integers indicate quarter note (non BBT) divisions.
3417 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3418 * @return The quarter note position of the supplied frame.
3420 * When working to a musical grid, the use of sub_nom indicates that
3421 * the frame position should be interpreted musically.
3423 * it effectively snaps to meter bars, meter beats or quarter note divisions
3424 * (as per current gui convention) and returns a musical position independent of frame rate.
3426 * If the supplied frame lies before the first meter, the return will be negative,
3427 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3428 * the continuation of the tempo curve (backwards).
3430 * This function is tempo-sensitive.
3433 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num)
3435 Glib::Threads::RWLock::ReaderLock lm (lock);
3437 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3441 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num)
3443 double qn = quarter_note_at_minute_locked (metrics, minute_at_frame (frame));
3446 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3447 } else if (sub_num == 1) {
3448 /* the gui requested exact musical (BBT) beat */
3449 qn = quarter_note_at_beat_locked (metrics, floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5));
3450 } else if (sub_num == -1) {
3452 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3456 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3458 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3460 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3470 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3471 * @param pos the frame position in the tempo map.
3472 * @param bbt the distance in BBT time from pos to calculate.
3473 * @param dir the rounding direction..
3474 * @return the duration in frames between pos and bbt
3477 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3479 Glib::Threads::RWLock::ReaderLock lm (lock);
3481 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3482 const framecnt_t offset = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3483 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3486 pos_bbt.bars += bbt.bars;
3488 pos_bbt.ticks += bbt.ticks;
3489 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3491 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3494 pos_bbt.beats += bbt.beats;
3495 if ((double) pos_bbt.beats > divisions) {
3497 pos_bbt.beats -= divisions;
3500 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt)) - offset;
3502 pos_bbt.bars -= bbt.bars;
3504 if (pos_bbt.ticks < bbt.ticks) {
3505 if (pos_bbt.beats == 1) {
3507 pos_bbt.beats = divisions;
3511 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3513 pos_bbt.ticks -= bbt.ticks;
3516 if (pos_bbt.beats <= bbt.beats) {
3518 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3520 pos_bbt.beats -= bbt.beats;
3523 return offset - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3530 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3532 return round_to_type (fr, dir, Bar);
3536 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3538 return round_to_type (fr, dir, Beat);
3542 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3544 Glib::Threads::RWLock::ReaderLock lm (lock);
3545 uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3546 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3547 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3549 ticks -= beats * BBT_Time::ticks_per_beat;
3552 /* round to next (or same iff dir == RoundUpMaybe) */
3554 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3556 if (mod == 0 && dir == RoundUpMaybe) {
3557 /* right on the subdivision, which is fine, so do nothing */
3559 } else if (mod == 0) {
3560 /* right on the subdivision, so the difference is just the subdivision ticks */
3561 ticks += ticks_one_subdivisions_worth;
3564 /* not on subdivision, compute distance to next subdivision */
3566 ticks += ticks_one_subdivisions_worth - mod;
3569 if (ticks >= BBT_Time::ticks_per_beat) {
3570 ticks -= BBT_Time::ticks_per_beat;
3572 } else if (dir < 0) {
3574 /* round to previous (or same iff dir == RoundDownMaybe) */
3576 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3578 if (difference == 0 && dir == RoundDownAlways) {
3579 /* right on the subdivision, but force-rounding down,
3580 so the difference is just the subdivision ticks */
3581 difference = ticks_one_subdivisions_worth;
3584 if (ticks < difference) {
3585 ticks = BBT_Time::ticks_per_beat - ticks;
3587 ticks -= difference;
3591 /* round to nearest */
3594 /* compute the distance to the previous and next subdivision */
3596 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3598 /* closer to the next subdivision, so shift forward */
3600 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3602 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3604 if (ticks > BBT_Time::ticks_per_beat) {
3606 ticks -= BBT_Time::ticks_per_beat;
3607 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3610 } else if (rem > 0) {
3612 /* closer to previous subdivision, so shift backward */
3616 /* can't go backwards past zero, so ... */
3619 /* step back to previous beat */
3621 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3622 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3624 ticks = lrint (ticks - rem);
3625 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3628 /* on the subdivision, do nothing */
3632 const framepos_t ret_frame = frame_at_minute (minute_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3638 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3640 Glib::Threads::RWLock::ReaderLock lm (lock);
3641 uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3642 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3643 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3645 ticks -= beats * BBT_Time::ticks_per_beat;
3648 /* round to next (or same iff dir == RoundUpMaybe) */
3650 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3652 if (mod == 0 && dir == RoundUpMaybe) {
3653 /* right on the subdivision, which is fine, so do nothing */
3655 } else if (mod == 0) {
3656 /* right on the subdivision, so the difference is just the subdivision ticks */
3657 ticks += ticks_one_subdivisions_worth;
3660 /* not on subdivision, compute distance to next subdivision */
3662 ticks += ticks_one_subdivisions_worth - mod;
3665 if (ticks >= BBT_Time::ticks_per_beat) {
3666 ticks -= BBT_Time::ticks_per_beat;
3668 } else if (dir < 0) {
3670 /* round to previous (or same iff dir == RoundDownMaybe) */
3672 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3674 if (difference == 0 && dir == RoundDownAlways) {
3675 /* right on the subdivision, but force-rounding down,
3676 so the difference is just the subdivision ticks */
3677 difference = ticks_one_subdivisions_worth;
3680 if (ticks < difference) {
3681 ticks = BBT_Time::ticks_per_beat - ticks;
3683 ticks -= difference;
3687 /* round to nearest */
3690 /* compute the distance to the previous and next subdivision */
3692 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3694 /* closer to the next subdivision, so shift forward */
3696 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3698 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3700 if (ticks > BBT_Time::ticks_per_beat) {
3702 ticks -= BBT_Time::ticks_per_beat;
3703 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3706 } else if (rem > 0) {
3708 /* closer to previous subdivision, so shift backward */
3712 /* can't go backwards past zero, so ... */
3715 /* step back to previous beat */
3717 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3718 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3720 ticks = lrint (ticks - rem);
3721 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3724 /* on the subdivision, do nothing */
3728 const framepos_t ret_frame = frame_at_minute (minute_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3734 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3736 Glib::Threads::RWLock::ReaderLock lm (lock);
3738 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (frame)));
3739 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3744 /* find bar previous to 'frame' */
3747 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3749 } else if (dir > 0) {
3750 /* find bar following 'frame' */
3754 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3756 /* true rounding: find nearest bar */
3757 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3760 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3762 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3764 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3775 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
3776 } else if (dir > 0) {
3777 return frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
3779 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
3788 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3789 framepos_t lower, framepos_t upper, uint32_t bar_mod)
3791 Glib::Threads::RWLock::ReaderLock lm (lock);
3792 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
3794 /* although the map handles negative beats, bbt doesn't. */
3799 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
3803 while (pos >= 0 && pos < upper) {
3804 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
3805 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3806 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3807 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3808 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3812 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
3817 bbt.bars -= bbt.bars % bar_mod;
3821 while (pos >= 0 && pos < upper) {
3822 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3823 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3824 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3825 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3826 bbt.bars += bar_mod;
3832 TempoMap::tempo_section_at_frame (framepos_t frame) const
3834 Glib::Threads::RWLock::ReaderLock lm (lock);
3836 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
3840 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
3842 TempoSection* prev = 0;
3846 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3848 if ((*i)->is_tempo()) {
3849 t = static_cast<TempoSection*> (*i);
3853 if (prev && t->minute() > minute) {
3863 abort(); /*NOTREACHED*/
3870 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3872 TempoSection* prev_t = 0;
3873 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3877 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3878 if ((*i)->is_tempo()) {
3879 t = static_cast<TempoSection*> (*i);
3880 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3890 /* don't use this to calculate length (the tempo is only correct for this frame).
3891 do that stuff based on the beat_at_frame and frame_at_beat api
3894 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
3896 Glib::Threads::RWLock::ReaderLock lm (lock);
3898 const TempoSection* ts_at = 0;
3899 const TempoSection* ts_after = 0;
3900 Metrics::const_iterator i;
3903 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3905 if ((*i)->is_tempo()) {
3906 t = static_cast<TempoSection*> (*i);
3910 if (ts_at && (*i)->frame() > frame) {
3919 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
3921 /* must be treated as constant tempo */
3922 return ts_at->frames_per_quarter_note (_frame_rate);
3926 TempoMap::meter_section_at_frame (framepos_t frame) const
3928 Glib::Threads::RWLock::ReaderLock lm (lock);
3929 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
3933 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
3935 Metrics::const_iterator i;
3936 MeterSection* prev = 0;
3940 for (i = metrics.begin(); i != metrics.end(); ++i) {
3942 if (!(*i)->is_tempo()) {
3943 m = static_cast<MeterSection*> (*i);
3945 if (prev && (*i)->minute() > minute) {
3955 abort(); /*NOTREACHED*/
3962 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3964 MeterSection* prev_m = 0;
3966 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3968 if (!(*i)->is_tempo()) {
3969 m = static_cast<MeterSection*> (*i);
3970 if (prev_m && m->beat() > beat) {
3981 TempoMap::meter_section_at_beat (double beat) const
3983 Glib::Threads::RWLock::ReaderLock lm (lock);
3984 return meter_section_at_beat_locked (_metrics, beat);
3988 TempoMap::meter_at_frame (framepos_t frame) const
3990 TempoMetric m (metric_at (frame));
3995 TempoMap::fix_legacy_session ()
3997 MeterSection* prev_m = 0;
3998 TempoSection* prev_t = 0;
4000 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4004 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4005 if (!m->movable()) {
4006 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4009 m->set_minute (0.0);
4010 m->set_position_lock_style (AudioTime);
4015 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4016 + (m->bbt().beats - 1)
4017 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4019 m->set_beat (start);
4020 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4021 + (m->bbt().beats - 1)
4022 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4023 m->set_pulse (start_beat / prev_m->note_divisor());
4026 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4032 if (!t->movable()) {
4034 t->set_minute (0.0);
4035 t->set_position_lock_style (AudioTime);
4041 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4042 + (t->legacy_bbt().beats - 1)
4043 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4045 t->set_pulse (beat / prev_m->note_divisor());
4047 /* really shouldn't happen but.. */
4048 t->set_pulse (beat / 4.0);
4057 TempoMap::get_state ()
4059 Metrics::const_iterator i;
4060 XMLNode *root = new XMLNode ("TempoMap");
4063 Glib::Threads::RWLock::ReaderLock lm (lock);
4064 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4065 root->add_child_nocopy ((*i)->get_state());
4073 TempoMap::set_state (const XMLNode& node, int /*version*/)
4076 Glib::Threads::RWLock::WriterLock lm (lock);
4079 XMLNodeConstIterator niter;
4080 Metrics old_metrics (_metrics);
4083 nlist = node.children();
4085 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4086 XMLNode* child = *niter;
4088 if (child->name() == TempoSection::xml_state_node_name) {
4091 TempoSection* ts = new TempoSection (*child, _frame_rate);
4092 _metrics.push_back (ts);
4095 catch (failed_constructor& err){
4096 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4097 _metrics = old_metrics;
4098 old_metrics.clear();
4102 } else if (child->name() == MeterSection::xml_state_node_name) {
4105 MeterSection* ms = new MeterSection (*child, _frame_rate);
4106 _metrics.push_back (ms);
4109 catch (failed_constructor& err) {
4110 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4111 _metrics = old_metrics;
4112 old_metrics.clear();
4118 if (niter == nlist.end()) {
4119 MetricSectionSorter cmp;
4120 _metrics.sort (cmp);
4123 /* check for legacy sessions where bbt was the base musical unit for tempo */
4124 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4126 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4127 if (t->legacy_bbt().bars != 0) {
4128 fix_legacy_session();
4135 /* check for multiple tempo/meters at the same location, which
4136 ardour2 somehow allowed.
4139 Metrics::iterator prev = _metrics.end();
4140 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4141 if (prev != _metrics.end()) {
4143 MeterSection* prev_m;
4145 TempoSection* prev_t;
4146 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4147 if (prev_m->pulse() == ms->pulse()) {
4148 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4149 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4152 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4153 if (prev_t->pulse() == ts->pulse()) {
4154 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4155 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4163 recompute_map (_metrics);
4165 Metrics::const_iterator d = old_metrics.begin();
4166 while (d != old_metrics.end()) {
4170 old_metrics.clear ();
4173 PropertyChanged (PropertyChange ());
4179 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
4181 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4182 const MeterSection* m;
4183 const TempoSection* t;
4184 const TempoSection* prev_t = 0;
4186 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4188 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4189 o << "Tempo @ " << *i << t->note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4190 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4191 << " minute= " << t->minute() << " frame= " << t->frame() << " (movable? " << t->movable() << ')'
4192 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4194 o << std::setprecision (17) << " current : " << t->note_types_per_minute()
4195 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4196 o << " previous : " << prev_t->note_types_per_minute()
4197 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4198 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4199 << " | " << prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())
4200 << " | " << frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))
4201 << " | " << prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()) << std::endl;
4204 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4205 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4206 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4207 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
4210 o << "------" << std::endl;
4214 TempoMap::n_tempos() const
4216 Glib::Threads::RWLock::ReaderLock lm (lock);
4219 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4220 if ((*i)->is_tempo()) {
4229 TempoMap::n_meters() const
4231 Glib::Threads::RWLock::ReaderLock lm (lock);
4234 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4235 if (!(*i)->is_tempo()) {
4244 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4246 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4247 if ((*i)->frame() >= where && (*i)->movable ()) {
4251 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4252 gui_move_meter (ms, (*i)->frame() + amount);
4255 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4256 gui_move_tempo (ts, (*i)->frame() + amount, 0);
4261 PropertyChanged (PropertyChange ());
4265 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4269 std::list<MetricSection*> metric_kill_list;
4271 TempoSection* last_tempo = NULL;
4272 MeterSection* last_meter = NULL;
4273 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4274 bool meter_after = false; // is there a meter marker likewise?
4276 Glib::Threads::RWLock::WriterLock lm (lock);
4277 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4278 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4279 metric_kill_list.push_back(*i);
4280 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4283 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4287 else if ((*i)->frame() >= where) {
4288 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4289 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4290 if ((*i)->frame() == where) {
4291 // marker was immediately after end of range
4292 tempo_after = dynamic_cast<TempoSection*> (*i);
4293 meter_after = dynamic_cast<MeterSection*> (*i);
4299 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4300 if (last_tempo && !tempo_after) {
4301 metric_kill_list.remove(last_tempo);
4302 last_tempo->set_minute (minute_at_frame (where));
4305 if (last_meter && !meter_after) {
4306 metric_kill_list.remove(last_meter);
4307 last_meter->set_minute (minute_at_frame (where));
4311 //remove all the remaining metrics
4312 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4313 _metrics.remove(*i);
4318 recompute_map (_metrics);
4321 PropertyChanged (PropertyChange ());
4325 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4326 * pos can be -ve, if required.
4329 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4331 Glib::Threads::RWLock::ReaderLock lm (lock);
4332 const double frame_qn = quarter_notes_between_frames_locked (_metrics, 0, frame);
4334 return frame_at_minute (minute_at_quarter_note_locked (_metrics, frame_qn + beats.to_double()));
4338 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4340 Glib::Threads::RWLock::ReaderLock lm (lock);
4342 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4343 pos_bbt.ticks += op.ticks;
4344 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4346 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4348 pos_bbt.beats += op.beats;
4349 /* the meter in effect will start on the bar */
4350 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();
4351 while (pos_bbt.beats >= divisions_per_bar + 1) {
4353 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4354 pos_bbt.beats -= divisions_per_bar;
4356 pos_bbt.bars += op.bars;
4358 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4361 /** Count the number of beats that are equivalent to distance when going forward,
4365 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4367 Glib::Threads::RWLock::ReaderLock lm (lock);
4369 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4373 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4379 operator<< (std::ostream& o, const Meter& m) {
4380 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4384 operator<< (std::ostream& o, const Tempo& t) {
4385 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4389 operator<< (std::ostream& o, const MetricSection& section) {
4391 o << "MetricSection @ " << section.frame() << ' ';
4393 const TempoSection* ts;
4394 const MeterSection* ms;
4396 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4397 o << *((const Tempo*) ts);
4398 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4399 o << *((const Meter*) ms);