2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0, 4.0);
51 MetricSection::frame_at_minute (const double& time) const
53 return (framepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
57 MetricSection::minute_at_frame (const framepos_t& frame) const
59 return (frame / (double) _sample_rate) / 60.0;
62 /***********************************************************************/
65 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
67 /* This is tempo- and meter-sensitive. The number it returns
68 is based on the interval between any two lines in the
69 grid that is constructed from tempo and meter sections.
71 The return value IS NOT interpretable in terms of "beats".
74 return (60.0 * sr) / (tempo.note_types_per_minute() * (_note_type/tempo.note_type()));
78 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
80 return frames_per_grid (tempo, sr) * _divisions_per_bar;
83 /***********************************************************************/
85 const string TempoSection::xml_state_node_name = "Tempo";
87 TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate)
88 : MetricSection (0.0, 0, MusicTime, true, sample_rate)
89 , Tempo (TempoMap::default_tempo())
92 , _locked_to_meter (false)
94 XMLProperty const * prop;
100 _legacy_bbt = BBT_Time (0, 0, 0);
102 if ((prop = node.property ("start")) != 0) {
103 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
107 /* legacy session - start used to be in bbt*/
110 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
114 if ((prop = node.property ("pulse")) != 0) {
115 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
116 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
122 if ((prop = node.property ("frame")) != 0) {
123 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
124 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
125 throw failed_constructor();
127 set_minute (minute_at_frame (frame));
131 /* XX replace old beats-per-minute name with note-types-per-minute */
132 if ((prop = node.property ("beats-per-minute")) != 0) {
133 if (sscanf (prop->value().c_str(), "%lf", &_note_types_per_minute) != 1 || _note_types_per_minute < 0.0) {
134 error << _("TempoSection XML node has an illegal \"beats-per-minute\" value") << endmsg;
135 throw failed_constructor();
139 if ((prop = node.property ("note-type")) == 0) {
140 /* older session, make note type be quarter by default */
143 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
144 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
145 throw failed_constructor();
149 if ((prop = node.property ("movable")) == 0) {
150 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
151 throw failed_constructor();
154 set_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 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
241 * there should be no ramp between the two even if we are ramped.
242 * in other words a ramp should only place a curve on note_types_per_minute.
243 * we should be able to use Tempo note type here, but the above
244 * complicates things a bit.
247 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
249 if (_type == Constant || _c_func == 0.0) {
250 return ((p - pulse()) / pulses_per_minute()) + minute();
253 return _time_at_tempo (ntpm) + minute();
256 /** returns the Tempo at the supplied whole-note pulse.
259 TempoSection::tempo_at_pulse (const double& p) const
262 if (_type == Constant || _c_func == 0.0) {
263 return Tempo (note_types_per_minute(), note_type());
266 return Tempo (_tempo_at_pulse (p - pulse()), _note_type);
269 /** returns the whole-note pulse where a tempo in note types per minute occurs.
270 * constant tempi require minute m.
271 * @param ntpm the note types per minute value used to calculate the returned pulse
272 * @param m the minute used to calculate the returned pulse if the tempo is constant
273 * @return the whole-note pulse at the supplied tempo
275 * note that note_type is currently ignored in this function. see minute_at_tempo().
277 * for constant tempi, this is anaologous to pulse_at_minute().
280 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
282 if (_type == Constant || _c_func == 0.0) {
283 return ((m - minute()) * pulses_per_minute()) + pulse();
286 return _pulse_at_tempo (ntpm) + pulse();
289 /** returns the whole-note pulse at the supplied session-relative minute.
292 TempoSection::pulse_at_minute (const double& m) const
294 if (_type == Constant || _c_func == 0.0) {
295 return ((m - minute()) * pulses_per_minute()) + pulse();
298 return _pulse_at_time (m - minute()) + pulse();
301 /** returns the session-relative minute at the supplied whole-note pulse.
304 TempoSection::minute_at_pulse (const double& p) const
306 if (_type == Constant || _c_func == 0.0) {
307 return ((p - pulse()) / pulses_per_minute()) + minute();
310 return _time_at_pulse (p - pulse()) + minute();
313 /** returns thw whole-note pulse at session frame position f.
314 * @param f the frame position.
315 * @return the position in whole-note pulses corresponding to f
317 * for use with musical units whose granularity is coarser than frames (e.g. ticks)
320 TempoSection::pulse_at_frame (const framepos_t& f) const
322 if (_type == Constant || _c_func == 0.0) {
323 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
326 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
330 TempoSection::frame_at_pulse (const double& p) const
332 if (_type == Constant || _c_func == 0.0) {
333 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
336 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
344 Tt----|-----------------*|
345 Ta----|--------------|* |
351 _______________|___|____
352 time a t (next tempo)
355 Duration in beats at time a is the integral of some Tempo function.
356 In our case, the Tempo function (Tempo at time t) is
359 with function constant
364 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
365 b(t) = T0(e^(ct) - 1) / c
367 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:
368 t(b) = log((c.b / T0) + 1) / c
370 The time t at which Tempo T occurs is a as above:
371 t(T) = log(T / T0) / c
373 The beat at which a Tempo T occurs is:
376 The Tempo at which beat b occurs is:
379 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
380 Our problem is that we usually don't know t.
381 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.
382 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
383 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
385 By substituting our expanded t as a in the c function above, our problem is reduced to:
386 c = T0 (e^(log (Ta / T0)) - 1) / b
388 Of course the word 'beat' has been left loosely defined above.
389 In music, a beat is defined by the musical pulse (which comes from the tempo)
390 and the meter in use at a particular time (how many pulse divisions there are in one bar).
391 It would be more accurate to substitute the work 'pulse' for 'beat' above.
395 We can now store c for future time calculations.
396 If the following tempo section (the one that defines c in conjunction with this one)
397 is changed or moved, c is no longer valid.
399 The public methods are session-relative.
401 Most of this stuff is taken from this paper:
404 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
407 Zurich University of Arts
408 Institute for Computer Music and Sound Technology
410 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
414 /** compute this ramp's function constant from some tempo-pulse point
415 * @param end_npm end tempo (in note types per minute)
416 * @param end_pulse duration (pulses into global start) of some other position.
417 * @return the calculated function constant
420 TempoSection::compute_c_func_pulse (const double& end_npm, const double& end_pulse) const
422 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
423 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
426 /** compute the function constant from some tempo-time point.
427 * @param end_npm tempo (note types/min.)
428 * @param end_minute distance (in minutes) from session origin
429 * @return the calculated function constant
432 TempoSection::compute_c_func_minute (const double& end_npm, const double& end_minute) const
434 return c_func (end_npm, end_minute - minute());
437 /* position function */
439 TempoSection::a_func (double end_npm, double c_func) const
441 return log (end_npm / note_types_per_minute()) / c_func;
444 /*function constant*/
446 TempoSection::c_func (double end_npm, double end_time) const
448 return log (end_npm / note_types_per_minute()) / end_time;
451 /* tempo in note types per minute at time in minutes */
453 TempoSection::_tempo_at_time (const double& time) const
455 return exp (_c_func * time) * note_types_per_minute();
458 /* time in minutes at tempo in note types per minute */
460 TempoSection::_time_at_tempo (const double& npm) const
462 return log (npm / note_types_per_minute()) / _c_func;
465 /* pulse at tempo in note types per minute */
467 TempoSection::_pulse_at_tempo (const double& npm) const
469 return ((npm - note_types_per_minute()) / _c_func) / _note_type;
472 /* tempo in note types per minute at pulse */
474 TempoSection::_tempo_at_pulse (const double& pulse) const
476 return (pulse * _note_type * _c_func) + note_types_per_minute();
479 /* pulse at time in minutes */
481 TempoSection::_pulse_at_time (const double& time) const
483 return (expm1 (_c_func * time) * (note_types_per_minute() / _c_func)) / _note_type;
486 /* time in minutes at pulse */
488 TempoSection::_time_at_pulse (const double& pulse) const
490 return log1p ((_c_func * pulse * _note_type) / note_types_per_minute()) / _c_func;
493 /***********************************************************************/
495 const string MeterSection::xml_state_node_name = "Meter";
497 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
498 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
500 XMLProperty const * prop;
505 framepos_t frame = 0;
506 pair<double, BBT_Time> start;
508 if ((prop = node.property ("start")) != 0) {
509 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
513 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
515 /* legacy session - start used to be in bbt*/
516 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
521 if ((prop = node.property ("pulse")) != 0) {
522 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
523 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
528 if ((prop = node.property ("beat")) != 0) {
529 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
530 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
536 if ((prop = node.property ("bbt")) == 0) {
537 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
538 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
542 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
543 throw failed_constructor();
549 if ((prop = node.property ("frame")) != 0) {
550 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
551 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
552 throw failed_constructor();
554 set_minute (minute_at_frame (frame));
558 /* beats-per-bar is old; divisions-per-bar is new */
560 if ((prop = node.property ("divisions-per-bar")) == 0) {
561 if ((prop = node.property ("beats-per-bar")) == 0) {
562 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
563 throw failed_constructor();
566 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
567 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
568 throw failed_constructor();
571 if ((prop = node.property ("note-type")) == 0) {
572 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
573 throw failed_constructor();
575 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
576 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
577 throw failed_constructor();
580 if ((prop = node.property ("movable")) == 0) {
581 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
582 throw failed_constructor();
585 set_movable (string_is_affirmative (prop->value()));
587 if ((prop = node.property ("lock-style")) == 0) {
588 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
590 set_position_lock_style (MusicTime);
592 set_position_lock_style (AudioTime);
595 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
600 MeterSection::get_state() const
602 XMLNode *root = new XMLNode (xml_state_node_name);
606 snprintf (buf, sizeof (buf), "%lf", pulse());
607 root->add_property ("pulse", buf);
608 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
612 root->add_property ("bbt", buf);
613 snprintf (buf, sizeof (buf), "%lf", beat());
614 root->add_property ("beat", buf);
615 snprintf (buf, sizeof (buf), "%lf", _note_type);
616 root->add_property ("note-type", buf);
617 snprintf (buf, sizeof (buf), "%li", frame());
618 root->add_property ("frame", buf);
619 root->add_property ("lock-style", enum_2_string (position_lock_style()));
620 snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
621 root->add_property ("divisions-per-bar", buf);
622 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
623 root->add_property ("movable", buf);
628 /***********************************************************************/
632 Tempo determines the rate of musical pulse determined by its components
633 note types per minute - the rate per minute of the whole note divisor _note_type
634 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
635 Meter divides the musical pulse into measures and beats according to its components
639 TempoSection - translates between time, musical pulse and tempo.
640 has a musical location in whole notes (pulses).
641 has a time location in minutes.
642 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
643 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
645 MeterSection - translates between BBT, meter-based beat and musical pulse.
646 has a musical location in whole notes (pulses)
647 has a musical location in meter-based beats
648 has a musical location in BBT time
649 has a time location expressed in minutes.
651 TempoSection and MeterSection may be locked to either audio or music (position lock style).
652 The lock style determines the location type to be kept as a reference when location is recalculated.
654 The first tempo and meter are special. they must move together, and are locked to audio.
655 Audio locked tempi which lie before the first meter are made inactive.
657 Recomputing the map is the process where the 'missing' location types are calculated.
658 We construct the tempo map by first using the locked location type of each section
659 to determine non-locked location types (pulse or minute position).
660 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
662 Having done this, we can now traverse the Metrics list by pulse or minute
663 to query its relevant meter/tempo.
665 It is important to keep the _metrics in an order that makes sense.
666 Because ramped MusicTime and AudioTime tempos can interact with each other,
667 reordering is frequent. Care must be taken to keep _metrics in a solved state.
668 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
672 Music and audio-locked objects may seem interchangeable on the surface, but when translating
673 between audio samples and beat, remember that a sample is only a quantised approximation
674 of the actual time (in minutes) of a beat.
675 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
676 mean that this frame is the actual location in time of 1|3|0.
678 You cannot use a frame measurement to determine beat distance except under special circumstances
679 (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).
681 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
682 sample space the user is operating at to be translated correctly to the object.
684 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
685 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
686 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
688 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
690 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
691 The result is rounded to audio frames.
692 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
695 frame_at_beat (beat_at_frame (frame)) == frame
697 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
699 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
700 frames_between_quarter_notes () eliminats this effect when determining time duration
701 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
703 The above pointless example could instead do:
704 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
706 The Shaggs - Things I Wonder
707 https://www.youtube.com/watch?v=9wQK6zMJOoQ
710 struct MetricSectionSorter {
711 bool operator() (const MetricSection* a, const MetricSection* b) {
712 return a->pulse() < b->pulse();
716 struct MetricSectionFrameSorter {
717 bool operator() (const MetricSection* a, const MetricSection* b) {
718 return a->frame() < b->frame();
722 TempoMap::TempoMap (framecnt_t fr)
725 BBT_Time start (1, 1, 0);
727 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo.note_types_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime, fr);
728 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
730 t->set_movable (false);
731 m->set_movable (false);
733 /* note: frame time is correct (zero) for both of these */
735 _metrics.push_back (t);
736 _metrics.push_back (m);
740 TempoMap::~TempoMap ()
742 Metrics::const_iterator d = _metrics.begin();
743 while (d != _metrics.end()) {
751 TempoMap::frame_at_minute (const double time) const
753 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
757 TempoMap::minute_at_frame (const framepos_t frame) const
759 return (frame / (double) _frame_rate) / 60.0;
763 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
765 bool removed = false;
768 Glib::Threads::RWLock::WriterLock lm (lock);
769 if ((removed = remove_tempo_locked (tempo))) {
770 if (complete_operation) {
771 recompute_map (_metrics);
776 if (removed && complete_operation) {
777 PropertyChanged (PropertyChange ());
782 TempoMap::remove_tempo_locked (const TempoSection& tempo)
786 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
787 if (dynamic_cast<TempoSection*> (*i) != 0) {
788 if (tempo.frame() == (*i)->frame()) {
789 if ((*i)->movable()) {
802 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
804 bool removed = false;
807 Glib::Threads::RWLock::WriterLock lm (lock);
808 if ((removed = remove_meter_locked (tempo))) {
809 if (complete_operation) {
810 recompute_map (_metrics);
815 if (removed && complete_operation) {
816 PropertyChanged (PropertyChange ());
821 TempoMap::remove_meter_locked (const MeterSection& meter)
824 if (meter.position_lock_style() == AudioTime) {
825 /* remove meter-locked tempo */
826 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
828 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
829 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
838 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
839 if (dynamic_cast<MeterSection*> (*i) != 0) {
840 if (meter.frame() == (*i)->frame()) {
841 if ((*i)->movable()) {
854 TempoMap::do_insert (MetricSection* section)
856 bool need_add = true;
857 /* we only allow new meters to be inserted on beat 1 of an existing
861 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
863 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
865 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
866 corrected.second.beats = 1;
867 corrected.second.ticks = 0;
868 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
869 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
870 m->bbt(), corrected.second) << endmsg;
871 //m->set_pulse (corrected);
875 /* Look for any existing MetricSection that is of the same type and
876 in the same bar as the new one, and remove it before adding
877 the new one. Note that this means that if we find a matching,
878 existing section, we can break out of the loop since we're
879 guaranteed that there is only one such match.
882 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
884 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
885 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
886 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
887 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
889 if (tempo && insert_tempo) {
892 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
893 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
895 if (!tempo->movable()) {
897 /* can't (re)move this section, so overwrite
898 * its data content (but not its properties as
902 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
903 (*i)->set_position_lock_style (AudioTime);
905 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
906 t->set_type (insert_tempo->type());
916 } else if (meter && insert_meter) {
920 bool const ipm = insert_meter->position_lock_style() == MusicTime;
922 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
924 if (!meter->movable()) {
926 /* can't (re)move this section, so overwrite
927 * its data content (but not its properties as
931 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
932 (*i)->set_position_lock_style (AudioTime);
942 /* non-matching types, so we don't care */
946 /* Add the given MetricSection, if we didn't just reset an existing
951 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
952 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
955 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
956 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
959 bool const ipm = insert_meter->position_lock_style() == MusicTime;
960 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
965 } else if (insert_tempo) {
966 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
967 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
970 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
971 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
978 _metrics.insert (i, section);
979 //dump (_metrics, std::cout);
982 /* user supplies the exact pulse if pls == MusicTime */
984 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
986 TempoSection* ts = 0;
988 Glib::Threads::RWLock::WriterLock lm (lock);
989 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
993 PropertyChanged (PropertyChange ());
999 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
1001 const bool locked_to_meter = ts.locked_to_meter();
1004 Glib::Threads::RWLock::WriterLock lm (lock);
1005 TempoSection& first (first_tempo());
1006 if (ts.frame() != first.frame()) {
1007 remove_tempo_locked (ts);
1008 add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter);
1010 first.set_type (type);
1011 first.set_pulse (0.0);
1012 first.set_minute (minute_at_frame (frame));
1013 first.set_position_lock_style (AudioTime);
1015 /* cannot move the first tempo section */
1016 *static_cast<Tempo*>(&first) = tempo;
1017 recompute_map (_metrics);
1022 PropertyChanged (PropertyChange ());
1026 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1027 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1029 TempoSection* t = new TempoSection (pulse, minute, tempo.note_types_per_minute(), tempo.note_type(), type, pls, _frame_rate);
1030 t->set_locked_to_meter (locked_to_meter);
1031 bool solved = false;
1036 if (pls == AudioTime) {
1037 solved = solve_map_minute (_metrics, t, t->minute());
1039 solved = solve_map_pulse (_metrics, t, t->pulse());
1041 recompute_meters (_metrics);
1044 if (!solved && recompute) {
1045 recompute_map (_metrics);
1052 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, PositionLockStyle pls)
1054 MeterSection* m = 0;
1056 Glib::Threads::RWLock::WriterLock lm (lock);
1057 m = add_meter_locked (meter, beat, where, pls, true);
1062 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1063 dump (_metrics, std::cerr);
1067 PropertyChanged (PropertyChange ());
1072 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, PositionLockStyle pls)
1075 Glib::Threads::RWLock::WriterLock lm (lock);
1076 const double beat = beat_at_bbt_locked (_metrics, where);
1079 remove_meter_locked (ms);
1080 add_meter_locked (meter, beat, where, pls, true);
1082 MeterSection& first (first_meter());
1083 TempoSection& first_t (first_tempo());
1084 /* cannot move the first meter section */
1085 *static_cast<Meter*>(&first) = meter;
1086 first.set_position_lock_style (AudioTime);
1087 first.set_pulse (0.0);
1088 //first.set_minute (minute_at_frame (frame));
1089 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1090 first.set_beat (beat);
1091 first_t.set_minute (first.minute());
1092 first_t.set_pulse (0.0);
1093 first_t.set_position_lock_style (AudioTime);
1094 recompute_map (_metrics);
1098 PropertyChanged (PropertyChange ());
1102 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, PositionLockStyle pls, bool recompute)
1104 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1105 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1106 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1107 TempoSection* mlt = 0;
1109 if (pls == AudioTime) {
1110 /* add meter-locked tempo */
1111 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, time_minutes, TempoSection::Ramp, AudioTime, true, true);
1119 MeterSection* new_meter = new MeterSection (pulse, time_minutes, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1120 bool solved = false;
1122 do_insert (new_meter);
1126 if (pls == AudioTime) {
1127 solved = solve_map_minute (_metrics, new_meter, time_minutes);
1129 solved = solve_map_bbt (_metrics, new_meter, where);
1130 /* required due to resetting the pulse of meter-locked tempi above.
1131 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1132 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1134 recompute_map (_metrics);
1138 if (!solved && recompute) {
1139 /* if this has failed to solve, there is little we can do other than to ensure that
1140 the new map is recalculated.
1142 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1143 recompute_map (_metrics);
1150 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type)
1152 Tempo newtempo (note_types_per_minute, note_type);
1155 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1156 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1161 Glib::Threads::RWLock::WriterLock lm (lock);
1162 *((Tempo*) t) = newtempo;
1163 recompute_map (_metrics);
1165 PropertyChanged (PropertyChange ());
1172 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type)
1174 Tempo newtempo (note_types_per_minute, note_type);
1177 TempoSection* first;
1178 Metrics::iterator i;
1180 /* find the TempoSection immediately preceding "where"
1183 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1185 if ((*i)->frame() > where) {
1191 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1204 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1214 Glib::Threads::RWLock::WriterLock lm (lock);
1215 /* cannot move the first tempo section */
1216 *((Tempo*)prev) = newtempo;
1217 recompute_map (_metrics);
1220 PropertyChanged (PropertyChange ());
1224 TempoMap::first_meter () const
1226 const MeterSection *m = 0;
1228 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1229 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1234 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1235 abort(); /*NOTREACHED*/
1240 TempoMap::first_meter ()
1242 MeterSection *m = 0;
1244 /* CALLER MUST HOLD LOCK */
1246 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1247 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1252 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1253 abort(); /*NOTREACHED*/
1258 TempoMap::first_tempo () const
1260 const TempoSection *t = 0;
1262 /* CALLER MUST HOLD LOCK */
1264 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1265 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1269 if (!t->movable()) {
1275 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1276 abort(); /*NOTREACHED*/
1281 TempoMap::first_tempo ()
1283 TempoSection *t = 0;
1285 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1286 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1290 if (!t->movable()) {
1296 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1297 abort(); /*NOTREACHED*/
1301 TempoMap::recompute_tempi (Metrics& metrics)
1303 TempoSection* prev_t = 0;
1305 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1308 if ((*i)->is_tempo()) {
1309 t = static_cast<TempoSection*> (*i);
1313 if (!t->movable()) {
1321 if (t->position_lock_style() == AudioTime) {
1322 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
1323 if (!t->locked_to_meter()) {
1324 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
1328 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
1329 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
1337 prev_t->set_c_func (0.0);
1340 /* tempos must be positioned correctly.
1341 the current approach is to use a meter's bbt time as its base position unit.
1342 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1343 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1346 TempoMap::recompute_meters (Metrics& metrics)
1348 MeterSection* meter = 0;
1349 MeterSection* prev_m = 0;
1351 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1352 if (!(*mi)->is_tempo()) {
1353 meter = static_cast<MeterSection*> (*mi);
1354 if (meter->position_lock_style() == AudioTime) {
1356 pair<double, BBT_Time> b_bbt;
1357 TempoSection* meter_locked_tempo = 0;
1358 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1360 if ((*ii)->is_tempo()) {
1361 t = static_cast<TempoSection*> (*ii);
1362 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1363 meter_locked_tempo = t;
1370 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1371 if (beats + prev_m->beat() != meter->beat()) {
1372 /* reordering caused a bbt change */
1374 beats = meter->beat() - prev_m->beat();
1375 b_bbt = make_pair (beats + prev_m->beat()
1376 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1377 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1379 } else if (meter->movable()) {
1380 b_bbt = make_pair (meter->beat(), meter->bbt());
1381 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1384 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1386 if (meter_locked_tempo) {
1387 meter_locked_tempo->set_pulse (pulse);
1389 meter->set_beat (b_bbt);
1390 meter->set_pulse (pulse);
1395 pair<double, BBT_Time> b_bbt;
1397 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1398 if (beats + prev_m->beat() != meter->beat()) {
1399 /* reordering caused a bbt change */
1400 b_bbt = make_pair (beats + prev_m->beat()
1401 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1403 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1405 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1407 /* shouldn't happen - the first is audio-locked */
1408 pulse = pulse_at_beat_locked (metrics, meter->beat());
1409 b_bbt = make_pair (meter->beat(), meter->bbt());
1412 meter->set_beat (b_bbt);
1413 meter->set_pulse (pulse);
1414 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1423 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1425 /* CALLER MUST HOLD WRITE LOCK */
1427 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1430 /* silly call from Session::process() during startup
1435 recompute_tempi (metrics);
1436 recompute_meters (metrics);
1440 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1442 Glib::Threads::RWLock::ReaderLock lm (lock);
1443 TempoMetric m (first_meter(), first_tempo());
1445 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1446 at something, because we insert the default tempo and meter during
1447 TempoMap construction.
1449 now see if we can find better candidates.
1452 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1454 if ((*i)->frame() > frame) {
1468 /* XX meters only */
1470 TempoMap::metric_at (BBT_Time bbt) const
1472 Glib::Threads::RWLock::ReaderLock lm (lock);
1473 TempoMetric m (first_meter(), first_tempo());
1475 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1476 at something, because we insert the default tempo and meter during
1477 TempoMap construction.
1479 now see if we can find better candidates.
1482 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1484 if (!(*i)->is_tempo()) {
1485 mw = static_cast<MeterSection*> (*i);
1486 BBT_Time section_start (mw->bbt());
1488 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1499 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1500 * @param frame The session frame position.
1501 * @return The beat duration according to the tempo map at the supplied frame.
1503 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1504 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1506 * This function uses both tempo and meter.
1509 TempoMap::beat_at_frame (const framecnt_t& frame) const
1511 Glib::Threads::RWLock::ReaderLock lm (lock);
1513 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1516 /* This function uses both tempo and meter.*/
1518 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1520 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1521 MeterSection* prev_m = 0;
1522 MeterSection* next_m = 0;
1524 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1525 if (!(*i)->is_tempo()) {
1526 if (prev_m && (*i)->minute() > minute) {
1527 next_m = static_cast<MeterSection*> (*i);
1530 prev_m = static_cast<MeterSection*> (*i);
1534 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1536 /* audio locked meters fake their beat */
1537 if (next_m && next_m->beat() < beat) {
1538 return next_m->beat();
1544 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1545 * @param beat The BBT (meter-based) beat.
1546 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1548 * This function uses both tempo and meter.
1551 TempoMap::frame_at_beat (const double& beat) const
1553 Glib::Threads::RWLock::ReaderLock lm (lock);
1555 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1558 /* meter & tempo section based */
1560 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1562 MeterSection* prev_m = 0;
1563 TempoSection* prev_t = 0;
1567 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1568 if (!(*i)->is_tempo()) {
1569 m = static_cast<MeterSection*> (*i);
1570 if (prev_m && m->beat() > beat) {
1580 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1581 if ((*i)->is_tempo()) {
1582 t = static_cast<TempoSection*> (*i);
1583 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1592 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1595 /** Returns a Tempo corresponding to the supplied frame position.
1596 * @param frame The audio frame.
1597 * @return a Tempo according to the tempo map at the supplied frame.
1601 TempoMap::tempo_at_frame (const framepos_t& frame) const
1603 Glib::Threads::RWLock::ReaderLock lm (lock);
1605 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1609 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1611 TempoSection* prev_t = 0;
1615 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1616 if ((*i)->is_tempo()) {
1617 t = static_cast<TempoSection*> (*i);
1621 if ((prev_t) && t->minute() > minute) {
1622 /* t is the section past frame */
1623 return prev_t->tempo_at_minute (minute);
1629 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1632 /** returns the frame at which the supplied tempo occurs, or
1633 * the frame of the last tempo section (search exhausted)
1634 * only the position of the first occurence will be returned
1638 TempoMap::frame_at_tempo (const Tempo& tempo) const
1640 Glib::Threads::RWLock::ReaderLock lm (lock);
1642 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1646 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1648 TempoSection* prev_t = 0;
1649 const double tempo_bpm = tempo.note_types_per_minute();
1651 Metrics::const_iterator i;
1653 for (i = metrics.begin(); i != metrics.end(); ++i) {
1655 if ((*i)->is_tempo()) {
1656 t = static_cast<TempoSection*> (*i);
1662 const double t_bpm = t->note_types_per_minute();
1664 if (t_bpm == tempo_bpm) {
1669 const double prev_t_bpm = prev_t->note_types_per_minute();
1671 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1672 return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse());
1679 return prev_t->minute();
1683 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1685 TempoSection* prev_t = 0;
1689 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1690 if ((*i)->is_tempo()) {
1691 t = static_cast<TempoSection*> (*i);
1695 if ((prev_t) && t->pulse() > pulse) {
1696 /* t is the section past frame */
1697 return prev_t->tempo_at_pulse (pulse);
1703 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1707 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1709 TempoSection* prev_t = 0;
1710 const double tempo_bpm = tempo.note_types_per_minute();
1712 Metrics::const_iterator i;
1714 for (i = metrics.begin(); i != metrics.end(); ++i) {
1716 if ((*i)->is_tempo()) {
1717 t = static_cast<TempoSection*> (*i);
1723 const double t_bpm = t->note_types_per_minute();
1725 if (t_bpm == tempo_bpm) {
1730 const double prev_t_bpm = prev_t->note_types_per_minute();
1732 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1733 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1740 return prev_t->pulse();
1743 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1744 * @param qn the position in quarter note beats.
1745 * @return the Tempo at the supplied quarter-note.
1748 TempoMap::tempo_at_quarter_note (const double& qn) const
1750 Glib::Threads::RWLock::ReaderLock lm (lock);
1752 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1755 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1756 * @param tempo the tempo.
1757 * @return the position in quarter-note beats where the map bpm
1758 * is equal to that of the Tempo. currently ignores note_type.
1761 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1763 Glib::Threads::RWLock::ReaderLock lm (lock);
1765 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1768 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1769 * @param metrics the list of metric sections used to calculate the pulse.
1770 * @param beat The BBT (meter-based) beat.
1771 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1773 * a pulse or whole note is the base musical position of a MetricSection.
1774 * it is equivalent to four quarter notes.
1778 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1780 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1782 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1785 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1786 * @param metrics the list of metric sections used to calculate the beat.
1787 * @param pulse the whole-note pulse.
1788 * @return the meter-based beat at the supplied whole-note pulse.
1790 * a pulse or whole note is the base musical position of a MetricSection.
1791 * it is equivalent to four quarter notes.
1794 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1796 MeterSection* prev_m = 0;
1798 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1800 if (!(*i)->is_tempo()) {
1801 m = static_cast<MeterSection*> (*i);
1802 if (prev_m && m->pulse() > pulse) {
1810 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1814 /* tempo section based */
1816 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1818 /* HOLD (at least) THE READER LOCK */
1819 TempoSection* prev_t = 0;
1821 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1823 if ((*i)->is_tempo()) {
1824 t = static_cast<TempoSection*> (*i);
1828 if (prev_t && t->minute() > minute) {
1829 /*the previous ts is the one containing the frame */
1830 const double ret = prev_t->pulse_at_minute (minute);
1831 /* audio locked section in new meter*/
1832 if (t->pulse() < ret) {
1841 /* treated as constant for this ts */
1842 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1844 return pulses_in_section + prev_t->pulse();
1847 /* tempo section based */
1849 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1851 /* HOLD THE READER LOCK */
1853 const TempoSection* prev_t = 0;
1855 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1858 if ((*i)->is_tempo()) {
1859 t = static_cast<TempoSection*> (*i);
1863 if (prev_t && t->pulse() > pulse) {
1864 return prev_t->minute_at_pulse (pulse);
1870 /* must be treated as constant, irrespective of _type */
1871 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1873 return dtime + prev_t->minute();
1876 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1877 * @param bbt The BBT time (meter-based).
1878 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1882 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1884 Glib::Threads::RWLock::ReaderLock lm (lock);
1885 return beat_at_bbt_locked (_metrics, bbt);
1890 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1892 /* CALLER HOLDS READ LOCK */
1894 MeterSection* prev_m = 0;
1896 /* because audio-locked meters have 'fake' integral beats,
1897 there is no pulse offset here.
1901 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1902 if (!(*i)->is_tempo()) {
1903 m = static_cast<MeterSection*> (*i);
1905 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1906 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1914 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1915 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1916 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1921 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
1922 * @param beat The BBT (meter-based) beat.
1923 * @return The BBT time (meter-based) at the supplied meter-based beat.
1927 TempoMap::bbt_at_beat (const double& beat)
1929 Glib::Threads::RWLock::ReaderLock lm (lock);
1930 return bbt_at_beat_locked (_metrics, beat);
1934 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1936 /* CALLER HOLDS READ LOCK */
1937 MeterSection* prev_m = 0;
1938 const double beats = max (0.0, b);
1940 MeterSection* m = 0;
1942 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1943 if (!(*i)->is_tempo()) {
1944 m = static_cast<MeterSection*> (*i);
1946 if (m->beat() > beats) {
1947 /* this is the meter after the one our beat is on*/
1957 const double beats_in_ms = beats - prev_m->beat();
1958 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1959 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1960 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1961 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1965 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1966 ret.beats = (uint32_t) floor (remaining_beats);
1967 ret.bars = total_bars;
1969 /* 0 0 0 to 1 1 0 - based mapping*/
1973 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1975 ret.ticks -= BBT_Time::ticks_per_beat;
1978 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1986 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
1987 * @param bbt The BBT time (meter-based).
1988 * @return the quarter note beat at the supplied BBT time
1990 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
1992 * while the input uses meter, the output does not.
1995 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
1997 Glib::Threads::RWLock::ReaderLock lm (lock);
1999 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2003 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2005 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2008 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2011 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2015 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2017 /* CALLER HOLDS READ LOCK */
2019 MeterSection* prev_m = 0;
2021 /* because audio-locked meters have 'fake' integral beats,
2022 there is no pulse offset here.
2026 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2027 if (!(*i)->is_tempo()) {
2028 m = static_cast<MeterSection*> (*i);
2030 if (m->bbt().bars > bbt.bars) {
2038 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2039 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2040 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2045 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2046 * @param qn the quarter-note beat.
2047 * @return The BBT time (meter-based) at the supplied meter-based beat.
2049 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2053 TempoMap::bbt_at_quarter_note (const double& qn)
2055 Glib::Threads::RWLock::ReaderLock lm (lock);
2057 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2060 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2061 * @param metrics The list of metric sections used to determine the result.
2062 * @param pulse The whole-note pulse.
2063 * @return The BBT time at the supplied whole-note pulse.
2065 * a pulse or whole note is the basic musical position of a MetricSection.
2066 * it is equivalent to four quarter notes.
2067 * while the output uses meter, the input does not.
2070 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2072 MeterSection* prev_m = 0;
2074 MeterSection* m = 0;
2076 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2078 if (!(*i)->is_tempo()) {
2079 m = static_cast<MeterSection*> (*i);
2082 double const pulses_to_m = m->pulse() - prev_m->pulse();
2083 if (prev_m->pulse() + pulses_to_m > pulse) {
2084 /* this is the meter after the one our beat is on*/
2095 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2096 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2097 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2098 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2099 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2103 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2104 ret.beats = (uint32_t) floor (remaining_beats);
2105 ret.bars = total_bars;
2107 /* 0 0 0 to 1 1 0 mapping*/
2111 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2113 ret.ticks -= BBT_Time::ticks_per_beat;
2116 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2124 /** Returns the BBT time corresponding to the supplied frame position.
2125 * @param frame the position in audio samples.
2126 * @return the BBT time at the frame position .
2130 TempoMap::bbt_at_frame (framepos_t frame)
2137 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2140 Glib::Threads::RWLock::ReaderLock lm (lock);
2142 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2146 TempoMap::bbt_at_frame_rt (framepos_t frame)
2148 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2151 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2154 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2158 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2168 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2169 MeterSection* prev_m = 0;
2170 MeterSection* next_m = 0;
2174 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2175 if (!(*i)->is_tempo()) {
2176 m = static_cast<MeterSection*> (*i);
2177 if (prev_m && m->minute() > minute) {
2185 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2187 /* handle frame before first meter */
2188 if (minute < prev_m->minute()) {
2191 /* audio locked meters fake their beat */
2192 if (next_m && next_m->beat() < beat) {
2193 beat = next_m->beat();
2196 beat = max (0.0, beat);
2198 const double beats_in_ms = beat - prev_m->beat();
2199 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2200 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2201 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2202 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2206 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2207 ret.beats = (uint32_t) floor (remaining_beats);
2208 ret.bars = total_bars;
2210 /* 0 0 0 to 1 1 0 - based mapping*/
2214 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2216 ret.ticks -= BBT_Time::ticks_per_beat;
2219 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2227 /** Returns the frame position corresponding to the supplied BBT time.
2228 * @param bbt the position in BBT time.
2229 * @return the frame position at bbt.
2233 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2236 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2240 if (bbt.beats < 1) {
2241 throw std::logic_error ("beats are counted from one");
2243 Glib::Threads::RWLock::ReaderLock lm (lock);
2245 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
2248 /* meter & tempo section based */
2250 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2252 /* HOLD THE READER LOCK */
2254 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2259 * Returns the quarter-note beat position corresponding to the supplied frame.
2261 * @param frame the position in frames.
2262 * @return The quarter-note position of the supplied frame. Ignores meter.
2266 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2268 Glib::Threads::RWLock::ReaderLock lm (lock);
2270 const double ret = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
2276 TempoMap::quarter_note_at_minute_locked (const Metrics& metrics, const double minute) const
2278 const double ret = pulse_at_minute_locked (metrics, minute) * 4.0;
2284 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2286 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2289 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2292 const double ret = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
2298 * Returns the frame position corresponding to the supplied quarter-note beat.
2300 * @param quarter_note the quarter-note position.
2301 * @return the frame position of the supplied quarter-note. Ignores meter.
2306 TempoMap::frame_at_quarter_note (const double quarter_note) const
2308 Glib::Threads::RWLock::ReaderLock lm (lock);
2310 const framepos_t ret = frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note));
2316 TempoMap::minute_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2318 const double ret = minute_at_pulse_locked (metrics, quarter_note / 4.0);
2323 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2324 * @param beat The BBT (meter-based) beat.
2325 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2327 * a quarter-note may be compared with and assigned to Evoral::Beats.
2331 TempoMap::quarter_note_at_beat (const double beat) const
2333 Glib::Threads::RWLock::ReaderLock lm (lock);
2335 const double ret = quarter_note_at_beat_locked (_metrics, beat);
2341 TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) const
2343 const double ret = pulse_at_beat_locked (metrics, beat) * 4.0;
2348 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2349 * @param quarter_note The position in quarter-note beats.
2350 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2352 * a quarter-note is the musical unit of Evoral::Beats.
2356 TempoMap::beat_at_quarter_note (const double quarter_note) const
2358 Glib::Threads::RWLock::ReaderLock lm (lock);
2360 const double ret = beat_at_quarter_note_locked (_metrics, quarter_note);
2366 TempoMap::beat_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2369 return beat_at_pulse_locked (metrics, quarter_note / 4.0);
2372 /** Returns the duration in frames between two supplied quarter-note beat positions.
2373 * @param start the first position in quarter-note beats.
2374 * @param end the end position in quarter-note beats.
2375 * @return the frame distance ober the quarter-note beats duration.
2377 * use this rather than e.g.
2378 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2379 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2383 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2385 Glib::Threads::RWLock::ReaderLock lm (lock);
2387 return frame_at_minute (minutes_between_quarter_notes_locked (_metrics, start, end));
2391 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2394 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2398 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2400 Glib::Threads::RWLock::ReaderLock lm (lock);
2402 return quarter_notes_between_frames_locked (_metrics, start, end);
2406 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2408 const TempoSection* prev_t = 0;
2410 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2413 if ((*i)->is_tempo()) {
2414 t = static_cast<TempoSection*> (*i);
2418 if (prev_t && t->frame() > start) {
2425 const double start_qn = prev_t->pulse_at_frame (start);
2427 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2430 if ((*i)->is_tempo()) {
2431 t = static_cast<TempoSection*> (*i);
2435 if (prev_t && t->frame() > end) {
2441 const double end_qn = prev_t->pulse_at_frame (end);
2443 return (end_qn - start_qn) * 4.0;
2447 TempoMap::check_solved (const Metrics& metrics) const
2449 TempoSection* prev_t = 0;
2450 MeterSection* prev_m = 0;
2452 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2455 if ((*i)->is_tempo()) {
2456 t = static_cast<TempoSection*> (*i);
2461 /* check ordering */
2462 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2466 /* precision check ensures tempo and frames align.*/
2467 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))) {
2468 if (!t->locked_to_meter()) {
2473 /* gradient limit - who knows what it should be?
2474 things are also ok (if a little chaotic) without this
2476 if (fabs (prev_t->c_func()) > 1000.0) {
2477 //std::cout << "c : " << prev_t->c_func() << std::endl;
2484 if (!(*i)->is_tempo()) {
2485 m = static_cast<MeterSection*> (*i);
2486 if (prev_m && m->position_lock_style() == AudioTime) {
2487 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2488 const double nascent_m_minute = t->minute_at_pulse (m->pulse());
2489 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2491 if (t && (nascent_m_minute > m->minute() || nascent_m_minute < 0.0)) {
2505 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
2507 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2509 if ((*i)->is_tempo()) {
2510 t = static_cast<TempoSection*> (*i);
2511 if (!t->movable()) {
2512 t->set_active (true);
2515 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
2516 t->set_active (false);
2518 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
2519 t->set_active (true);
2520 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
2529 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2531 TempoSection* prev_t = 0;
2532 TempoSection* section_prev = 0;
2533 double first_m_minute = 0.0;
2535 /* can't move a tempo before the first meter */
2536 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2538 if (!(*i)->is_tempo()) {
2539 m = static_cast<MeterSection*> (*i);
2540 if (!m->movable()) {
2541 first_m_minute = m->minute();
2546 if (section->movable() && minute <= first_m_minute) {
2550 section->set_active (true);
2551 section->set_minute (minute);
2553 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2555 if ((*i)->is_tempo()) {
2556 t = static_cast<TempoSection*> (*i);
2563 section_prev = prev_t;
2564 if (t->locked_to_meter()) {
2569 if (t->position_lock_style() == MusicTime) {
2570 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2571 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2573 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2574 if (!t->locked_to_meter()) {
2575 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2584 section_prev->set_c_func (section_prev->compute_c_func_minute (section->note_types_per_minute(), minute));
2585 if (!section->locked_to_meter()) {
2586 section->set_pulse (section_prev->pulse_at_ntpm (section->note_types_per_minute(), minute));
2591 recompute_tempi (imaginary);
2593 if (check_solved (imaginary)) {
2596 dunp (imaginary, std::cout);
2600 MetricSectionFrameSorter fcmp;
2601 imaginary.sort (fcmp);
2603 recompute_tempi (imaginary);
2605 if (check_solved (imaginary)) {
2613 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2615 TempoSection* prev_t = 0;
2616 TempoSection* section_prev = 0;
2618 section->set_pulse (pulse);
2620 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2622 if ((*i)->is_tempo()) {
2623 t = static_cast<TempoSection*> (*i);
2627 if (!t->movable()) {
2634 section_prev = prev_t;
2637 if (t->position_lock_style() == MusicTime) {
2638 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2639 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2641 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2642 if (!t->locked_to_meter()) {
2643 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2652 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->note_types_per_minute(), pulse));
2653 section->set_minute (section_prev->minute_at_ntpm (section->note_types_per_minute(), pulse));
2657 recompute_tempi (imaginary);
2659 if (check_solved (imaginary)) {
2662 dunp (imaginary, std::cout);
2666 MetricSectionSorter cmp;
2667 imaginary.sort (cmp);
2669 recompute_tempi (imaginary);
2671 * XX need a restriction here, but only for this case,
2672 * as audio locked tempos don't interact in the same way.
2674 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2676 * |50 bpm |250 bpm |60 bpm
2677 * drag 250 to the pulse after 60->
2678 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2680 if (check_solved (imaginary)) {
2688 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2690 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2691 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2692 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->minute() >= minute)) {
2696 if (!section->movable()) {
2697 /* lock the first tempo to our first meter */
2698 if (!set_active_tempos (imaginary, section->frame_at_minute (minute))) {
2703 TempoSection* meter_locked_tempo = 0;
2705 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2707 if ((*ii)->is_tempo()) {
2708 t = static_cast<TempoSection*> (*ii);
2709 if ((t->locked_to_meter() || !t->movable()) && t->minute() == section->minute()) {
2710 meter_locked_tempo = t;
2716 if (!meter_locked_tempo) {
2720 MeterSection* prev_m = 0;
2722 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2723 bool solved = false;
2725 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2727 if (!(*i)->is_tempo()) {
2728 m = static_cast<MeterSection*> (*i);
2730 if (prev_m && section->movable()) {
2731 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2732 if (beats + prev_m->beat() < section->beat()) {
2733 /* set the section pulse according to its musical position,
2734 * as an earlier time than this has been requested.
2736 const double new_pulse = ((section->beat() - prev_m->beat())
2737 / prev_m->note_divisor()) + prev_m->pulse();
2739 tempo_copy->set_position_lock_style (MusicTime);
2740 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2741 meter_locked_tempo->set_position_lock_style (MusicTime);
2742 section->set_position_lock_style (MusicTime);
2743 section->set_pulse (new_pulse);
2744 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2745 meter_locked_tempo->set_position_lock_style (AudioTime);
2746 section->set_position_lock_style (AudioTime);
2747 section->set_minute (meter_locked_tempo->minute());
2753 Metrics::const_iterator d = future_map.begin();
2754 while (d != future_map.end()) {
2763 /* all is ok. set section's locked tempo if allowed.
2764 possibly disallowed if there is an adjacent audio-locked tempo.
2765 XX this check could possibly go. its never actually happened here.
2767 MeterSection* meter_copy = const_cast<MeterSection*>
2768 (&meter_section_at_minute_locked (future_map, section->minute()));
2770 meter_copy->set_minute (minute);
2772 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2773 section->set_minute (minute);
2774 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2775 / prev_m->note_divisor()) + prev_m->pulse());
2776 solve_map_minute (imaginary, meter_locked_tempo, minute);
2781 Metrics::const_iterator d = future_map.begin();
2782 while (d != future_map.end()) {
2792 /* not movable (first meter atm) */
2794 tempo_copy->set_minute (minute);
2795 tempo_copy->set_pulse (0.0);
2797 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2798 section->set_minute (minute);
2799 meter_locked_tempo->set_minute (minute);
2800 meter_locked_tempo->set_pulse (0.0);
2801 solve_map_minute (imaginary, meter_locked_tempo, minute);
2806 Metrics::const_iterator d = future_map.begin();
2807 while (d != future_map.end()) {
2816 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2817 section->set_beat (b_bbt);
2818 section->set_pulse (0.0);
2828 MetricSectionFrameSorter fcmp;
2829 imaginary.sort (fcmp);
2831 recompute_meters (imaginary);
2837 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2839 /* disallow setting section to an existing meter's bbt */
2840 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2842 if (!(*i)->is_tempo()) {
2843 m = static_cast<MeterSection*> (*i);
2844 if (m != section && m->bbt().bars == when.bars) {
2850 MeterSection* prev_m = 0;
2851 MeterSection* section_prev = 0;
2853 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2855 if (!(*i)->is_tempo()) {
2856 m = static_cast<MeterSection*> (*i);
2862 pair<double, BBT_Time> b_bbt;
2863 double new_pulse = 0.0;
2865 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2866 section_prev = prev_m;
2868 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2869 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2870 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2872 section->set_beat (b_bbt);
2873 section->set_pulse (pulse);
2874 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2878 if (m->position_lock_style() == AudioTime) {
2879 TempoSection* meter_locked_tempo = 0;
2881 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2883 if ((*ii)->is_tempo()) {
2884 t = static_cast<TempoSection*> (*ii);
2885 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2886 meter_locked_tempo = t;
2892 if (!meter_locked_tempo) {
2897 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). */
2902 /* the user has requested that the previous section of music overlaps this one.
2903 we have no choice but to change the bar number here, as being locked to audio means
2904 we must stay where we are on the timeline.
2906 beats = m->beat() - prev_m->beat();
2907 b_bbt = make_pair (beats + prev_m->beat()
2908 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2909 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2911 } else if (m->movable()) {
2912 b_bbt = make_pair (m->beat(), m->bbt());
2913 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2916 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2919 meter_locked_tempo->set_pulse (new_pulse);
2920 m->set_beat (b_bbt);
2921 m->set_pulse (new_pulse);
2925 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2926 if (beats + prev_m->beat() != m->beat()) {
2927 /* tempo/ meter change caused a change in beat (bar). */
2928 b_bbt = make_pair (beats + prev_m->beat()
2929 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2931 b_bbt = make_pair (beats + prev_m->beat()
2934 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2935 m->set_beat (b_bbt);
2936 m->set_pulse (new_pulse);
2937 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
2944 if (!section_prev) {
2946 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2947 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2948 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2950 section->set_beat (b_bbt);
2951 section->set_pulse (pulse);
2952 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2955 MetricSectionSorter cmp;
2956 imaginary.sort (cmp);
2958 recompute_meters (imaginary);
2963 /** places a copy of _metrics into copy and returns a pointer
2964 * to section's equivalent in copy.
2967 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2969 TempoSection* ret = 0;
2971 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2974 if ((*i)->is_tempo()) {
2975 t = static_cast<TempoSection*> (*i);
2977 ret = new TempoSection (*t);
2978 copy.push_back (ret);
2982 TempoSection* cp = new TempoSection (*t);
2983 copy.push_back (cp);
2985 if (!(*i)->is_tempo()) {
2986 m = static_cast<MeterSection *> (*i);
2987 MeterSection* cp = new MeterSection (*m);
2988 copy.push_back (cp);
2996 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2998 MeterSection* ret = 0;
3000 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3003 if ((*i)->is_tempo()) {
3004 t = static_cast<TempoSection*> (*i);
3005 TempoSection* cp = new TempoSection (*t);
3006 copy.push_back (cp);
3009 if (!(*i)->is_tempo()) {
3010 m = static_cast<MeterSection *> (*i);
3012 ret = new MeterSection (*m);
3013 copy.push_back (ret);
3016 MeterSection* cp = new MeterSection (*m);
3017 copy.push_back (cp);
3024 /** answers the question "is this a valid beat position for this tempo section?".
3025 * it returns true if the tempo section can be moved to the requested bbt position,
3026 * leaving the tempo map in a solved state.
3027 * @param ts the tempo section to be moved
3028 * @param bbt the requested new position for the tempo section
3029 * @return true if the tempo section can be moved to the position, otherwise false.
3032 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3035 TempoSection* tempo_copy = 0;
3038 Glib::Threads::RWLock::ReaderLock lm (lock);
3039 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3045 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3047 Metrics::const_iterator d = copy.begin();
3048 while (d != copy.end()) {
3057 * 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,
3058 * taking any possible reordering as a consequence of this into account.
3059 * @param section - the section to be altered
3060 * @param bbt - the BBT time where the altered tempo will fall
3061 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3063 pair<double, framepos_t>
3064 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3067 pair<double, framepos_t> ret = make_pair (0.0, 0);
3069 Glib::Threads::RWLock::ReaderLock lm (lock);
3071 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3073 const double beat = beat_at_bbt_locked (future_map, bbt);
3075 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3076 ret.first = tempo_copy->pulse();
3077 ret.second = tempo_copy->frame();
3079 ret.first = section->pulse();
3080 ret.second = section->frame();
3083 Metrics::const_iterator d = future_map.begin();
3084 while (d != future_map.end()) {
3091 /** moves a TempoSection to a specified position.
3092 * @param ts - the section to be moved
3093 * @param frame - the new position in frames for the tempo
3094 * @param sub_num - the snap division to use if using musical time.
3096 * if sub_num is non-zero, the frame position is used to calculate an exact
3099 * -1 | snap to bars (meter-based)
3100 * 0 | no snap - use audio frame for musical position
3101 * 1 | snap to meter-based (BBT) beat
3102 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3104 * this follows the snap convention in the gui.
3105 * if sub_num is zero, the musical position will be taken from the supplied frame.
3108 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3112 if (ts->position_lock_style() == MusicTime) {
3114 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3115 Glib::Threads::RWLock::WriterLock lm (lock);
3116 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3118 tempo_copy->set_position_lock_style (AudioTime);
3120 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
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 solve_map_pulse (_metrics, ts, pulse);
3126 recompute_meters (_metrics);
3134 Glib::Threads::RWLock::WriterLock lm (lock);
3135 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3137 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3139 /* We're moving the object that defines the grid while snapping to it...
3140 * Placing the ts at the beat corresponding to the requested frame may shift the
3141 * grid in such a way that the mouse is left hovering over a completerly different division,
3142 * causing jittering when the mouse next moves (esp. large tempo deltas).
3143 * To avoid this, place the ts at the requested frame in a dummy map
3144 * then find the closest beat subdivision to that frame in the dummy.
3145 * This alters the snap behaviour slightly in that we snap to beat divisions
3146 * in the future map rather than the existing one.
3148 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3149 const double pulse = pulse_at_beat_locked (future_map, beat);
3151 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3152 /* snapping to a grid. force MusicTime temporarily. */
3153 ts->set_position_lock_style (MusicTime);
3154 solve_map_pulse (_metrics, ts, pulse);
3155 ts->set_position_lock_style (AudioTime);
3157 recompute_meters (_metrics);
3160 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3161 recompute_meters (_metrics);
3167 Metrics::const_iterator d = future_map.begin();
3168 while (d != future_map.end()) {
3173 MetricPositionChanged (); // Emit Signal
3176 /** moves a MeterSection to a specified position.
3177 * @param ms - the section to be moved
3178 * @param frame - the new position in frames for the meter
3180 * as a meter cannot snap to anything but bars,
3181 * the supplied frame is rounded to the nearest bar, possibly
3182 * leaving the meter position unchanged.
3185 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
3189 if (ms->position_lock_style() == AudioTime) {
3192 Glib::Threads::RWLock::WriterLock lm (lock);
3193 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3195 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3196 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3197 recompute_tempi (_metrics);
3202 Glib::Threads::RWLock::WriterLock lm (lock);
3203 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3205 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3206 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3208 if (solve_map_bbt (future_map, copy, bbt)) {
3209 solve_map_bbt (_metrics, ms, bbt);
3210 recompute_tempi (_metrics);
3215 Metrics::const_iterator d = future_map.begin();
3216 while (d != future_map.end()) {
3221 MetricPositionChanged (); // Emit Signal
3225 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3228 bool can_solve = false;
3230 Glib::Threads::RWLock::WriterLock lm (lock);
3231 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3232 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3233 recompute_tempi (future_map);
3235 if (check_solved (future_map)) {
3236 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3237 recompute_map (_metrics);
3242 Metrics::const_iterator d = future_map.begin();
3243 while (d != future_map.end()) {
3248 MetricPositionChanged (); // Emit Signal
3254 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
3257 Ts (future prev_t) Tnext
3260 |----------|----------
3267 Glib::Threads::RWLock::WriterLock lm (lock);
3273 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3274 TempoSection* prev_to_prev_t = 0;
3275 const frameoffset_t fr_off = end_frame - frame;
3279 if (prev_t->pulse() > 0.0) {
3280 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
3283 TempoSection* next_t = 0;
3284 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
3285 TempoSection* t = 0;
3286 if ((*i)->is_tempo()) {
3287 t = static_cast<TempoSection*> (*i);
3288 if (t->frame() > ts->frame()) {
3294 /* minimum allowed measurement distance in frames */
3295 const framepos_t min_dframe = 2;
3297 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3298 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3300 double contribution = 0.0;
3302 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3303 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3306 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3308 const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame));
3309 const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame));
3313 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
3315 if (prev_t->position_lock_style() == MusicTime) {
3316 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3317 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3319 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3320 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3322 new_bpm = prev_t->note_types_per_minute();
3325 /* prev to prev is irrelevant */
3327 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3328 new_bpm = prev_t->note_types_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3330 new_bpm = prev_t->note_types_per_minute();
3335 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3336 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3338 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3339 / (double) ((end_frame) - prev_to_prev_t->frame()));
3341 new_bpm = prev_t->note_types_per_minute();
3344 /* prev_to_prev_t is irrelevant */
3346 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3347 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3349 new_bpm = prev_t->note_types_per_minute();
3355 double frame_ratio = 1.0;
3356 double pulse_ratio = 1.0;
3357 const double pulse_pos = frame;
3359 if (prev_to_prev_t) {
3360 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3361 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3363 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3364 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3367 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3368 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3370 pulse_ratio = (start_pulse / end_pulse);
3372 new_bpm = prev_t->note_types_per_minute() * (pulse_ratio * frame_ratio);
3375 /* don't clamp and proceed here.
3376 testing has revealed that this can go negative,
3377 which is an entirely different thing to just being too low.
3379 if (new_bpm < 0.5) {
3382 new_bpm = min (new_bpm, (double) 1000.0);
3383 prev_t->set_note_types_per_minute (new_bpm);
3384 recompute_tempi (future_map);
3385 recompute_meters (future_map);
3387 if (check_solved (future_map)) {
3388 ts->set_note_types_per_minute (new_bpm);
3389 recompute_tempi (_metrics);
3390 recompute_meters (_metrics);
3394 Metrics::const_iterator d = future_map.begin();
3395 while (d != future_map.end()) {
3400 MetricPositionChanged (); // Emit Signal
3403 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3404 * the supplied frame, possibly returning a negative value.
3406 * @param frame The session frame position.
3407 * @param sub_num The subdivision to use when rounding the beat.
3408 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3409 * Positive integers indicate quarter note (non BBT) divisions.
3410 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3411 * @return The beat position of the supplied frame.
3413 * when working to a musical grid, the use of sub_nom indicates that
3414 * the position should be interpreted musically.
3416 * it effectively snaps to meter bars, meter beats or quarter note divisions
3417 * (as per current gui convention) and returns a musical position independent of frame rate.
3419 * If the supplied frame lies before the first meter, the return will be negative,
3420 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3421 * the continuation of the tempo curve (backwards).
3423 * This function is sensitive to tempo and meter.
3426 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3428 Glib::Threads::RWLock::ReaderLock lm (lock);
3430 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3434 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3436 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3439 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3440 * the supplied frame, possibly returning a negative value.
3442 * @param frame The session frame position.
3443 * @param sub_num The subdivision to use when rounding the quarter note.
3444 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3445 * Positive integers indicate quarter note (non BBT) divisions.
3446 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3447 * @return The quarter note position of the supplied frame.
3449 * When working to a musical grid, the use of sub_nom indicates that
3450 * the frame position should be interpreted musically.
3452 * it effectively snaps to meter bars, meter beats or quarter note divisions
3453 * (as per current gui convention) and returns a musical position independent of frame rate.
3455 * If the supplied frame lies before the first meter, the return will be negative,
3456 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3457 * the continuation of the tempo curve (backwards).
3459 * This function is tempo-sensitive.
3462 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3464 Glib::Threads::RWLock::ReaderLock lm (lock);
3466 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3470 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3472 double qn = quarter_note_at_minute_locked (metrics, minute_at_frame (frame));
3475 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3476 } else if (sub_num == 1) {
3477 /* the gui requested exact musical (BBT) beat */
3478 qn = quarter_note_at_beat_locked (metrics, floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5));
3479 } else if (sub_num == -1) {
3481 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3485 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3487 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3489 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3499 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3500 * @param pos the frame position in the tempo map.
3501 * @param bbt the distance in BBT time from pos to calculate.
3502 * @param dir the rounding direction..
3503 * @return the duration in frames between pos and bbt
3506 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3508 Glib::Threads::RWLock::ReaderLock lm (lock);
3510 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3512 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3515 pos_bbt.bars += bbt.bars;
3517 pos_bbt.ticks += bbt.ticks;
3518 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3520 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3523 pos_bbt.beats += bbt.beats;
3524 if ((double) pos_bbt.beats > divisions) {
3526 pos_bbt.beats -= divisions;
3528 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3530 return pos_bbt_frame - pos;
3534 if (pos_bbt.bars <= bbt.bars) {
3537 pos_bbt.bars -= bbt.bars;
3540 if (pos_bbt.ticks < bbt.ticks) {
3541 if (pos_bbt.bars > 1) {
3542 if (pos_bbt.beats == 1) {
3544 pos_bbt.beats = divisions;
3548 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3554 pos_bbt.ticks -= bbt.ticks;
3557 if (pos_bbt.beats <= bbt.beats) {
3558 if (pos_bbt.bars > 1) {
3560 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3565 pos_bbt.beats -= bbt.beats;
3568 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3575 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3577 return round_to_type (fr, dir, Bar);
3581 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3583 return round_to_type (fr, dir, Beat);
3587 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3589 Glib::Threads::RWLock::ReaderLock lm (lock);
3590 uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3591 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3592 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3594 ticks -= beats * BBT_Time::ticks_per_beat;
3597 /* round to next (or same iff dir == RoundUpMaybe) */
3599 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3601 if (mod == 0 && dir == RoundUpMaybe) {
3602 /* right on the subdivision, which is fine, so do nothing */
3604 } else if (mod == 0) {
3605 /* right on the subdivision, so the difference is just the subdivision ticks */
3606 ticks += ticks_one_subdivisions_worth;
3609 /* not on subdivision, compute distance to next subdivision */
3611 ticks += ticks_one_subdivisions_worth - mod;
3614 if (ticks >= BBT_Time::ticks_per_beat) {
3615 ticks -= BBT_Time::ticks_per_beat;
3617 } else if (dir < 0) {
3619 /* round to previous (or same iff dir == RoundDownMaybe) */
3621 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3623 if (difference == 0 && dir == RoundDownAlways) {
3624 /* right on the subdivision, but force-rounding down,
3625 so the difference is just the subdivision ticks */
3626 difference = ticks_one_subdivisions_worth;
3629 if (ticks < difference) {
3630 ticks = BBT_Time::ticks_per_beat - ticks;
3632 ticks -= difference;
3636 /* round to nearest */
3639 /* compute the distance to the previous and next subdivision */
3641 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3643 /* closer to the next subdivision, so shift forward */
3645 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3647 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3649 if (ticks > BBT_Time::ticks_per_beat) {
3651 ticks -= BBT_Time::ticks_per_beat;
3652 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3655 } else if (rem > 0) {
3657 /* closer to previous subdivision, so shift backward */
3661 /* can't go backwards past zero, so ... */
3664 /* step back to previous beat */
3666 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3667 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3669 ticks = lrint (ticks - rem);
3670 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3673 /* on the subdivision, do nothing */
3677 const framepos_t ret_frame = frame_at_minute (minute_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3683 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3685 Glib::Threads::RWLock::ReaderLock lm (lock);
3686 uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3687 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3688 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3690 ticks -= beats * BBT_Time::ticks_per_beat;
3693 /* round to next (or same iff dir == RoundUpMaybe) */
3695 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3697 if (mod == 0 && dir == RoundUpMaybe) {
3698 /* right on the subdivision, which is fine, so do nothing */
3700 } else if (mod == 0) {
3701 /* right on the subdivision, so the difference is just the subdivision ticks */
3702 ticks += ticks_one_subdivisions_worth;
3705 /* not on subdivision, compute distance to next subdivision */
3707 ticks += ticks_one_subdivisions_worth - mod;
3710 if (ticks >= BBT_Time::ticks_per_beat) {
3711 ticks -= BBT_Time::ticks_per_beat;
3713 } else if (dir < 0) {
3715 /* round to previous (or same iff dir == RoundDownMaybe) */
3717 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3719 if (difference == 0 && dir == RoundDownAlways) {
3720 /* right on the subdivision, but force-rounding down,
3721 so the difference is just the subdivision ticks */
3722 difference = ticks_one_subdivisions_worth;
3725 if (ticks < difference) {
3726 ticks = BBT_Time::ticks_per_beat - ticks;
3728 ticks -= difference;
3732 /* round to nearest */
3735 /* compute the distance to the previous and next subdivision */
3737 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3739 /* closer to the next subdivision, so shift forward */
3741 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3743 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3745 if (ticks > BBT_Time::ticks_per_beat) {
3747 ticks -= BBT_Time::ticks_per_beat;
3748 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3751 } else if (rem > 0) {
3753 /* closer to previous subdivision, so shift backward */
3757 /* can't go backwards past zero, so ... */
3760 /* step back to previous beat */
3762 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3763 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3765 ticks = lrint (ticks - rem);
3766 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3769 /* on the subdivision, do nothing */
3773 const framepos_t ret_frame = frame_at_minute (minute_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3779 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3781 Glib::Threads::RWLock::ReaderLock lm (lock);
3783 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (frame)));
3784 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3789 /* find bar previous to 'frame' */
3792 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3794 } else if (dir > 0) {
3795 /* find bar following 'frame' */
3799 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3801 /* true rounding: find nearest bar */
3802 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3805 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3807 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3809 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3820 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
3821 } else if (dir > 0) {
3822 return frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
3824 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
3833 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3834 framepos_t lower, framepos_t upper, uint32_t bar_mod)
3836 Glib::Threads::RWLock::ReaderLock lm (lock);
3837 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
3839 /* although the map handles negative beats, bbt doesn't. */
3844 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
3848 while (pos >= 0 && pos < upper) {
3849 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
3850 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3851 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3852 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3853 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3857 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
3862 bbt.bars -= bbt.bars % bar_mod;
3866 while (pos >= 0 && pos < upper) {
3867 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3868 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3869 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3870 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3871 bbt.bars += bar_mod;
3877 TempoMap::tempo_section_at_frame (framepos_t frame) const
3879 Glib::Threads::RWLock::ReaderLock lm (lock);
3881 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
3885 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
3887 TempoSection* prev = 0;
3891 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3893 if ((*i)->is_tempo()) {
3894 t = static_cast<TempoSection*> (*i);
3898 if (prev && t->minute() > minute) {
3908 abort(); /*NOTREACHED*/
3915 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3917 TempoSection* prev_t = 0;
3918 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3922 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3923 if ((*i)->is_tempo()) {
3924 t = static_cast<TempoSection*> (*i);
3925 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3935 /* don't use this to calculate length (the tempo is only correct for this frame).
3936 do that stuff based on the beat_at_frame and frame_at_beat api
3939 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
3941 Glib::Threads::RWLock::ReaderLock lm (lock);
3943 const TempoSection* ts_at = 0;
3944 const TempoSection* ts_after = 0;
3945 Metrics::const_iterator i;
3948 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3950 if ((*i)->is_tempo()) {
3951 t = static_cast<TempoSection*> (*i);
3955 if (ts_at && (*i)->frame() > frame) {
3965 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
3967 /* must be treated as constant tempo */
3968 return ts_at->frames_per_quarter_note (_frame_rate);
3972 TempoMap::meter_section_at_frame (framepos_t frame) const
3974 Glib::Threads::RWLock::ReaderLock lm (lock);
3975 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
3979 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
3981 Metrics::const_iterator i;
3982 MeterSection* prev = 0;
3986 for (i = metrics.begin(); i != metrics.end(); ++i) {
3988 if (!(*i)->is_tempo()) {
3989 m = static_cast<MeterSection*> (*i);
3991 if (prev && (*i)->minute() > minute) {
4001 abort(); /*NOTREACHED*/
4008 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4010 MeterSection* prev_m = 0;
4012 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4014 if (!(*i)->is_tempo()) {
4015 m = static_cast<MeterSection*> (*i);
4016 if (prev_m && m->beat() > beat) {
4027 TempoMap::meter_section_at_beat (double beat) const
4029 Glib::Threads::RWLock::ReaderLock lm (lock);
4030 return meter_section_at_beat_locked (_metrics, beat);
4034 TempoMap::meter_at_frame (framepos_t frame) const
4036 TempoMetric m (metric_at (frame));
4041 TempoMap::fix_legacy_session ()
4043 MeterSection* prev_m = 0;
4044 TempoSection* prev_t = 0;
4046 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4050 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4051 if (!m->movable()) {
4052 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4055 m->set_minute (0.0);
4056 m->set_position_lock_style (AudioTime);
4061 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4062 + (m->bbt().beats - 1)
4063 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4065 m->set_beat (start);
4066 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4067 + (m->bbt().beats - 1)
4068 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4069 m->set_pulse (start_beat / prev_m->note_divisor());
4072 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4078 if (!t->movable()) {
4080 t->set_minute (0.0);
4081 t->set_position_lock_style (AudioTime);
4087 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4088 + (t->legacy_bbt().beats - 1)
4089 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4091 t->set_pulse (beat / prev_m->note_divisor());
4093 /* really shouldn't happen but.. */
4094 t->set_pulse (beat / 4.0);
4103 TempoMap::get_state ()
4105 Metrics::const_iterator i;
4106 XMLNode *root = new XMLNode ("TempoMap");
4109 Glib::Threads::RWLock::ReaderLock lm (lock);
4110 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4111 root->add_child_nocopy ((*i)->get_state());
4119 TempoMap::set_state (const XMLNode& node, int /*version*/)
4122 Glib::Threads::RWLock::WriterLock lm (lock);
4125 XMLNodeConstIterator niter;
4126 Metrics old_metrics (_metrics);
4129 nlist = node.children();
4131 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4132 XMLNode* child = *niter;
4134 if (child->name() == TempoSection::xml_state_node_name) {
4137 TempoSection* ts = new TempoSection (*child, _frame_rate);
4138 _metrics.push_back (ts);
4141 catch (failed_constructor& err){
4142 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4143 _metrics = old_metrics;
4144 old_metrics.clear();
4148 } else if (child->name() == MeterSection::xml_state_node_name) {
4151 MeterSection* ms = new MeterSection (*child, _frame_rate);
4152 _metrics.push_back (ms);
4155 catch (failed_constructor& err) {
4156 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4157 _metrics = old_metrics;
4158 old_metrics.clear();
4164 if (niter == nlist.end()) {
4165 MetricSectionSorter cmp;
4166 _metrics.sort (cmp);
4169 /* check for legacy sessions where bbt was the base musical unit for tempo */
4170 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4172 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4173 if (t->legacy_bbt().bars != 0) {
4174 fix_legacy_session();
4181 /* check for multiple tempo/meters at the same location, which
4182 ardour2 somehow allowed.
4185 Metrics::iterator prev = _metrics.end();
4186 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4187 if (prev != _metrics.end()) {
4189 MeterSection* prev_m;
4191 TempoSection* prev_t;
4192 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4193 if (prev_m->pulse() == ms->pulse()) {
4194 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4195 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4198 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4199 if (prev_t->pulse() == ts->pulse()) {
4200 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4201 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4209 recompute_map (_metrics);
4211 Metrics::const_iterator d = old_metrics.begin();
4212 while (d != old_metrics.end()) {
4216 old_metrics.clear ();
4219 PropertyChanged (PropertyChange ());
4225 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
4227 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4228 const MeterSection* m;
4229 const TempoSection* t;
4230 const TempoSection* prev_t = 0;
4232 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4234 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4235 o << "Tempo @ " << *i << t->note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4236 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4237 << " minute= " << t->minute() << " frame= " << t->frame() << " (movable? " << t->movable() << ')'
4238 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4240 o << std::setprecision (17) << " current : " << t->note_types_per_minute()
4241 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4242 o << " previous : " << prev_t->note_types_per_minute()
4243 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4244 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4245 << " | " << prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())
4246 << " | " << frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))
4247 << " | " << prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()) << std::endl;
4250 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4251 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4252 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4253 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
4256 o << "------" << std::endl;
4260 TempoMap::n_tempos() const
4262 Glib::Threads::RWLock::ReaderLock lm (lock);
4265 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4266 if ((*i)->is_tempo()) {
4275 TempoMap::n_meters() const
4277 Glib::Threads::RWLock::ReaderLock lm (lock);
4280 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4281 if (!(*i)->is_tempo()) {
4290 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4292 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4293 if ((*i)->frame() >= where && (*i)->movable ()) {
4297 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4298 gui_move_meter (ms, (*i)->frame() + amount);
4301 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4302 gui_move_tempo (ts, (*i)->frame() + amount, 0);
4307 PropertyChanged (PropertyChange ());
4311 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4315 std::list<MetricSection*> metric_kill_list;
4317 TempoSection* last_tempo = NULL;
4318 MeterSection* last_meter = NULL;
4319 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4320 bool meter_after = false; // is there a meter marker likewise?
4322 Glib::Threads::RWLock::WriterLock lm (lock);
4323 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4324 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4325 metric_kill_list.push_back(*i);
4326 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4329 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4333 else if ((*i)->frame() >= where) {
4334 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4335 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4336 if ((*i)->frame() == where) {
4337 // marker was immediately after end of range
4338 tempo_after = dynamic_cast<TempoSection*> (*i);
4339 meter_after = dynamic_cast<MeterSection*> (*i);
4345 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4346 if (last_tempo && !tempo_after) {
4347 metric_kill_list.remove(last_tempo);
4348 last_tempo->set_minute (minute_at_frame (where));
4351 if (last_meter && !meter_after) {
4352 metric_kill_list.remove(last_meter);
4353 last_meter->set_minute (minute_at_frame (where));
4357 //remove all the remaining metrics
4358 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4359 _metrics.remove(*i);
4364 recompute_map (_metrics);
4367 PropertyChanged (PropertyChange ());
4371 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4372 * pos can be -ve, if required.
4375 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4377 Glib::Threads::RWLock::ReaderLock lm (lock);
4378 const double frame_qn = quarter_notes_between_frames_locked (_metrics, 0, frame);
4380 return frame_at_minute (minute_at_quarter_note_locked (_metrics, frame_qn + beats.to_double()));
4384 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4386 Glib::Threads::RWLock::ReaderLock lm (lock);
4388 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4389 pos_bbt.ticks += op.ticks;
4390 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4392 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4394 pos_bbt.beats += op.beats;
4395 /* the meter in effect will start on the bar */
4396 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();
4397 while (pos_bbt.beats >= divisions_per_bar + 1) {
4399 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4400 pos_bbt.beats -= divisions_per_bar;
4402 pos_bbt.bars += op.bars;
4404 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4407 /** Count the number of beats that are equivalent to distance when going forward,
4411 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4413 Glib::Threads::RWLock::ReaderLock lm (lock);
4415 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4419 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4425 operator<< (std::ostream& o, const Meter& m) {
4426 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4430 operator<< (std::ostream& o, const Tempo& t) {
4431 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4435 operator<< (std::ostream& o, const MetricSection& section) {
4437 o << "MetricSection @ " << section.frame() << ' ';
4439 const TempoSection* ts;
4440 const MeterSection* ms;
4442 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4443 o << *((const Tempo*) ts);
4444 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4445 o << *((const Meter*) ms);