2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0, 4.0);
51 MetricSection::frame_at_minute (const double& time) const
53 return (framepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
57 MetricSection::minute_at_frame (const framepos_t& frame) const
59 return (frame / (double) _sample_rate) / 60.0;
62 /***********************************************************************/
65 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
67 /* This is tempo- and meter-sensitive. The number it returns
68 is based on the interval between any two lines in the
69 grid that is constructed from tempo and meter sections.
71 The return value IS NOT interpretable in terms of "beats".
74 return (60.0 * sr) / (tempo.note_types_per_minute() * (_note_type/tempo.note_type()));
78 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
80 return frames_per_grid (tempo, sr) * _divisions_per_bar;
83 /***********************************************************************/
85 const string TempoSection::xml_state_node_name = "Tempo";
87 TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate)
88 : MetricSection (0.0, 0, MusicTime, true, sample_rate)
89 , Tempo (TempoMap::default_tempo())
92 , _locked_to_meter (false)
94 XMLProperty const * prop;
100 _legacy_bbt = BBT_Time (0, 0, 0);
102 if ((prop = node.property ("start")) != 0) {
103 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
107 /* legacy session - start used to be in bbt*/
110 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
114 if ((prop = node.property ("pulse")) != 0) {
115 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
116 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
122 if ((prop = node.property ("frame")) != 0) {
123 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
124 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
125 throw failed_constructor();
127 set_minute (minute_at_frame (frame));
131 /* XX replace old beats-per-minute name with note-types-per-minute */
132 if ((prop = node.property ("beats-per-minute")) != 0) {
133 if (sscanf (prop->value().c_str(), "%lf", &_note_types_per_minute) != 1 || _note_types_per_minute < 0.0) {
134 error << _("TempoSection XML node has an illegal \"beats-per-minute\" value") << endmsg;
135 throw failed_constructor();
139 if ((prop = node.property ("note-type")) == 0) {
140 /* older session, make note type be quarter by default */
143 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
144 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
145 throw failed_constructor();
149 if ((prop = node.property ("movable")) == 0) {
150 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
151 throw failed_constructor();
154 set_initial (!string_is_affirmative (prop->value()));
156 if ((prop = node.property ("active")) == 0) {
157 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
160 set_active (string_is_affirmative (prop->value()));
163 if ((prop = node.property ("tempo-type")) == 0) {
166 _type = Type (string_2_enum (prop->value(), _type));
169 if ((prop = node.property ("lock-style")) == 0) {
171 set_position_lock_style (MusicTime);
173 set_position_lock_style (AudioTime);
176 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
179 if ((prop = node.property ("locked-to-meter")) == 0) {
180 set_locked_to_meter (false);
182 set_locked_to_meter (string_is_affirmative (prop->value()));
187 TempoSection::get_state() const
189 XMLNode *root = new XMLNode (xml_state_node_name);
193 snprintf (buf, sizeof (buf), "%lf", pulse());
194 root->add_property ("pulse", buf);
195 snprintf (buf, sizeof (buf), "%li", frame());
196 root->add_property ("frame", buf);
197 snprintf (buf, sizeof (buf), "%lf", _note_types_per_minute);
198 root->add_property ("beats-per-minute", buf);
199 snprintf (buf, sizeof (buf), "%lf", _note_type);
200 root->add_property ("note-type", buf);
201 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
202 root->add_property ("movable", buf);
203 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
204 root->add_property ("active", buf);
205 root->add_property ("tempo-type", enum_2_string (_type));
206 root->add_property ("lock-style", enum_2_string (position_lock_style()));
207 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
213 TempoSection::set_type (Type type)
218 /** returns the Tempo at the session-relative minute.
221 TempoSection::tempo_at_minute (const double& m) const
223 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && m < minute());
225 return Tempo (note_types_per_minute(), note_type());
228 return Tempo (_tempo_at_time (m - minute()), _note_type);
231 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
232 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
233 * @param p the pulse used to calculate the returned minute for constant tempi
234 * @return the minute at the supplied tempo
236 * note that the note_type is currently ignored in this function. see below.
240 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
241 * there should be no ramp between the two even if we are ramped.
242 * in other words a ramp should only place a curve on note_types_per_minute.
243 * we should be able to use Tempo note type here, but the above
244 * complicates things a bit.
247 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
249 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && p < pulse());
251 return ((p - pulse()) / pulses_per_minute()) + minute();
254 return _time_at_tempo (ntpm) + minute();
257 /** returns the Tempo at the supplied whole-note pulse.
260 TempoSection::tempo_at_pulse (const double& p) const
262 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && p < pulse());
265 return Tempo (note_types_per_minute(), note_type());
268 return Tempo (_tempo_at_pulse (p - pulse()), _note_type);
271 /** returns the whole-note pulse where a tempo in note types per minute occurs.
272 * constant tempi require minute m.
273 * @param ntpm the note types per minute value used to calculate the returned pulse
274 * @param m the minute used to calculate the returned pulse if the tempo is constant
275 * @return the whole-note pulse at the supplied tempo
277 * note that note_type is currently ignored in this function. see minute_at_tempo().
279 * for constant tempi, this is anaologous to pulse_at_minute().
282 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
284 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && m < minute());
286 return ((m - minute()) * pulses_per_minute()) + pulse();
289 return _pulse_at_tempo (ntpm) + pulse();
292 /** returns the whole-note pulse at the supplied session-relative minute.
295 TempoSection::pulse_at_minute (const double& m) const
297 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && m < minute());
299 return ((m - minute()) * pulses_per_minute()) + pulse();
302 return _pulse_at_time (m - minute()) + pulse();
305 /** returns the session-relative minute at the supplied whole-note pulse.
308 TempoSection::minute_at_pulse (const double& p) const
310 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && p < pulse());
312 return ((p - pulse()) / pulses_per_minute()) + minute();
315 return _time_at_pulse (p - pulse()) + minute();
318 /** returns thw whole-note pulse at session frame position f.
319 * @param f the frame position.
320 * @return the position in whole-note pulses corresponding to f
322 * for use with musical units whose granularity is coarser than frames (e.g. ticks)
325 TempoSection::pulse_at_frame (const framepos_t& f) const
327 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && f < frame());
329 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
332 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
336 TempoSection::frame_at_pulse (const double& p) const
338 const bool constant = _type == Constant || _c_func == 0.0 || (initial() && p < pulse());
340 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
343 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
351 Tt----|-----------------*|
352 Ta----|--------------|* |
358 _______________|___|____
359 time a t (next tempo)
362 Duration in beats at time a is the integral of some Tempo function.
363 In our case, the Tempo function (Tempo at time t) is
366 with function constant
371 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
372 b(t) = T0(e^(ct) - 1) / c
374 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
375 t(b) = log((c.b / T0) + 1) / c
377 The time t at which Tempo T occurs is a as above:
378 t(T) = log(T / T0) / c
380 The beat at which a Tempo T occurs is:
383 The Tempo at which beat b occurs is:
386 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
387 Our problem is that we usually don't know t.
388 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
389 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
390 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
392 By substituting our expanded t as a in the c function above, our problem is reduced to:
393 c = T0 (e^(log (Ta / T0)) - 1) / b
395 Of course the word 'beat' has been left loosely defined above.
396 In music, a beat is defined by the musical pulse (which comes from the tempo)
397 and the meter in use at a particular time (how many pulse divisions there are in one bar).
398 It would be more accurate to substitute the work 'pulse' for 'beat' above.
402 We can now store c for future time calculations.
403 If the following tempo section (the one that defines c in conjunction with this one)
404 is changed or moved, c is no longer valid.
406 The public methods are session-relative.
408 Most of this stuff is taken from this paper:
411 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
414 Zurich University of Arts
415 Institute for Computer Music and Sound Technology
417 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
421 /** compute this ramp's function constant from some tempo-pulse point
422 * @param end_npm end tempo (in note types per minute)
423 * @param end_pulse duration (pulses into global start) of some other position.
424 * @return the calculated function constant
427 TempoSection::compute_c_func_pulse (const double& end_npm, const double& end_pulse) const
429 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
430 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
433 /** compute the function constant from some tempo-time point.
434 * @param end_npm tempo (note types/min.)
435 * @param end_minute distance (in minutes) from session origin
436 * @return the calculated function constant
439 TempoSection::compute_c_func_minute (const double& end_npm, const double& end_minute) const
441 return c_func (end_npm, end_minute - minute());
444 /* position function */
446 TempoSection::a_func (double end_npm, double c_func) const
448 return log (end_npm / note_types_per_minute()) / c_func;
451 /*function constant*/
453 TempoSection::c_func (double end_npm, double end_time) const
455 return log (end_npm / note_types_per_minute()) / end_time;
458 /* tempo in note types per minute at time in minutes */
460 TempoSection::_tempo_at_time (const double& time) const
462 return exp (_c_func * time) * note_types_per_minute();
465 /* time in minutes at tempo in note types per minute */
467 TempoSection::_time_at_tempo (const double& npm) const
469 return log (npm / note_types_per_minute()) / _c_func;
472 /* pulse at tempo in note types per minute */
474 TempoSection::_pulse_at_tempo (const double& npm) const
476 return ((npm - note_types_per_minute()) / _c_func) / _note_type;
479 /* tempo in note types per minute at pulse */
481 TempoSection::_tempo_at_pulse (const double& pulse) const
483 return (pulse * _note_type * _c_func) + note_types_per_minute();
486 /* pulse at time in minutes */
488 TempoSection::_pulse_at_time (const double& time) const
490 return (expm1 (_c_func * time) * (note_types_per_minute() / _c_func)) / _note_type;
493 /* time in minutes at pulse */
495 TempoSection::_time_at_pulse (const double& pulse) const
497 return log1p ((_c_func * pulse * _note_type) / note_types_per_minute()) / _c_func;
500 /***********************************************************************/
502 const string MeterSection::xml_state_node_name = "Meter";
504 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
505 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
507 XMLProperty const * prop;
512 framepos_t frame = 0;
513 pair<double, BBT_Time> start;
515 if ((prop = node.property ("start")) != 0) {
516 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
520 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
522 /* legacy session - start used to be in bbt*/
523 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
528 if ((prop = node.property ("pulse")) != 0) {
529 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
530 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
535 if ((prop = node.property ("beat")) != 0) {
536 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
537 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
543 if ((prop = node.property ("bbt")) == 0) {
544 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
545 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
549 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
550 throw failed_constructor();
556 if ((prop = node.property ("frame")) != 0) {
557 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
558 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
559 throw failed_constructor();
561 set_minute (minute_at_frame (frame));
565 /* beats-per-bar is old; divisions-per-bar is new */
567 if ((prop = node.property ("divisions-per-bar")) == 0) {
568 if ((prop = node.property ("beats-per-bar")) == 0) {
569 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
570 throw failed_constructor();
573 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
574 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
575 throw failed_constructor();
578 if ((prop = node.property ("note-type")) == 0) {
579 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
580 throw failed_constructor();
582 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
583 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
584 throw failed_constructor();
587 if ((prop = node.property ("movable")) == 0) {
588 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
589 throw failed_constructor();
592 set_initial (!string_is_affirmative (prop->value()));
594 if ((prop = node.property ("lock-style")) == 0) {
595 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
597 set_position_lock_style (MusicTime);
599 set_position_lock_style (AudioTime);
602 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
607 MeterSection::get_state() const
609 XMLNode *root = new XMLNode (xml_state_node_name);
613 snprintf (buf, sizeof (buf), "%lf", pulse());
614 root->add_property ("pulse", buf);
615 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
619 root->add_property ("bbt", buf);
620 snprintf (buf, sizeof (buf), "%lf", beat());
621 root->add_property ("beat", buf);
622 snprintf (buf, sizeof (buf), "%lf", _note_type);
623 root->add_property ("note-type", buf);
624 snprintf (buf, sizeof (buf), "%li", frame());
625 root->add_property ("frame", buf);
626 root->add_property ("lock-style", enum_2_string (position_lock_style()));
627 snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
628 root->add_property ("divisions-per-bar", buf);
629 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
630 root->add_property ("movable", buf);
635 /***********************************************************************/
639 Tempo determines the rate of musical pulse determined by its components
640 note types per minute - the rate per minute of the whole note divisor _note_type
641 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
642 Meter divides the musical pulse into measures and beats according to its components
646 TempoSection - translates between time, musical pulse and tempo.
647 has a musical location in whole notes (pulses).
648 has a time location in minutes.
649 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
650 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
652 MeterSection - translates between BBT, meter-based beat and musical pulse.
653 has a musical location in whole notes (pulses)
654 has a musical location in meter-based beats
655 has a musical location in BBT time
656 has a time location expressed in minutes.
658 TempoSection and MeterSection may be locked to either audio or music (position lock style).
659 The lock style determines the location type to be kept as a reference when location is recalculated.
661 The first tempo and meter are special. they must move together, and are locked to audio.
662 Audio locked tempi which lie before the first meter are made inactive.
664 Recomputing the map is the process where the 'missing' location types are calculated.
665 We construct the tempo map by first using the locked location type of each section
666 to determine non-locked location types (pulse or minute position).
667 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
669 Having done this, we can now traverse the Metrics list by pulse or minute
670 to query its relevant meter/tempo.
672 It is important to keep the _metrics in an order that makes sense.
673 Because ramped MusicTime and AudioTime tempos can interact with each other,
674 reordering is frequent. Care must be taken to keep _metrics in a solved state.
675 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
679 Music and audio-locked objects may seem interchangeable on the surface, but when translating
680 between audio samples and beat, remember that a sample is only a quantised approximation
681 of the actual time (in minutes) of a beat.
682 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
683 mean that this frame is the actual location in time of 1|3|0.
685 You cannot use a frame measurement to determine beat distance except under special circumstances
686 (e.g. where the user has requested that a beat lie on a SMPTE frame or if the tempo is known to be constant over the duration).
688 This means is that a user operating on a musical grid must supply the desired beat position and/or current beat quantization in order for the
689 sample space the user is operating at to be translated correctly to the object.
691 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
692 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
693 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
695 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
697 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
698 The result is rounded to audio frames.
699 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
702 frame_at_beat (beat_at_frame (frame)) == frame
704 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
706 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
707 frames_between_quarter_notes () eliminats this effect when determining time duration
708 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
710 The above pointless example could instead do:
711 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
713 The Shaggs - Things I Wonder
714 https://www.youtube.com/watch?v=9wQK6zMJOoQ
717 struct MetricSectionSorter {
718 bool operator() (const MetricSection* a, const MetricSection* b) {
719 return a->pulse() < b->pulse();
723 struct MetricSectionFrameSorter {
724 bool operator() (const MetricSection* a, const MetricSection* b) {
725 return a->frame() < b->frame();
729 TempoMap::TempoMap (framecnt_t fr)
732 BBT_Time start (1, 1, 0);
734 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo.note_types_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime, fr);
735 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
737 t->set_initial (true);
738 m->set_initial (true);
740 /* note: frame time is correct (zero) for both of these */
742 _metrics.push_back (t);
743 _metrics.push_back (m);
747 TempoMap::~TempoMap ()
749 Metrics::const_iterator d = _metrics.begin();
750 while (d != _metrics.end()) {
758 TempoMap::frame_at_minute (const double time) const
760 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
764 TempoMap::minute_at_frame (const framepos_t frame) const
766 return (frame / (double) _frame_rate) / 60.0;
770 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
772 bool removed = false;
775 Glib::Threads::RWLock::WriterLock lm (lock);
776 if ((removed = remove_tempo_locked (tempo))) {
777 if (complete_operation) {
778 recompute_map (_metrics);
783 if (removed && complete_operation) {
784 PropertyChanged (PropertyChange ());
789 TempoMap::remove_tempo_locked (const TempoSection& tempo)
793 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
794 if (dynamic_cast<TempoSection*> (*i) != 0) {
795 if (tempo.frame() == (*i)->frame()) {
796 if (!(*i)->initial()) {
809 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
811 bool removed = false;
814 Glib::Threads::RWLock::WriterLock lm (lock);
815 if ((removed = remove_meter_locked (tempo))) {
816 if (complete_operation) {
817 recompute_map (_metrics);
822 if (removed && complete_operation) {
823 PropertyChanged (PropertyChange ());
828 TempoMap::remove_meter_locked (const MeterSection& meter)
831 if (meter.position_lock_style() == AudioTime) {
832 /* remove meter-locked tempo */
833 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
835 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
836 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
845 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
846 if (dynamic_cast<MeterSection*> (*i) != 0) {
847 if (meter.frame() == (*i)->frame()) {
848 if (!(*i)->initial()) {
861 TempoMap::do_insert (MetricSection* section)
863 bool need_add = true;
864 /* we only allow new meters to be inserted on beat 1 of an existing
868 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
870 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
872 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
873 corrected.second.beats = 1;
874 corrected.second.ticks = 0;
875 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
876 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
877 m->bbt(), corrected.second) << endmsg;
878 //m->set_pulse (corrected);
882 /* Look for any existing MetricSection that is of the same type and
883 in the same bar as the new one, and remove it before adding
884 the new one. Note that this means that if we find a matching,
885 existing section, we can break out of the loop since we're
886 guaranteed that there is only one such match.
889 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
891 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
892 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
893 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
894 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
896 if (tempo && insert_tempo) {
899 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
900 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
902 if (tempo->initial()) {
904 /* can't (re)move this section, so overwrite
905 * its data content (but not its properties as
909 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
910 (*i)->set_position_lock_style (AudioTime);
912 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
913 t->set_type (insert_tempo->type());
923 } else if (meter && insert_meter) {
927 bool const ipm = insert_meter->position_lock_style() == MusicTime;
929 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
931 if (meter->initial()) {
933 /* can't (re)move this section, so overwrite
934 * its data content (but not its properties as
938 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
939 (*i)->set_position_lock_style (AudioTime);
949 /* non-matching types, so we don't care */
953 /* Add the given MetricSection, if we didn't just reset an existing
958 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
959 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
962 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
963 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
966 bool const ipm = insert_meter->position_lock_style() == MusicTime;
967 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
972 } else if (insert_tempo) {
973 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
974 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
977 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
978 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
985 _metrics.insert (i, section);
986 //dump (_metrics, std::cout);
989 /* user supplies the exact pulse if pls == MusicTime */
991 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
993 TempoSection* ts = 0;
995 Glib::Threads::RWLock::WriterLock lm (lock);
996 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
1000 PropertyChanged (PropertyChange ());
1006 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
1008 const bool locked_to_meter = ts.locked_to_meter();
1011 Glib::Threads::RWLock::WriterLock lm (lock);
1012 TempoSection& first (first_tempo());
1013 if (ts.frame() != first.frame()) {
1014 remove_tempo_locked (ts);
1015 add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter);
1017 first.set_type (type);
1018 first.set_pulse (0.0);
1019 first.set_minute (minute_at_frame (frame));
1020 first.set_position_lock_style (AudioTime);
1022 /* cannot move the first tempo section */
1023 *static_cast<Tempo*>(&first) = tempo;
1024 recompute_map (_metrics);
1029 PropertyChanged (PropertyChange ());
1033 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1034 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1036 TempoSection* t = new TempoSection (pulse, minute, tempo.note_types_per_minute(), tempo.note_type(), type, pls, _frame_rate);
1037 t->set_locked_to_meter (locked_to_meter);
1038 bool solved = false;
1043 if (pls == AudioTime) {
1044 solved = solve_map_minute (_metrics, t, t->minute());
1046 solved = solve_map_pulse (_metrics, t, t->pulse());
1048 recompute_meters (_metrics);
1051 if (!solved && recompute) {
1052 recompute_map (_metrics);
1059 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, PositionLockStyle pls)
1061 MeterSection* m = 0;
1063 Glib::Threads::RWLock::WriterLock lm (lock);
1064 m = add_meter_locked (meter, beat, where, pls, true);
1069 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1070 dump (_metrics, std::cerr);
1074 PropertyChanged (PropertyChange ());
1079 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, PositionLockStyle pls)
1082 Glib::Threads::RWLock::WriterLock lm (lock);
1083 const double beat = beat_at_bbt_locked (_metrics, where);
1085 if (!ms.initial()) {
1086 remove_meter_locked (ms);
1087 add_meter_locked (meter, beat, where, pls, true);
1089 MeterSection& first (first_meter());
1090 TempoSection& first_t (first_tempo());
1091 /* cannot move the first meter section */
1092 *static_cast<Meter*>(&first) = meter;
1093 first.set_position_lock_style (AudioTime);
1094 first.set_pulse (0.0);
1095 //first.set_minute (minute_at_frame (frame));
1096 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1097 first.set_beat (beat);
1098 first_t.set_minute (first.minute());
1099 first_t.set_pulse (0.0);
1100 first_t.set_position_lock_style (AudioTime);
1101 recompute_map (_metrics);
1105 PropertyChanged (PropertyChange ());
1109 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, PositionLockStyle pls, bool recompute)
1111 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1112 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1113 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1114 TempoSection* mlt = 0;
1116 if (pls == AudioTime) {
1117 /* add meter-locked tempo */
1118 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, time_minutes, TempoSection::Ramp, AudioTime, true, true);
1126 MeterSection* new_meter = new MeterSection (pulse, time_minutes, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1127 bool solved = false;
1129 do_insert (new_meter);
1133 if (pls == AudioTime) {
1134 solved = solve_map_minute (_metrics, new_meter, time_minutes);
1136 solved = solve_map_bbt (_metrics, new_meter, where);
1137 /* required due to resetting the pulse of meter-locked tempi above.
1138 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1139 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1141 recompute_map (_metrics);
1145 if (!solved && recompute) {
1146 /* if this has failed to solve, there is little we can do other than to ensure that
1147 the new map is recalculated.
1149 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1150 recompute_map (_metrics);
1157 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type)
1159 Tempo newtempo (note_types_per_minute, note_type);
1162 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1163 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1168 Glib::Threads::RWLock::WriterLock lm (lock);
1169 *((Tempo*) t) = newtempo;
1170 recompute_map (_metrics);
1172 PropertyChanged (PropertyChange ());
1179 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type)
1181 Tempo newtempo (note_types_per_minute, note_type);
1184 TempoSection* first;
1185 Metrics::iterator i;
1187 /* find the TempoSection immediately preceding "where"
1190 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1192 if ((*i)->frame() > where) {
1198 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1211 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1221 Glib::Threads::RWLock::WriterLock lm (lock);
1222 /* cannot move the first tempo section */
1223 *((Tempo*)prev) = newtempo;
1224 recompute_map (_metrics);
1227 PropertyChanged (PropertyChange ());
1231 TempoMap::first_meter () const
1233 const MeterSection *m = 0;
1235 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1236 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1241 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1242 abort(); /*NOTREACHED*/
1247 TempoMap::first_meter ()
1249 MeterSection *m = 0;
1251 /* CALLER MUST HOLD LOCK */
1253 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1254 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1259 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1260 abort(); /*NOTREACHED*/
1265 TempoMap::first_tempo () const
1267 const TempoSection *t = 0;
1269 /* CALLER MUST HOLD LOCK */
1271 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1272 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1282 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1283 abort(); /*NOTREACHED*/
1288 TempoMap::first_tempo ()
1290 TempoSection *t = 0;
1292 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1293 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1303 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1304 abort(); /*NOTREACHED*/
1308 TempoMap::recompute_tempi (Metrics& metrics)
1310 TempoSection* prev_t = 0;
1312 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1315 if ((*i)->is_tempo()) {
1316 t = static_cast<TempoSection*> (*i);
1328 if (t->position_lock_style() == AudioTime) {
1329 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
1330 if (!t->locked_to_meter()) {
1331 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
1335 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
1336 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
1344 prev_t->set_c_func (0.0);
1347 /* tempos must be positioned correctly.
1348 the current approach is to use a meter's bbt time as its base position unit.
1349 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1350 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1353 TempoMap::recompute_meters (Metrics& metrics)
1355 MeterSection* meter = 0;
1356 MeterSection* prev_m = 0;
1358 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1359 if (!(*mi)->is_tempo()) {
1360 meter = static_cast<MeterSection*> (*mi);
1361 if (meter->position_lock_style() == AudioTime) {
1363 pair<double, BBT_Time> b_bbt;
1364 TempoSection* meter_locked_tempo = 0;
1365 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1367 if ((*ii)->is_tempo()) {
1368 t = static_cast<TempoSection*> (*ii);
1369 if ((t->locked_to_meter() || t->initial()) && t->frame() == meter->frame()) {
1370 meter_locked_tempo = t;
1377 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1378 if (beats + prev_m->beat() != meter->beat()) {
1379 /* reordering caused a bbt change */
1381 beats = meter->beat() - prev_m->beat();
1382 b_bbt = make_pair (beats + prev_m->beat()
1383 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1384 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1386 } else if (!meter->initial()) {
1387 b_bbt = make_pair (meter->beat(), meter->bbt());
1388 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1391 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1393 if (meter_locked_tempo) {
1394 meter_locked_tempo->set_pulse (pulse);
1396 meter->set_beat (b_bbt);
1397 meter->set_pulse (pulse);
1402 pair<double, BBT_Time> b_bbt;
1404 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1405 if (beats + prev_m->beat() != meter->beat()) {
1406 /* reordering caused a bbt change */
1407 b_bbt = make_pair (beats + prev_m->beat()
1408 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1410 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1412 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1414 /* shouldn't happen - the first is audio-locked */
1415 pulse = pulse_at_beat_locked (metrics, meter->beat());
1416 b_bbt = make_pair (meter->beat(), meter->bbt());
1419 meter->set_beat (b_bbt);
1420 meter->set_pulse (pulse);
1421 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1430 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1432 /* CALLER MUST HOLD WRITE LOCK */
1434 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1437 /* silly call from Session::process() during startup
1442 recompute_tempi (metrics);
1443 recompute_meters (metrics);
1447 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1449 Glib::Threads::RWLock::ReaderLock lm (lock);
1450 TempoMetric m (first_meter(), first_tempo());
1452 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1453 at something, because we insert the default tempo and meter during
1454 TempoMap construction.
1456 now see if we can find better candidates.
1459 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1461 if ((*i)->frame() > frame) {
1475 /* XX meters only */
1477 TempoMap::metric_at (BBT_Time bbt) const
1479 Glib::Threads::RWLock::ReaderLock lm (lock);
1480 TempoMetric m (first_meter(), first_tempo());
1482 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1483 at something, because we insert the default tempo and meter during
1484 TempoMap construction.
1486 now see if we can find better candidates.
1489 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1491 if (!(*i)->is_tempo()) {
1492 mw = static_cast<MeterSection*> (*i);
1493 BBT_Time section_start (mw->bbt());
1495 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1506 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1507 * @param frame The session frame position.
1508 * @return The beat duration according to the tempo map at the supplied frame.
1510 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1511 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1513 * This function uses both tempo and meter.
1516 TempoMap::beat_at_frame (const framecnt_t& frame) const
1518 Glib::Threads::RWLock::ReaderLock lm (lock);
1520 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1523 /* This function uses both tempo and meter.*/
1525 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1527 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1528 MeterSection* prev_m = 0;
1529 MeterSection* next_m = 0;
1531 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1532 if (!(*i)->is_tempo()) {
1533 if (prev_m && (*i)->minute() > minute) {
1534 next_m = static_cast<MeterSection*> (*i);
1537 prev_m = static_cast<MeterSection*> (*i);
1541 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1543 /* audio locked meters fake their beat */
1544 if (next_m && next_m->beat() < beat) {
1545 return next_m->beat();
1551 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1552 * @param beat The BBT (meter-based) beat.
1553 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1555 * This function uses both tempo and meter.
1558 TempoMap::frame_at_beat (const double& beat) const
1560 Glib::Threads::RWLock::ReaderLock lm (lock);
1562 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1565 /* meter & tempo section based */
1567 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1569 MeterSection* prev_m = 0;
1570 TempoSection* prev_t = 0;
1574 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1575 if (!(*i)->is_tempo()) {
1576 m = static_cast<MeterSection*> (*i);
1577 if (prev_m && m->beat() > beat) {
1587 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1588 if ((*i)->is_tempo()) {
1589 t = static_cast<TempoSection*> (*i);
1590 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1599 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1602 /** Returns a Tempo corresponding to the supplied frame position.
1603 * @param frame The audio frame.
1604 * @return a Tempo according to the tempo map at the supplied frame.
1608 TempoMap::tempo_at_frame (const framepos_t& frame) const
1610 Glib::Threads::RWLock::ReaderLock lm (lock);
1612 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1616 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1618 TempoSection* prev_t = 0;
1622 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1623 if ((*i)->is_tempo()) {
1624 t = static_cast<TempoSection*> (*i);
1628 if ((prev_t) && t->minute() > minute) {
1629 /* t is the section past frame */
1630 return prev_t->tempo_at_minute (minute);
1636 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1639 /** returns the frame at which the supplied tempo occurs, or
1640 * the frame of the last tempo section (search exhausted)
1641 * only the position of the first occurence will be returned
1645 TempoMap::frame_at_tempo (const Tempo& tempo) const
1647 Glib::Threads::RWLock::ReaderLock lm (lock);
1649 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1653 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1655 TempoSection* prev_t = 0;
1656 const double tempo_bpm = tempo.note_types_per_minute();
1658 Metrics::const_iterator i;
1660 for (i = metrics.begin(); i != metrics.end(); ++i) {
1662 if ((*i)->is_tempo()) {
1663 t = static_cast<TempoSection*> (*i);
1669 const double t_bpm = t->note_types_per_minute();
1671 if (t_bpm == tempo_bpm) {
1676 const double prev_t_bpm = prev_t->note_types_per_minute();
1678 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1679 return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse());
1686 return prev_t->minute();
1690 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1692 TempoSection* prev_t = 0;
1696 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1697 if ((*i)->is_tempo()) {
1698 t = static_cast<TempoSection*> (*i);
1702 if ((prev_t) && t->pulse() > pulse) {
1703 /* t is the section past frame */
1704 return prev_t->tempo_at_pulse (pulse);
1710 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1714 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1716 TempoSection* prev_t = 0;
1717 const double tempo_bpm = tempo.note_types_per_minute();
1719 Metrics::const_iterator i;
1721 for (i = metrics.begin(); i != metrics.end(); ++i) {
1723 if ((*i)->is_tempo()) {
1724 t = static_cast<TempoSection*> (*i);
1730 const double t_bpm = t->note_types_per_minute();
1732 if (t_bpm == tempo_bpm) {
1737 const double prev_t_bpm = prev_t->note_types_per_minute();
1739 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1740 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1747 return prev_t->pulse();
1750 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1751 * @param qn the position in quarter note beats.
1752 * @return the Tempo at the supplied quarter-note.
1755 TempoMap::tempo_at_quarter_note (const double& qn) const
1757 Glib::Threads::RWLock::ReaderLock lm (lock);
1759 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1762 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1763 * @param tempo the tempo.
1764 * @return the position in quarter-note beats where the map bpm
1765 * is equal to that of the Tempo. currently ignores note_type.
1768 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1770 Glib::Threads::RWLock::ReaderLock lm (lock);
1772 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1775 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1776 * @param metrics the list of metric sections used to calculate the pulse.
1777 * @param beat The BBT (meter-based) beat.
1778 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1780 * a pulse or whole note is the base musical position of a MetricSection.
1781 * it is equivalent to four quarter notes.
1785 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1787 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1789 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1792 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1793 * @param metrics the list of metric sections used to calculate the beat.
1794 * @param pulse the whole-note pulse.
1795 * @return the meter-based beat at the supplied whole-note pulse.
1797 * a pulse or whole note is the base musical position of a MetricSection.
1798 * it is equivalent to four quarter notes.
1801 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1803 MeterSection* prev_m = 0;
1805 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1807 if (!(*i)->is_tempo()) {
1808 m = static_cast<MeterSection*> (*i);
1809 if (prev_m && m->pulse() > pulse) {
1817 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1821 /* tempo section based */
1823 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1825 /* HOLD (at least) THE READER LOCK */
1826 TempoSection* prev_t = 0;
1828 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1830 if ((*i)->is_tempo()) {
1831 t = static_cast<TempoSection*> (*i);
1835 if (prev_t && t->minute() > minute) {
1836 /*the previous ts is the one containing the frame */
1837 const double ret = prev_t->pulse_at_minute (minute);
1838 /* audio locked section in new meter*/
1839 if (t->pulse() < ret) {
1848 /* treated as constant for this ts */
1849 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1851 return pulses_in_section + prev_t->pulse();
1854 /* tempo section based */
1856 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1858 /* HOLD THE READER LOCK */
1860 const TempoSection* prev_t = 0;
1862 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1865 if ((*i)->is_tempo()) {
1866 t = static_cast<TempoSection*> (*i);
1870 if (prev_t && t->pulse() > pulse) {
1871 return prev_t->minute_at_pulse (pulse);
1877 /* must be treated as constant, irrespective of _type */
1878 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1880 return dtime + prev_t->minute();
1883 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1884 * @param bbt The BBT time (meter-based).
1885 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1889 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1891 Glib::Threads::RWLock::ReaderLock lm (lock);
1892 return beat_at_bbt_locked (_metrics, bbt);
1897 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1899 /* CALLER HOLDS READ LOCK */
1901 MeterSection* prev_m = 0;
1903 /* because audio-locked meters have 'fake' integral beats,
1904 there is no pulse offset here.
1908 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1909 if (!(*i)->is_tempo()) {
1910 m = static_cast<MeterSection*> (*i);
1912 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1913 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1921 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1922 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1923 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1928 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
1929 * @param beat The BBT (meter-based) beat.
1930 * @return The BBT time (meter-based) at the supplied meter-based beat.
1934 TempoMap::bbt_at_beat (const double& beat)
1936 Glib::Threads::RWLock::ReaderLock lm (lock);
1937 return bbt_at_beat_locked (_metrics, beat);
1941 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1943 /* CALLER HOLDS READ LOCK */
1944 MeterSection* prev_m = 0;
1945 const double beats = max (0.0, b);
1947 MeterSection* m = 0;
1949 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1950 if (!(*i)->is_tempo()) {
1951 m = static_cast<MeterSection*> (*i);
1953 if (m->beat() > beats) {
1954 /* this is the meter after the one our beat is on*/
1964 const double beats_in_ms = beats - prev_m->beat();
1965 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1966 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1967 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1968 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1972 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1973 ret.beats = (uint32_t) floor (remaining_beats);
1974 ret.bars = total_bars;
1976 /* 0 0 0 to 1 1 0 - based mapping*/
1980 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1982 ret.ticks -= BBT_Time::ticks_per_beat;
1985 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1993 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
1994 * @param bbt The BBT time (meter-based).
1995 * @return the quarter note beat at the supplied BBT time
1997 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
1999 * while the input uses meter, the output does not.
2002 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2004 Glib::Threads::RWLock::ReaderLock lm (lock);
2006 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2010 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2012 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2015 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2018 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2022 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2024 /* CALLER HOLDS READ LOCK */
2026 MeterSection* prev_m = 0;
2028 /* because audio-locked meters have 'fake' integral beats,
2029 there is no pulse offset here.
2033 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2034 if (!(*i)->is_tempo()) {
2035 m = static_cast<MeterSection*> (*i);
2037 if (m->bbt().bars > bbt.bars) {
2045 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2046 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2047 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2052 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2053 * @param qn the quarter-note beat.
2054 * @return The BBT time (meter-based) at the supplied meter-based beat.
2056 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2060 TempoMap::bbt_at_quarter_note (const double& qn)
2062 Glib::Threads::RWLock::ReaderLock lm (lock);
2064 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2067 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2068 * @param metrics The list of metric sections used to determine the result.
2069 * @param pulse The whole-note pulse.
2070 * @return The BBT time at the supplied whole-note pulse.
2072 * a pulse or whole note is the basic musical position of a MetricSection.
2073 * it is equivalent to four quarter notes.
2074 * while the output uses meter, the input does not.
2077 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2079 MeterSection* prev_m = 0;
2081 MeterSection* m = 0;
2083 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2085 if (!(*i)->is_tempo()) {
2086 m = static_cast<MeterSection*> (*i);
2089 double const pulses_to_m = m->pulse() - prev_m->pulse();
2090 if (prev_m->pulse() + pulses_to_m > pulse) {
2091 /* this is the meter after the one our beat is on*/
2102 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2103 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2104 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2105 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2106 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2110 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2111 ret.beats = (uint32_t) floor (remaining_beats);
2112 ret.bars = total_bars;
2114 /* 0 0 0 to 1 1 0 mapping*/
2118 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2120 ret.ticks -= BBT_Time::ticks_per_beat;
2123 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2131 /** Returns the BBT time corresponding to the supplied frame position.
2132 * @param frame the position in audio samples.
2133 * @return the BBT time at the frame position .
2137 TempoMap::bbt_at_frame (framepos_t frame)
2144 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2147 Glib::Threads::RWLock::ReaderLock lm (lock);
2149 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2153 TempoMap::bbt_at_frame_rt (framepos_t frame)
2155 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2158 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2161 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2165 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2175 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2176 MeterSection* prev_m = 0;
2177 MeterSection* next_m = 0;
2181 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2182 if (!(*i)->is_tempo()) {
2183 m = static_cast<MeterSection*> (*i);
2184 if (prev_m && m->minute() > minute) {
2192 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2194 /* handle frame before first meter */
2195 if (minute < prev_m->minute()) {
2198 /* audio locked meters fake their beat */
2199 if (next_m && next_m->beat() < beat) {
2200 beat = next_m->beat();
2203 beat = max (0.0, beat);
2205 const double beats_in_ms = beat - prev_m->beat();
2206 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2207 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2208 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2209 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2213 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2214 ret.beats = (uint32_t) floor (remaining_beats);
2215 ret.bars = total_bars;
2217 /* 0 0 0 to 1 1 0 - based mapping*/
2221 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2223 ret.ticks -= BBT_Time::ticks_per_beat;
2226 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2234 /** Returns the frame position corresponding to the supplied BBT time.
2235 * @param bbt the position in BBT time.
2236 * @return the frame position at bbt.
2240 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2243 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2247 if (bbt.beats < 1) {
2248 throw std::logic_error ("beats are counted from one");
2250 Glib::Threads::RWLock::ReaderLock lm (lock);
2252 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
2255 /* meter & tempo section based */
2257 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2259 /* HOLD THE READER LOCK */
2261 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2266 * Returns the quarter-note beat position corresponding to the supplied frame.
2268 * @param frame the position in frames.
2269 * @return The quarter-note position of the supplied frame. Ignores meter.
2273 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2275 Glib::Threads::RWLock::ReaderLock lm (lock);
2277 const double ret = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
2283 TempoMap::quarter_note_at_minute_locked (const Metrics& metrics, const double minute) const
2285 const double ret = pulse_at_minute_locked (metrics, minute) * 4.0;
2291 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2293 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2296 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2299 const double ret = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
2305 * Returns the frame position corresponding to the supplied quarter-note beat.
2307 * @param quarter_note the quarter-note position.
2308 * @return the frame position of the supplied quarter-note. Ignores meter.
2313 TempoMap::frame_at_quarter_note (const double quarter_note) const
2315 Glib::Threads::RWLock::ReaderLock lm (lock);
2317 const framepos_t ret = frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note));
2323 TempoMap::minute_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2325 const double ret = minute_at_pulse_locked (metrics, quarter_note / 4.0);
2330 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2331 * @param beat The BBT (meter-based) beat.
2332 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2334 * a quarter-note may be compared with and assigned to Evoral::Beats.
2338 TempoMap::quarter_note_at_beat (const double beat) const
2340 Glib::Threads::RWLock::ReaderLock lm (lock);
2342 const double ret = quarter_note_at_beat_locked (_metrics, beat);
2348 TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) const
2350 const double ret = pulse_at_beat_locked (metrics, beat) * 4.0;
2355 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2356 * @param quarter_note The position in quarter-note beats.
2357 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2359 * a quarter-note is the musical unit of Evoral::Beats.
2363 TempoMap::beat_at_quarter_note (const double quarter_note) const
2365 Glib::Threads::RWLock::ReaderLock lm (lock);
2367 const double ret = beat_at_quarter_note_locked (_metrics, quarter_note);
2373 TempoMap::beat_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2376 return beat_at_pulse_locked (metrics, quarter_note / 4.0);
2379 /** Returns the duration in frames between two supplied quarter-note beat positions.
2380 * @param start the first position in quarter-note beats.
2381 * @param end the end position in quarter-note beats.
2382 * @return the frame distance ober the quarter-note beats duration.
2384 * use this rather than e.g.
2385 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2386 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2390 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2392 Glib::Threads::RWLock::ReaderLock lm (lock);
2394 return frame_at_minute (minutes_between_quarter_notes_locked (_metrics, start, end));
2398 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2401 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2405 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2407 Glib::Threads::RWLock::ReaderLock lm (lock);
2409 return quarter_notes_between_frames_locked (_metrics, start, end);
2413 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2415 const TempoSection* prev_t = 0;
2417 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2420 if ((*i)->is_tempo()) {
2421 t = static_cast<TempoSection*> (*i);
2425 if (prev_t && t->frame() > start) {
2432 const double start_qn = prev_t->pulse_at_frame (start);
2434 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2437 if ((*i)->is_tempo()) {
2438 t = static_cast<TempoSection*> (*i);
2442 if (prev_t && t->frame() > end) {
2448 const double end_qn = prev_t->pulse_at_frame (end);
2450 return (end_qn - start_qn) * 4.0;
2454 TempoMap::check_solved (const Metrics& metrics) const
2456 TempoSection* prev_t = 0;
2457 MeterSection* prev_m = 0;
2459 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2462 if ((*i)->is_tempo()) {
2463 t = static_cast<TempoSection*> (*i);
2468 /* check ordering */
2469 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2473 /* precision check ensures tempo and frames align.*/
2474 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))) {
2475 if (!t->locked_to_meter()) {
2480 /* gradient limit - who knows what it should be?
2481 things are also ok (if a little chaotic) without this
2483 if (fabs (prev_t->c_func()) > 1000.0) {
2484 //std::cout << "c : " << prev_t->c_func() << std::endl;
2491 if (!(*i)->is_tempo()) {
2492 m = static_cast<MeterSection*> (*i);
2493 if (prev_m && m->position_lock_style() == AudioTime) {
2494 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2495 const double nascent_m_minute = t->minute_at_pulse (m->pulse());
2496 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2498 if (t && (nascent_m_minute > m->minute() || nascent_m_minute < 0.0)) {
2512 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2514 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2516 if ((*i)->is_tempo()) {
2517 t = static_cast<TempoSection*> (*i);
2519 t->set_active (true);
2521 } else if (t->position_lock_style() == AudioTime) {
2522 if (t->active () && t->frame() < frame) {
2523 t->set_active (false);
2525 } else if (t->frame() > frame) {
2526 t->set_active (true);
2527 } else if (t->frame() == frame) {
2537 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2539 TempoSection* prev_t = 0;
2540 TempoSection* section_prev = 0;
2541 double first_m_minute = 0.0;
2543 /* can't move a tempo before the first meter */
2544 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2546 if (!(*i)->is_tempo()) {
2547 m = static_cast<MeterSection*> (*i);
2549 first_m_minute = m->minute();
2554 if (!section->initial() && minute <= first_m_minute) {
2558 section->set_active (true);
2559 section->set_minute (minute);
2561 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2563 if ((*i)->is_tempo()) {
2564 t = static_cast<TempoSection*> (*i);
2571 section_prev = prev_t;
2572 if (t->locked_to_meter()) {
2578 if (t->frame() == frame_at_minute (minute)) {
2582 if (t->position_lock_style() == MusicTime) {
2583 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2584 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2586 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2587 if (!t->locked_to_meter()) {
2588 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2597 section_prev->set_c_func (section_prev->compute_c_func_minute (section->note_types_per_minute(), minute));
2598 if (!section->locked_to_meter()) {
2599 section->set_pulse (section_prev->pulse_at_ntpm (section->note_types_per_minute(), minute));
2604 recompute_tempi (imaginary);
2606 if (check_solved (imaginary)) {
2609 dunp (imaginary, std::cout);
2613 MetricSectionFrameSorter fcmp;
2614 imaginary.sort (fcmp);
2616 recompute_tempi (imaginary);
2618 if (check_solved (imaginary)) {
2626 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2628 TempoSection* prev_t = 0;
2629 TempoSection* section_prev = 0;
2631 section->set_pulse (pulse);
2633 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2635 if ((*i)->is_tempo()) {
2636 t = static_cast<TempoSection*> (*i);
2647 section_prev = prev_t;
2650 if (t->position_lock_style() == MusicTime) {
2651 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2652 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2654 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2655 if (!t->locked_to_meter()) {
2656 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2665 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->note_types_per_minute(), pulse));
2666 section->set_minute (section_prev->minute_at_ntpm (section->note_types_per_minute(), pulse));
2670 recompute_tempi (imaginary);
2672 if (check_solved (imaginary)) {
2675 dunp (imaginary, std::cout);
2679 MetricSectionSorter cmp;
2680 imaginary.sort (cmp);
2682 recompute_tempi (imaginary);
2684 * XX need a restriction here, but only for this case,
2685 * as audio locked tempos don't interact in the same way.
2687 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2689 * |50 bpm |250 bpm |60 bpm
2690 * drag 250 to the pulse after 60->
2691 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2693 if (check_solved (imaginary)) {
2701 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2703 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2704 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2705 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2709 if (section->initial()) {
2710 /* lock the first tempo to our first meter */
2711 if (!set_active_tempi (imaginary, section->frame_at_minute (minute))) {
2716 TempoSection* meter_locked_tempo = 0;
2718 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2720 if ((*ii)->is_tempo()) {
2721 t = static_cast<TempoSection*> (*ii);
2722 if ((t->locked_to_meter() || t->initial()) && t->minute() == section->minute()) {
2723 meter_locked_tempo = t;
2729 if (!meter_locked_tempo) {
2733 MeterSection* prev_m = 0;
2735 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2736 bool solved = false;
2738 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2740 if (!(*i)->is_tempo()) {
2741 m = static_cast<MeterSection*> (*i);
2743 if (prev_m && !section->initial()) {
2744 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2745 if (beats + prev_m->beat() < section->beat()) {
2746 /* set the section pulse according to its musical position,
2747 * as an earlier time than this has been requested.
2749 const double new_pulse = ((section->beat() - prev_m->beat())
2750 / prev_m->note_divisor()) + prev_m->pulse();
2752 tempo_copy->set_position_lock_style (MusicTime);
2753 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2754 meter_locked_tempo->set_position_lock_style (MusicTime);
2755 section->set_position_lock_style (MusicTime);
2756 section->set_pulse (new_pulse);
2757 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2758 meter_locked_tempo->set_position_lock_style (AudioTime);
2759 section->set_position_lock_style (AudioTime);
2760 section->set_minute (meter_locked_tempo->minute());
2766 Metrics::const_iterator d = future_map.begin();
2767 while (d != future_map.end()) {
2776 /* all is ok. set section's locked tempo if allowed.
2777 possibly disallowed if there is an adjacent audio-locked tempo.
2778 XX this check could possibly go. its never actually happened here.
2780 MeterSection* meter_copy = const_cast<MeterSection*>
2781 (&meter_section_at_minute_locked (future_map, section->minute()));
2783 meter_copy->set_minute (minute);
2785 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2786 section->set_minute (minute);
2787 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2788 / prev_m->note_divisor()) + prev_m->pulse());
2789 solve_map_minute (imaginary, meter_locked_tempo, minute);
2794 Metrics::const_iterator d = future_map.begin();
2795 while (d != future_map.end()) {
2805 /* initial (first meter atm) */
2807 tempo_copy->set_minute (minute);
2808 tempo_copy->set_pulse (0.0);
2810 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2811 section->set_minute (minute);
2812 meter_locked_tempo->set_minute (minute);
2813 meter_locked_tempo->set_pulse (0.0);
2814 solve_map_minute (imaginary, meter_locked_tempo, minute);
2819 Metrics::const_iterator d = future_map.begin();
2820 while (d != future_map.end()) {
2829 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2830 section->set_beat (b_bbt);
2831 section->set_pulse (0.0);
2841 MetricSectionFrameSorter fcmp;
2842 imaginary.sort (fcmp);
2844 recompute_meters (imaginary);
2850 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2852 /* disallow setting section to an existing meter's bbt */
2853 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2855 if (!(*i)->is_tempo()) {
2856 m = static_cast<MeterSection*> (*i);
2857 if (m != section && m->bbt().bars == when.bars) {
2863 MeterSection* prev_m = 0;
2864 MeterSection* section_prev = 0;
2866 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2868 if (!(*i)->is_tempo()) {
2869 m = static_cast<MeterSection*> (*i);
2875 pair<double, BBT_Time> b_bbt;
2876 double new_pulse = 0.0;
2878 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2879 section_prev = prev_m;
2881 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2882 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2883 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2885 section->set_beat (b_bbt);
2886 section->set_pulse (pulse);
2887 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2891 if (m->position_lock_style() == AudioTime) {
2892 TempoSection* meter_locked_tempo = 0;
2894 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2896 if ((*ii)->is_tempo()) {
2897 t = static_cast<TempoSection*> (*ii);
2898 if ((t->locked_to_meter() || t->initial()) && t->frame() == m->frame()) {
2899 meter_locked_tempo = t;
2905 if (!meter_locked_tempo) {
2910 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2912 if (beats + prev_m->beat() != m->beat()) {
2913 /* tempo/ meter change caused a change in beat (bar). */
2915 /* the user has requested that the previous section of music overlaps this one.
2916 we have no choice but to change the bar number here, as being locked to audio means
2917 we must stay where we are on the timeline.
2919 beats = m->beat() - prev_m->beat();
2920 b_bbt = make_pair (beats + prev_m->beat()
2921 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2922 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2924 } else if (!m->initial()) {
2925 b_bbt = make_pair (m->beat(), m->bbt());
2926 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2929 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2932 meter_locked_tempo->set_pulse (new_pulse);
2933 m->set_beat (b_bbt);
2934 m->set_pulse (new_pulse);
2938 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2939 if (beats + prev_m->beat() != m->beat()) {
2940 /* tempo/ meter change caused a change in beat (bar). */
2941 b_bbt = make_pair (beats + prev_m->beat()
2942 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2944 b_bbt = make_pair (beats + prev_m->beat()
2947 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2948 m->set_beat (b_bbt);
2949 m->set_pulse (new_pulse);
2950 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
2957 if (!section_prev) {
2959 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2960 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2961 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2963 section->set_beat (b_bbt);
2964 section->set_pulse (pulse);
2965 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2968 MetricSectionSorter cmp;
2969 imaginary.sort (cmp);
2971 recompute_meters (imaginary);
2976 /** places a copy of _metrics into copy and returns a pointer
2977 * to section's equivalent in copy.
2980 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2982 TempoSection* ret = 0;
2984 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2987 if ((*i)->is_tempo()) {
2988 t = static_cast<TempoSection*> (*i);
2990 ret = new TempoSection (*t);
2991 copy.push_back (ret);
2995 TempoSection* cp = new TempoSection (*t);
2996 copy.push_back (cp);
2998 if (!(*i)->is_tempo()) {
2999 m = static_cast<MeterSection *> (*i);
3000 MeterSection* cp = new MeterSection (*m);
3001 copy.push_back (cp);
3009 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3011 MeterSection* ret = 0;
3013 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3016 if ((*i)->is_tempo()) {
3017 t = static_cast<TempoSection*> (*i);
3018 TempoSection* cp = new TempoSection (*t);
3019 copy.push_back (cp);
3022 if (!(*i)->is_tempo()) {
3023 m = static_cast<MeterSection *> (*i);
3025 ret = new MeterSection (*m);
3026 copy.push_back (ret);
3029 MeterSection* cp = new MeterSection (*m);
3030 copy.push_back (cp);
3037 /** answers the question "is this a valid beat position for this tempo section?".
3038 * it returns true if the tempo section can be moved to the requested bbt position,
3039 * leaving the tempo map in a solved state.
3040 * @param ts the tempo section to be moved
3041 * @param bbt the requested new position for the tempo section
3042 * @return true if the tempo section can be moved to the position, otherwise false.
3045 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3048 TempoSection* tempo_copy = 0;
3051 Glib::Threads::RWLock::ReaderLock lm (lock);
3052 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3058 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3060 Metrics::const_iterator d = copy.begin();
3061 while (d != copy.end()) {
3070 * 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,
3071 * taking any possible reordering as a consequence of this into account.
3072 * @param section - the section to be altered
3073 * @param bbt - the BBT time where the altered tempo will fall
3074 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3076 pair<double, framepos_t>
3077 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3080 pair<double, framepos_t> ret = make_pair (0.0, 0);
3082 Glib::Threads::RWLock::ReaderLock lm (lock);
3084 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3086 const double beat = beat_at_bbt_locked (future_map, bbt);
3088 if (section->position_lock_style() == AudioTime) {
3089 tempo_copy->set_position_lock_style (MusicTime);
3092 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3093 ret.first = tempo_copy->pulse();
3094 ret.second = tempo_copy->frame();
3096 ret.first = section->pulse();
3097 ret.second = section->frame();
3100 Metrics::const_iterator d = future_map.begin();
3101 while (d != future_map.end()) {
3108 /** moves a TempoSection to a specified position.
3109 * @param ts - the section to be moved
3110 * @param frame - the new position in frames for the tempo
3111 * @param sub_num - the snap division to use if using musical time.
3113 * if sub_num is non-zero, the frame position is used to calculate an exact
3116 * -1 | snap to bars (meter-based)
3117 * 0 | no snap - use audio frame for musical position
3118 * 1 | snap to meter-based (BBT) beat
3119 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3121 * this follows the snap convention in the gui.
3122 * if sub_num is zero, the musical position will be taken from the supplied frame.
3125 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3129 if (ts->position_lock_style() == MusicTime) {
3131 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3132 Glib::Threads::RWLock::WriterLock lm (lock);
3133 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3135 tempo_copy->set_position_lock_style (AudioTime);
3137 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3138 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3139 const double pulse = pulse_at_beat_locked (future_map, beat);
3141 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3142 solve_map_pulse (_metrics, ts, pulse);
3143 recompute_meters (_metrics);
3151 Glib::Threads::RWLock::WriterLock lm (lock);
3152 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3154 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3156 /* We're moving the object that defines the grid while snapping to it...
3157 * Placing the ts at the beat corresponding to the requested frame may shift the
3158 * grid in such a way that the mouse is left hovering over a completerly different division,
3159 * causing jittering when the mouse next moves (esp. large tempo deltas).
3161 * This alters the snap behaviour slightly in that we snap to beat divisions
3162 * in the future map rather than the existing one.
3164 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3165 const framepos_t snapped_frame = frame_at_minute (minute_at_quarter_note_locked (future_map, qn));
3167 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3168 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3169 ts->set_pulse (qn / 4.0);
3170 recompute_meters (_metrics);
3173 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3174 recompute_meters (_metrics);
3180 Metrics::const_iterator d = future_map.begin();
3181 while (d != future_map.end()) {
3186 MetricPositionChanged (); // Emit Signal
3189 /** moves a MeterSection to a specified position.
3190 * @param ms - the section to be moved
3191 * @param frame - the new position in frames for the meter
3193 * as a meter cannot snap to anything but bars,
3194 * the supplied frame is rounded to the nearest bar, possibly
3195 * leaving the meter position unchanged.
3198 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
3202 if (ms->position_lock_style() == AudioTime) {
3205 Glib::Threads::RWLock::WriterLock lm (lock);
3206 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3208 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3209 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3210 recompute_tempi (_metrics);
3215 Glib::Threads::RWLock::WriterLock lm (lock);
3216 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3218 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3219 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3221 if (solve_map_bbt (future_map, copy, bbt)) {
3222 solve_map_bbt (_metrics, ms, bbt);
3223 recompute_tempi (_metrics);
3228 Metrics::const_iterator d = future_map.begin();
3229 while (d != future_map.end()) {
3234 MetricPositionChanged (); // Emit Signal
3238 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3241 bool can_solve = false;
3243 Glib::Threads::RWLock::WriterLock lm (lock);
3244 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3245 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3246 recompute_tempi (future_map);
3248 if (check_solved (future_map)) {
3249 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3250 recompute_map (_metrics);
3255 Metrics::const_iterator d = future_map.begin();
3256 while (d != future_map.end()) {
3261 MetricPositionChanged (); // Emit Signal
3267 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
3270 Ts (future prev_t) Tnext
3273 |----------|----------
3280 Glib::Threads::RWLock::WriterLock lm (lock);
3286 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3287 TempoSection* prev_to_prev_t = 0;
3288 const frameoffset_t fr_off = end_frame - frame;
3292 if (prev_t->pulse() > 0.0) {
3293 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
3296 TempoSection* next_t = 0;
3297 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
3298 TempoSection* t = 0;
3299 if ((*i)->is_tempo()) {
3300 t = static_cast<TempoSection*> (*i);
3301 if (t->frame() > ts->frame()) {
3307 /* minimum allowed measurement distance in frames */
3308 const framepos_t min_dframe = 2;
3310 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3311 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3313 double contribution = 0.0;
3315 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3316 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3319 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3321 const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame));
3322 const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame));
3326 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
3328 if (prev_t->position_lock_style() == MusicTime) {
3329 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3330 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3332 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3333 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3335 new_bpm = prev_t->note_types_per_minute();
3338 /* prev to prev is irrelevant */
3340 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3341 new_bpm = prev_t->note_types_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3343 new_bpm = prev_t->note_types_per_minute();
3348 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3349 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3351 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3352 / (double) ((end_frame) - prev_to_prev_t->frame()));
3354 new_bpm = prev_t->note_types_per_minute();
3357 /* prev_to_prev_t is irrelevant */
3359 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3360 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3362 new_bpm = prev_t->note_types_per_minute();
3368 double frame_ratio = 1.0;
3369 double pulse_ratio = 1.0;
3370 const double pulse_pos = frame;
3372 if (prev_to_prev_t) {
3373 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3374 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3376 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3377 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3380 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3381 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3383 pulse_ratio = (start_pulse / end_pulse);
3385 new_bpm = prev_t->note_types_per_minute() * (pulse_ratio * frame_ratio);
3388 /* don't clamp and proceed here.
3389 testing has revealed that this can go negative,
3390 which is an entirely different thing to just being too low.
3392 if (new_bpm < 0.5) {
3395 new_bpm = min (new_bpm, (double) 1000.0);
3396 prev_t->set_note_types_per_minute (new_bpm);
3397 recompute_tempi (future_map);
3398 recompute_meters (future_map);
3400 if (check_solved (future_map)) {
3401 ts->set_note_types_per_minute (new_bpm);
3402 recompute_tempi (_metrics);
3403 recompute_meters (_metrics);
3407 Metrics::const_iterator d = future_map.begin();
3408 while (d != future_map.end()) {
3413 MetricPositionChanged (); // Emit Signal
3416 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3417 * the supplied frame, possibly returning a negative value.
3419 * @param frame The session frame position.
3420 * @param sub_num The subdivision to use when rounding the beat.
3421 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3422 * Positive integers indicate quarter note (non BBT) divisions.
3423 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3424 * @return The beat position of the supplied frame.
3426 * when working to a musical grid, the use of sub_nom indicates that
3427 * the position should be interpreted musically.
3429 * it effectively snaps to meter bars, meter beats or quarter note divisions
3430 * (as per current gui convention) and returns a musical position independent of frame rate.
3432 * If the supplied frame lies before the first meter, the return will be negative,
3433 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3434 * the continuation of the tempo curve (backwards).
3436 * This function is sensitive to tempo and meter.
3439 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3441 Glib::Threads::RWLock::ReaderLock lm (lock);
3443 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3447 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3449 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3452 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3453 * the supplied frame, possibly returning a negative value.
3455 * @param frame The session frame position.
3456 * @param sub_num The subdivision to use when rounding the quarter note.
3457 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3458 * Positive integers indicate quarter note (non BBT) divisions.
3459 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3460 * @return The quarter note position of the supplied frame.
3462 * When working to a musical grid, the use of sub_nom indicates that
3463 * the frame position should be interpreted musically.
3465 * it effectively snaps to meter bars, meter beats or quarter note divisions
3466 * (as per current gui convention) and returns a musical position independent of frame rate.
3468 * If the supplied frame lies before the first meter, the return will be negative,
3469 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3470 * the continuation of the tempo curve (backwards).
3472 * This function is tempo-sensitive.
3475 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3477 Glib::Threads::RWLock::ReaderLock lm (lock);
3479 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3483 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3485 double qn = quarter_note_at_minute_locked (metrics, minute_at_frame (frame));
3488 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3489 } else if (sub_num == 1) {
3490 /* the gui requested exact musical (BBT) beat */
3491 qn = quarter_note_at_beat_locked (metrics, floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5));
3492 } else if (sub_num == -1) {
3494 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3498 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3500 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3502 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3512 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3513 * @param pos the frame position in the tempo map.
3514 * @param bbt the distance in BBT time from pos to calculate.
3515 * @param dir the rounding direction..
3516 * @return the duration in frames between pos and bbt
3519 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3521 Glib::Threads::RWLock::ReaderLock lm (lock);
3523 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3525 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3528 pos_bbt.bars += bbt.bars;
3530 pos_bbt.ticks += bbt.ticks;
3531 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3533 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3536 pos_bbt.beats += bbt.beats;
3537 if ((double) pos_bbt.beats > divisions) {
3539 pos_bbt.beats -= divisions;
3541 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3543 return pos_bbt_frame - pos;
3547 if (pos_bbt.bars <= bbt.bars) {
3550 pos_bbt.bars -= bbt.bars;
3553 if (pos_bbt.ticks < bbt.ticks) {
3554 if (pos_bbt.bars > 1) {
3555 if (pos_bbt.beats == 1) {
3557 pos_bbt.beats = divisions;
3561 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3567 pos_bbt.ticks -= bbt.ticks;
3570 if (pos_bbt.beats <= bbt.beats) {
3571 if (pos_bbt.bars > 1) {
3573 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3578 pos_bbt.beats -= bbt.beats;
3581 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3588 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3590 return round_to_type (fr, dir, Bar);
3594 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3596 return round_to_type (fr, dir, Beat);
3600 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3602 Glib::Threads::RWLock::ReaderLock lm (lock);
3603 uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3604 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3605 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3607 ticks -= beats * BBT_Time::ticks_per_beat;
3610 /* round to next (or same iff dir == RoundUpMaybe) */
3612 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3614 if (mod == 0 && dir == RoundUpMaybe) {
3615 /* right on the subdivision, which is fine, so do nothing */
3617 } else if (mod == 0) {
3618 /* right on the subdivision, so the difference is just the subdivision ticks */
3619 ticks += ticks_one_subdivisions_worth;
3622 /* not on subdivision, compute distance to next subdivision */
3624 ticks += ticks_one_subdivisions_worth - mod;
3627 if (ticks >= BBT_Time::ticks_per_beat) {
3628 ticks -= BBT_Time::ticks_per_beat;
3630 } else if (dir < 0) {
3632 /* round to previous (or same iff dir == RoundDownMaybe) */
3634 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3636 if (difference == 0 && dir == RoundDownAlways) {
3637 /* right on the subdivision, but force-rounding down,
3638 so the difference is just the subdivision ticks */
3639 difference = ticks_one_subdivisions_worth;
3642 if (ticks < difference) {
3643 ticks = BBT_Time::ticks_per_beat - ticks;
3645 ticks -= difference;
3649 /* round to nearest */
3652 /* compute the distance to the previous and next subdivision */
3654 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3656 /* closer to the next subdivision, so shift forward */
3658 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3660 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3662 if (ticks > BBT_Time::ticks_per_beat) {
3664 ticks -= BBT_Time::ticks_per_beat;
3665 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3668 } else if (rem > 0) {
3670 /* closer to previous subdivision, so shift backward */
3674 /* can't go backwards past zero, so ... */
3677 /* step back to previous beat */
3679 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3680 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3682 ticks = lrint (ticks - rem);
3683 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3686 /* on the subdivision, do nothing */
3690 const framepos_t ret_frame = frame_at_minute (minute_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3696 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3698 Glib::Threads::RWLock::ReaderLock lm (lock);
3699 uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3700 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3701 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3703 ticks -= beats * BBT_Time::ticks_per_beat;
3706 /* round to next (or same iff dir == RoundUpMaybe) */
3708 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3710 if (mod == 0 && dir == RoundUpMaybe) {
3711 /* right on the subdivision, which is fine, so do nothing */
3713 } else if (mod == 0) {
3714 /* right on the subdivision, so the difference is just the subdivision ticks */
3715 ticks += ticks_one_subdivisions_worth;
3718 /* not on subdivision, compute distance to next subdivision */
3720 ticks += ticks_one_subdivisions_worth - mod;
3723 if (ticks >= BBT_Time::ticks_per_beat) {
3724 ticks -= BBT_Time::ticks_per_beat;
3726 } else if (dir < 0) {
3728 /* round to previous (or same iff dir == RoundDownMaybe) */
3730 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3732 if (difference == 0 && dir == RoundDownAlways) {
3733 /* right on the subdivision, but force-rounding down,
3734 so the difference is just the subdivision ticks */
3735 difference = ticks_one_subdivisions_worth;
3738 if (ticks < difference) {
3739 ticks = BBT_Time::ticks_per_beat - ticks;
3741 ticks -= difference;
3745 /* round to nearest */
3748 /* compute the distance to the previous and next subdivision */
3750 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3752 /* closer to the next subdivision, so shift forward */
3754 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3756 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3758 if (ticks > BBT_Time::ticks_per_beat) {
3760 ticks -= BBT_Time::ticks_per_beat;
3761 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3764 } else if (rem > 0) {
3766 /* closer to previous subdivision, so shift backward */
3770 /* can't go backwards past zero, so ... */
3773 /* step back to previous beat */
3775 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3776 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3778 ticks = lrint (ticks - rem);
3779 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3782 /* on the subdivision, do nothing */
3786 const framepos_t ret_frame = frame_at_minute (minute_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3792 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3794 Glib::Threads::RWLock::ReaderLock lm (lock);
3796 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (frame)));
3797 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3802 /* find bar previous to 'frame' */
3805 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3807 } else if (dir > 0) {
3808 /* find bar following 'frame' */
3812 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3814 /* true rounding: find nearest bar */
3815 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3818 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3820 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3822 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3833 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
3834 } else if (dir > 0) {
3835 return frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
3837 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
3846 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3847 framepos_t lower, framepos_t upper, uint32_t bar_mod)
3849 Glib::Threads::RWLock::ReaderLock lm (lock);
3850 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
3852 /* although the map handles negative beats, bbt doesn't. */
3857 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
3861 while (pos >= 0 && pos < upper) {
3862 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
3863 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3864 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3865 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3866 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3870 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
3875 bbt.bars -= bbt.bars % bar_mod;
3879 while (pos >= 0 && pos < upper) {
3880 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3881 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3882 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3883 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3884 bbt.bars += bar_mod;
3890 TempoMap::tempo_section_at_frame (framepos_t frame) const
3892 Glib::Threads::RWLock::ReaderLock lm (lock);
3894 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
3898 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
3900 TempoSection* prev = 0;
3904 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3906 if ((*i)->is_tempo()) {
3907 t = static_cast<TempoSection*> (*i);
3911 if (prev && t->minute() > minute) {
3921 abort(); /*NOTREACHED*/
3928 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3930 TempoSection* prev_t = 0;
3931 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3935 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3936 if ((*i)->is_tempo()) {
3937 t = static_cast<TempoSection*> (*i);
3938 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3948 /* don't use this to calculate length (the tempo is only correct for this frame).
3949 do that stuff based on the beat_at_frame and frame_at_beat api
3952 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
3954 Glib::Threads::RWLock::ReaderLock lm (lock);
3956 const TempoSection* ts_at = 0;
3957 const TempoSection* ts_after = 0;
3958 Metrics::const_iterator i;
3961 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3963 if ((*i)->is_tempo()) {
3964 t = static_cast<TempoSection*> (*i);
3968 if (ts_at && (*i)->frame() > frame) {
3978 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
3980 /* must be treated as constant tempo */
3981 return ts_at->frames_per_quarter_note (_frame_rate);
3985 TempoMap::meter_section_at_frame (framepos_t frame) const
3987 Glib::Threads::RWLock::ReaderLock lm (lock);
3988 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
3992 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
3994 Metrics::const_iterator i;
3995 MeterSection* prev = 0;
3999 for (i = metrics.begin(); i != metrics.end(); ++i) {
4001 if (!(*i)->is_tempo()) {
4002 m = static_cast<MeterSection*> (*i);
4004 if (prev && (*i)->minute() > minute) {
4014 abort(); /*NOTREACHED*/
4021 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4023 MeterSection* prev_m = 0;
4025 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4027 if (!(*i)->is_tempo()) {
4028 m = static_cast<MeterSection*> (*i);
4029 if (prev_m && m->beat() > beat) {
4040 TempoMap::meter_section_at_beat (double beat) const
4042 Glib::Threads::RWLock::ReaderLock lm (lock);
4043 return meter_section_at_beat_locked (_metrics, beat);
4047 TempoMap::meter_at_frame (framepos_t frame) const
4049 TempoMetric m (metric_at (frame));
4054 TempoMap::fix_legacy_session ()
4056 MeterSection* prev_m = 0;
4057 TempoSection* prev_t = 0;
4059 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4063 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4065 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4068 m->set_minute (0.0);
4069 m->set_position_lock_style (AudioTime);
4074 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4075 + (m->bbt().beats - 1)
4076 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4078 m->set_beat (start);
4079 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4080 + (m->bbt().beats - 1)
4081 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4082 m->set_pulse (start_beat / prev_m->note_divisor());
4085 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4093 t->set_minute (0.0);
4094 t->set_position_lock_style (AudioTime);
4100 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4101 + (t->legacy_bbt().beats - 1)
4102 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4104 t->set_pulse (beat / prev_m->note_divisor());
4106 /* really shouldn't happen but.. */
4107 t->set_pulse (beat / 4.0);
4116 TempoMap::get_state ()
4118 Metrics::const_iterator i;
4119 XMLNode *root = new XMLNode ("TempoMap");
4122 Glib::Threads::RWLock::ReaderLock lm (lock);
4123 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4124 root->add_child_nocopy ((*i)->get_state());
4132 TempoMap::set_state (const XMLNode& node, int /*version*/)
4135 Glib::Threads::RWLock::WriterLock lm (lock);
4138 XMLNodeConstIterator niter;
4139 Metrics old_metrics (_metrics);
4142 nlist = node.children();
4144 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4145 XMLNode* child = *niter;
4147 if (child->name() == TempoSection::xml_state_node_name) {
4150 TempoSection* ts = new TempoSection (*child, _frame_rate);
4151 _metrics.push_back (ts);
4154 catch (failed_constructor& err){
4155 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4156 _metrics = old_metrics;
4157 old_metrics.clear();
4161 } else if (child->name() == MeterSection::xml_state_node_name) {
4164 MeterSection* ms = new MeterSection (*child, _frame_rate);
4165 _metrics.push_back (ms);
4168 catch (failed_constructor& err) {
4169 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4170 _metrics = old_metrics;
4171 old_metrics.clear();
4177 if (niter == nlist.end()) {
4178 MetricSectionSorter cmp;
4179 _metrics.sort (cmp);
4182 /* check for legacy sessions where bbt was the base musical unit for tempo */
4183 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4185 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4186 if (t->legacy_bbt().bars != 0) {
4187 fix_legacy_session();
4194 /* check for multiple tempo/meters at the same location, which
4195 ardour2 somehow allowed.
4198 Metrics::iterator prev = _metrics.end();
4199 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4200 if (prev != _metrics.end()) {
4202 MeterSection* prev_m;
4204 TempoSection* prev_t;
4205 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4206 if (prev_m->pulse() == ms->pulse()) {
4207 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4208 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4211 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4212 if (prev_t->pulse() == ts->pulse()) {
4213 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4214 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4222 recompute_map (_metrics);
4224 Metrics::const_iterator d = old_metrics.begin();
4225 while (d != old_metrics.end()) {
4229 old_metrics.clear ();
4232 PropertyChanged (PropertyChange ());
4238 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
4240 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4241 const MeterSection* m;
4242 const TempoSection* t;
4243 const TempoSection* prev_t = 0;
4245 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4247 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4248 o << "Tempo @ " << *i << t->note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4249 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4250 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4251 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4253 o << " current : " << t->note_types_per_minute()
4254 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4255 o << " previous : " << prev_t->note_types_per_minute()
4256 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4257 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4258 << " | " << prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())
4259 << " | " << frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))
4260 << " | " << prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()) << std::endl;
4263 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4264 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4265 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4266 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4269 o << "------" << std::endl;
4273 TempoMap::n_tempos() const
4275 Glib::Threads::RWLock::ReaderLock lm (lock);
4278 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4279 if ((*i)->is_tempo()) {
4288 TempoMap::n_meters() const
4290 Glib::Threads::RWLock::ReaderLock lm (lock);
4293 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4294 if (!(*i)->is_tempo()) {
4303 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4305 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4306 if ((*i)->frame() >= where && !(*i)->initial ()) {
4310 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4311 gui_move_meter (ms, (*i)->frame() + amount);
4314 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4315 gui_move_tempo (ts, (*i)->frame() + amount, 0);
4320 PropertyChanged (PropertyChange ());
4324 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4328 std::list<MetricSection*> metric_kill_list;
4330 TempoSection* last_tempo = NULL;
4331 MeterSection* last_meter = NULL;
4332 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4333 bool meter_after = false; // is there a meter marker likewise?
4335 Glib::Threads::RWLock::WriterLock lm (lock);
4336 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4337 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4338 metric_kill_list.push_back(*i);
4339 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4342 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4346 else if ((*i)->frame() >= where) {
4347 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4348 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4349 if ((*i)->frame() == where) {
4350 // marker was immediately after end of range
4351 tempo_after = dynamic_cast<TempoSection*> (*i);
4352 meter_after = dynamic_cast<MeterSection*> (*i);
4358 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4359 if (last_tempo && !tempo_after) {
4360 metric_kill_list.remove(last_tempo);
4361 last_tempo->set_minute (minute_at_frame (where));
4364 if (last_meter && !meter_after) {
4365 metric_kill_list.remove(last_meter);
4366 last_meter->set_minute (minute_at_frame (where));
4370 //remove all the remaining metrics
4371 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4372 _metrics.remove(*i);
4377 recompute_map (_metrics);
4380 PropertyChanged (PropertyChange ());
4384 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4385 * pos can be -ve, if required.
4388 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4390 Glib::Threads::RWLock::ReaderLock lm (lock);
4391 const double frame_qn = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
4393 return frame_at_minute (minute_at_quarter_note_locked (_metrics, frame_qn + beats.to_double()));
4397 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4399 Glib::Threads::RWLock::ReaderLock lm (lock);
4401 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4402 pos_bbt.ticks += op.ticks;
4403 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4405 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4407 pos_bbt.beats += op.beats;
4408 /* the meter in effect will start on the bar */
4409 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();
4410 while (pos_bbt.beats >= divisions_per_bar + 1) {
4412 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4413 pos_bbt.beats -= divisions_per_bar;
4415 pos_bbt.bars += op.bars;
4417 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4420 /** Count the number of beats that are equivalent to distance when going forward,
4424 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4426 Glib::Threads::RWLock::ReaderLock lm (lock);
4428 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4432 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4438 operator<< (std::ostream& o, const Meter& m) {
4439 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4443 operator<< (std::ostream& o, const Tempo& t) {
4444 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4448 operator<< (std::ostream& o, const MetricSection& section) {
4450 o << "MetricSection @ " << section.frame() << ' ';
4452 const TempoSection* ts;
4453 const MeterSection* ms;
4455 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4456 o << *((const Tempo*) ts);
4457 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4458 o << *((const Meter*) ms);